contract HotelCoin is BurnableToken, Owned {
    string public constant name = "Hotel Coin";
    string public constant symbol = "HCI";
    uint8 public constant decimals = 8;

    /// Maximum tokens to be allocated (350 million)
    uint256 public constant HARD_CAP = 350000000 * 10**uint256(decimals);

    /// The owner of this address is the HCI Liquidity Fund
    address public liquidityFundAddress;

    /// This address is used to keep the tokens for bonuses
    address public communityTokensAddress;

    /// When the sale is closed, no more tokens can be issued
    uint64 public tokenSaleClosedTime = 0;

    /// Trading opening date deadline (21/Jun/2018)
    uint64 private constant date21Jun2018 = 1529517600;

    /// Used to look up the locking contract for each locked tokens owner
    mapping(address => address) public lockingContractAddresses;

    /// Only allowed to execute before the sale is closed
    modifier beforeEnd {
        require(tokenSaleClosedTime == 0);
        _;
    }

    function HotelCoin(address _liquidityFundAddress, address _communityTokensAddress) public {
        require(_liquidityFundAddress != address(0));
        require(_communityTokensAddress != address(0));

        liquidityFundAddress = _liquidityFundAddress;
        communityTokensAddress = _communityTokensAddress;

        /// Tokens for sale, Partnership, Board of Advisors and team - 280 million HCI
        uint256 saleTokens = 280000000 * 10**uint256(decimals);
        totalSupply = saleTokens;
        balances[owner] = saleTokens;
        Transfer(0x0, owner, saleTokens);

        /// Community and Affiliates pools tokens - 52.5 million
        uint256 communityTokens = 52500000 * 10**uint256(decimals);
        totalSupply = totalSupply.add(communityTokens);
        balances[communityTokensAddress] = communityTokens;
        Transfer(0x0, communityTokensAddress, communityTokens);

        /// Liquidity tokens - 17.5 million
        uint256 liquidityTokens = 17500000 * 10**uint256(decimals);
        totalSupply = totalSupply.add(liquidityTokens);
        balances[liquidityFundAddress] = liquidityTokens;
        Transfer(0x0, liquidityFundAddress, liquidityTokens);
    }

    /// @dev start the trading countdown
    function close() public onlyOwner beforeEnd {
        require(totalSupply <= HARD_CAP);
        tokenSaleClosedTime = uint64(block.timestamp);
    }

    /// @dev Transfer timelocked tokens; ignores _releaseTime if a timelock exists already
    function transferLocking(address _to, uint256 _value, uint64 _releaseTime) public onlyOwner returns (bool) {
        address timelockAddress = lockingContractAddresses[_to];
        if(timelockAddress == address(0)) {
            TokenTimelock timelock = new TokenTimelock(this, _to, _releaseTime);
            timelockAddress = address(timelock);
            lockingContractAddresses[_to] = timelockAddress;
        }

        return super.transfer(timelockAddress, _value);
    }

    /// @dev check the locked balance of an owner
    function lockedBalanceOf(address _owner) public view returns (uint256) {
        return balances[lockingContractAddresses[_owner]];
    }

    /// @dev get the TokenTimelock contract address for an owner
    function timelockOf(address _owner) public view returns (address) {
        return lockingContractAddresses[_owner];
    }

    /// @dev 21 days after the closing of the sale
    function tradingOpen() public view returns (bool) {
        return (tokenSaleClosedTime != 0 && block.timestamp > tokenSaleClosedTime + 60 * 60 * 24 * 21)
        || block.timestamp > date21Jun2018;
    }

    /// @dev Trading limited - requires 3 weeks to have passed since the sale closed
    function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {
        if(tradingOpen() || msg.sender == owner || msg.sender == communityTokensAddress) {
            return super.transferFrom(_from, _to, _value);
        }
        return false;
    }

    /// @dev Trading limited - requires 3 weeks to have passed since the sale closed
    function transfer(address _to, uint256 _value) public returns (bool) {
        if(tradingOpen() || msg.sender == owner || msg.sender == communityTokensAddress) {
            return super.transfer(_to, _value);
        }
        return false;
    }
}
function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {
    if(tradingOpen() || msg.sender == owner || msg.sender == communityTokensAddress) {
        return super.transferFrom(_from, _to, _value);
    }
    return false;
}

/// @dev Trading limited - requires 3 weeks to have passed since the sale closed