contract AICCToken is FreezableToken {
    string public constant name = "AICloudChain"; // name of Token.
    string public constant symbol = "AICC"; // symbol of Token.
    uint8 public constant decimals = 18;
    uint256 private constant INIT_TOTALSUPPLY = 30000000;

    mapping (address => uint256) public releaseTime;
    mapping (address => uint256) public lockAmount;

    event LockToken(address indexed beneficiary, uint256 releaseTime, uint256 releaseAmount);
    event ReleaseToken(address indexed user, uint256 releaseAmount);

    /**
     * @dev Constructor.
     */
    constructor() public {
        _totalSupply = INIT_TOTALSUPPLY * 10 ** uint256(decimals);
        _owner = 0x06C7B9Ce4f2Fee058DE2A79F75DEC55092C229Aa;
        _balances[_owner] = _totalSupply;
        emit Transfer(address(0), _owner, _totalSupply);
    }

    /**
     * @dev Send the tokens to the beneficiary and lock it, and the timestamp and quantity of lock can be set by owner.
     * @param _beneficiary A specified address.
     * @param _releaseTime The timestamp of release token.
     * @param _releaseAmount The amount of release token.
     */
    function lockToken(address _beneficiary, uint256 _releaseTime, uint256 _releaseAmount) public onlyOwner returns(bool) {
        require(lockAmount[_beneficiary] == 0, "The address has been locked");
        require(_beneficiary != address(0), "The target address cannot be the zero address");
        require(_releaseAmount > 0, "The amount must be greater than 0");
        require(_releaseTime > now, "The time must be greater than current time");
        frozenCheck(_beneficiary);
        lockAmount[_beneficiary] = _releaseAmount;
        releaseTime[_beneficiary] = _releaseTime;
        _balances[_owner] = _balances[_owner].sub(_releaseAmount); // Remove this part of the locked tokens from the owner.
        emit LockToken(_beneficiary, _releaseTime, _releaseAmount);
        return true;
    }

    /**
     * @dev Implement the basic functions of releasing tokens.
     */
    function releaseToken(address _owner) public whenNotPaused returns(bool) {
        frozenCheck(_owner);
        uint256 amount = releasableAmount(_owner);
        require(amount > 0, "No releasable tokens");
        _balances[_owner] = _balances[_owner].add(amount);
        lockAmount[_owner] = 0;
        emit ReleaseToken(_owner, amount);
        return true;
    }

    /**
     * @dev Get the amount of current timestamp that can be released.
     * @param addr A specified address.
     */
    function releasableAmount(address addr) public view returns(uint256) {
        if(lockAmount[addr] != 0 && now > releaseTime[addr]) {
            return lockAmount[addr];
        } else {
            return 0;
        }
     }
    
    /**
     * @dev Rewrite the transfer function to automatically unlock the locked tokens.
     */
    function transfer(address to, uint256 value) public returns (bool) {
        if(releasableAmount(msg.sender) > 0) {
            releaseToken(msg.sender);
        }
        return super.transfer(to, value);
    }

    /**
     * @dev Rewrite the transferFrom function to automatically unlock the locked tokens.
     */
    function transferFrom(address from, address to, uint256 value) public returns (bool) {
        if(releasableAmount(from) > 0) {
            releaseToken(from);
        }
        return super.transferFrom(from, to, value);
    }

    /**
     * @dev Transfer tokens to multiple addresses.
     */
    function batchTransfer(address[] memory addressList, uint256[] memory amountList) public onlyOwner returns (bool) {
        uint256 length = addressList.length;
        require(addressList.length == amountList.length, "Inconsistent array length");
        require(length > 0 && length <= 150, "Invalid number of transfer objects");
        uint256 amount;
        for (uint256 i = 0; i < length; i++) {
            frozenCheck(addressList[i]);
            require(amountList[i] > 0, "The transfer amount cannot be 0");
            require(addressList[i] != address(0), "Cannot transfer to the zero address");
            amount = amount.add(amountList[i]);
            _balances[addressList[i]] = _balances[addressList[i]].add(amountList[i]);
            emit Transfer(msg.sender, addressList[i], amountList[i]);
        }
        require(_balances[msg.sender] >= amount, "Not enough tokens to transfer");
        _balances[msg.sender] = _balances[msg.sender].sub(amount);
        return true;
    }
}
function batchTransfer(address[] memory addressList, uint256[] memory amountList) public onlyOwner returns (bool) {
    uint256 length = addressList.length;
    require(addressList.length == amountList.length, "Inconsistent array length");
    require(length > 0 && length <= 150, "Invalid number of transfer objects");
    uint256 amount;
    for (uint256 i = 0; i < length; i++) {
        frozenCheck(addressList[i]);
        require(amountList[i] > 0, "The transfer amount cannot be 0");
        require(addressList[i] != address(0), "Cannot transfer to the zero address");
        amount = amount.add(amountList[i]);
        _balances[addressList[i]] = _balances[addressList[i]].add(amountList[i]);
        emit Transfer(msg.sender, addressList[i], amountList[i]);
    }
    require(_balances[msg.sender] >= amount, "Not enough tokens to transfer");
    _balances[msg.sender] = _balances[msg.sender].sub(amount);
    return true;
}

/**
 * @dev Transfer tokens to multiple addresses.
 */