contract ERCAddressFrozenFund is ERC20{

    using SafeMath for uint;

    struct LockedWallet {
        address owner; // the owner of the locked wallet, he/she must secure the private key
        uint256 amount; //
        uint256 start; // timestamp when "lock" function is executed
        uint256 duration; // duration period in seconds. if we want to lock an amount for
        uint256 release;  // release = start+duration
        // "start" and "duration" is for bookkeeping purpose only. Only "release" will be actually checked once unlock function is called
    }


    address public owner;

    uint256 _lockedSupply;

    mapping (address => LockedWallet) addressFrozenFund; //address -> (deadline, amount),freeze fund of an address its so that no token can be transferred out until deadline

    function mintToken(address _owner, uint256 amount) internal;
    function burnToken(address _owner, uint256 amount) internal;

    event LockBalance(address indexed addressOwner, uint256 releasetime, uint256 amount);
    event LockSubBalance(address indexed addressOwner, uint256 index, uint256 releasetime, uint256 amount);
    event UnlockBalance(address indexed addressOwner, uint256 releasetime, uint256 amount);
    event UnlockSubBalance(address indexed addressOwner, uint256 index, uint256 releasetime, uint256 amount);

    function lockedSupply() public view returns (uint256) {
        return _lockedSupply;
    }

    function releaseTimeOf(address _owner) public view returns (uint256 releaseTime) {
        return addressFrozenFund[_owner].release;
    }

    function lockedBalanceOf(address _owner) public view returns (uint256 lockedBalance) {
        return addressFrozenFund[_owner].amount;
    }

    function lockBalance(uint256 duration, uint256 amount) public{

        address _owner = msg.sender;

        require(address(0) != _owner && amount > 0 && duration > 0 && balanceOf(_owner) >= amount);
        require(addressFrozenFund[_owner].release <= now && addressFrozenFund[_owner].amount == 0);

        addressFrozenFund[_owner].start = now;
        addressFrozenFund[_owner].duration = duration;
        addressFrozenFund[_owner].release = addressFrozenFund[_owner].start + duration;
        addressFrozenFund[_owner].amount = amount;
        burnToken(_owner, amount);
        _lockedSupply = SafeMath.add(_lockedSupply, lockedBalanceOf(_owner));

        LockBalance(_owner, addressFrozenFund[_owner].release, amount);
    }

    //_owner must call this function explicitly to release locked balance in a locked wallet
    function releaseLockedBalance() public {

        address _owner = msg.sender;

        require(address(0) != _owner && lockedBalanceOf(_owner) > 0 && releaseTimeOf(_owner) <= now);
        mintToken(_owner, lockedBalanceOf(_owner));
        _lockedSupply = SafeMath.sub(_lockedSupply, lockedBalanceOf(_owner));

        UnlockBalance(_owner, addressFrozenFund[_owner].release, lockedBalanceOf(_owner));

        delete addressFrozenFund[_owner];
    }

}
function releaseLockedBalance() public {

    address _owner = msg.sender;

    require(address(0) != _owner && lockedBalanceOf(_owner) > 0 && releaseTimeOf(_owner) <= now);
    mintToken(_owner, lockedBalanceOf(_owner));
    _lockedSupply = SafeMath.sub(_lockedSupply, lockedBalanceOf(_owner));

    UnlockBalance(_owner, addressFrozenFund[_owner].release, lockedBalanceOf(_owner));

    delete addressFrozenFund[_owner];
}

//_owner must call this function explicitly to release locked balance in a locked wallet