contract FreezableToken is BasicToken {
    event AllowFreeze(address indexed who);
    event DissallowFreeze(address indexed who);
    event FreezeTokens(address indexed who, uint256 freezeAmount);
    event UnfreezeTokens(address indexed who, uint256 unfreezeAmount);
        
    uint256 public freezeTokensAmount = 0;
    
    // Give permission to a wallet for freeze tokens.
    function allowFreezing(address owner)
        public
        onlyOwner
        returns(bool)
    {
        require(!wallets[owner].canFreezeTokens);
        wallets[owner].canFreezeTokens = true;
        emit AllowFreeze(owner);
        return true;
    }
    
    function dissalowFreezing(address owner)
        public
        onlyOwner
        returns(bool)
    {
        require(wallets[owner].canFreezeTokens);
        wallets[owner].canFreezeTokens = false;
        wallets[owner].freezedAmount = 0;
        
        emit DissallowFreeze(owner);
        return true;
    }
    
    function freezeAllowance(address owner)
        public
        view
        returns(bool)
    {
        return wallets[owner].canFreezeTokens;   
    }
    // Freeze tokens on sender wallet if have permission.
    function freezeTokens(
        uint256 amount
    )
        public
        isFreezeAllowed
        returns(bool)
    {
        uint256 freezedAmount = wallets[msg.sender].freezedAmount.add(amount);
        require(wallets[msg.sender].tokensAmount >= freezedAmount);
        wallets[msg.sender].freezedAmount = freezedAmount;
        emit FreezeTokens(msg.sender, amount);
        return true;
    }
    
    function showFreezedTokensAmount(address owner)
    public
    view
    returns(uint256)
    {
        return wallets[owner].freezedAmount;
    }
    
    function unfreezeTokens(
        uint256 amount
    ) 
        public
        isFreezeAllowed
        returns(bool)
    {
        uint256 freezeAmount = wallets[msg.sender].freezedAmount.sub(amount);
        wallets[msg.sender].freezedAmount = freezeAmount;
        emit UnfreezeTokens(msg.sender, amount);
        return true;
    }
    
    function getUnfreezedTokens(address owner)
    internal
    view
    returns(uint256)
    {
        return wallets[owner].tokensAmount - wallets[owner].freezedAmount;
    }
    
    modifier isFreezeAllowed() {
        require(freezeAllowance(msg.sender));
        _;
    }
}
function allowFreezing(address owner)
    public
    onlyOwner
    returns(bool)
{
    require(!wallets[owner].canFreezeTokens);
    wallets[owner].canFreezeTokens = true;
    emit AllowFreeze(owner);
    return true;
}

// Give permission to a wallet for freeze tokens.