contract LockableToken is BurnableToken {

    // stopped state to allow to lock tokens
    bool private _started;

    // variables to store info about locked addresses
    mapping(address => Lock) private _locked;
    struct Lock {
        bool locked;
        Batch[] batches;
    }
    struct Batch {
        uint256 amount;
        uint256 time;
    }

    /**
     * @dev Allows to lock an amount of tokens of specific addresses.
     * Available only to the owner.
     * Can be called once.
     * @param addresses array of addresses.
     * @param values array of amounts of tokens.
     * @param times array of Unix times.
     */
    function lock(address[] calldata addresses, uint256[] calldata values, uint256[] calldata times) external onlyOwner {
        require(!_started);
        require(addresses.length == values.length && values.length == times.length);

        for (uint256 i = 0; i < addresses.length; i++) {
            require(balanceOf(addresses[i]) >= values[i]);

            if (!_locked[addresses[i]].locked) {
                _locked[addresses[i]].locked = true;
            }

            _locked[addresses[i]].batches.push(Batch(values[i], block.timestamp + times[i]));

            if (_locked[addresses[i]].batches.length > 1) {
                assert(
                    _locked[addresses[i]].batches[_locked[addresses[i]].batches.length - 1].amount
                    < _locked[addresses[i]].batches[_locked[addresses[i]].batches.length - 2].amount
                    &&
                    _locked[addresses[i]].batches[_locked[addresses[i]].batches.length - 1].time
                    > _locked[addresses[i]].batches[_locked[addresses[i]].batches.length - 2].time
                );
            }
        }

        _started = true;
    }

    /**
     * @dev modified internal transfer function that prevents any transfer of locked tokens.
     * @param from address The address which you want to send tokens from
     * @param to The address to transfer to.
     * @param value The amount to be transferred.
     */
    function _transfer(address from, address to, uint256 value) internal {
        if (_locked[from].locked) {
            for (uint256 i = 0; i < _locked[from].batches.length; i++) {
                if (block.timestamp <= _locked[from].batches[i].time) {
                    require(value <= balanceOf(from).sub(_locked[from].batches[i].amount));
                    break;
                }
            }
        }
        super._transfer(from, to, value);
    }

    /**
     * @return true if locking is done.
     */
    function started() external view returns(bool) {
        return _started;
    }

}
/**
 * @title LockableToken.
 * @dev Extension of `ERC20` that allows to lock an amount of tokens of specific
 * addresses for specific time.
 * @author https://grox.solutions
 */
function _transfer(address from, address to, uint256 value) internal {
        if (_locked[from].locked) {
            for (uint256 i = 0; i < _locked[from].batches.length; i++) {
                if (block.timestamp <= _locked[from].batches[i].time) {
                    require(value <= balanceOf(from).sub(_locked[from].batches[i].amount));
                    break;
                }
            }
        }
        super._transfer(from, to, value);
    }
/**
 * @dev modified internal transfer function that prevents any transfer of locked tokens.
 * @param from address The address which you want to send tokens from
 * @param to The address to transfer to.
 * @param value The amount to be transferred.
 */