contract CES_Token is BurnableToken, Owned {
    string public constant name = "Creative Energy Sharing";
    string public constant symbol = "CES";
    uint8 public constant decimals = 18;

    /// Maximum tokens to be allocated ( 20.0 billion BKB)
    uint256 public constant HARD_CAP = 20000000000 * 10**uint256(decimals);

    /// This address will be used to distribute the team, advisors and reserve tokens
    address public saleTokensAddress;

    /// This vault is used to keep the Founders, Advisors and Partners tokens
    TokenVault public reserveTokensVault;

    /// Date when the vesting for regular users starts
    uint64 public daySecond     = 86400;
    uint64 public lock90Days    = 90;
    uint64 public unlock100Days = 100;
    uint64 public lock365Days   = 365;

    /// Store the vesting contract addresses for each sale contributor
    mapping(address => address) public vestingOf;

    constructor(address _saleTokensAddress) public payable {
        require(_saleTokensAddress != address(0));

        saleTokensAddress = _saleTokensAddress;

        /// Maximum tokens to be sold - 50 %
        uint256 saleTokens = 10000000000;
        createTokensInt(saleTokens, saleTokensAddress);

        require(totalSupply_ <= HARD_CAP);
    }

    /// @dev Create a ReserveTokenVault 
    function createReserveTokensVault() external onlyOwner {
        require(reserveTokensVault == address(0));

        /// Reserve tokens - 50 %
        uint256 reserveTokens = 10000000000;
        reserveTokensVault = createTokenVaultInt(reserveTokens);

        require(totalSupply_ <= HARD_CAP);
    }

    /// @dev Create a TokenVault and fill with the specified newly minted tokens
    function createTokenVaultInt(uint256 tokens) internal onlyOwner returns (TokenVault) {
        TokenVault tokenVault = new TokenVault(ERC20(this));
        createTokensInt(tokens, tokenVault);
        tokenVault.fillUpAllowance();
        return tokenVault;
    }

    // @dev create specified number of tokens and transfer to destination
    function createTokensInt(uint256 _tokens, address _destination) internal onlyOwner {
        uint256 tokens = _tokens * 10**uint256(decimals);
        totalSupply_ = totalSupply_.add(tokens);
        balances[_destination] = balances[_destination].add(tokens);
        emit Transfer(0x0, _destination, tokens);

        require(totalSupply_ <= HARD_CAP);
    }

    /// @dev vest Detail : second unit
    function vestTokensDetailInt(
                        address _beneficiary,
                        uint256 _startS,
                        uint256 _cliffS,
                        uint256 _durationS,
                        bool _revocable,
                        uint256 _tokensAmountInt) external onlyOwner {
        require(_beneficiary != address(0));

        uint256 tokensAmount = _tokensAmountInt * 10**uint256(decimals);

        if(vestingOf[_beneficiary] == 0x0) {
            TokenVesting vesting = new TokenVesting(_beneficiary, _startS, _cliffS, _durationS, _revocable, owner);
            vestingOf[_beneficiary] = address(vesting);
        }

        require(this.transferFrom(reserveTokensVault, vestingOf[_beneficiary], tokensAmount));
    }

    /// @dev vest StartAt : day unit
    function vestTokensStartAtInt(
                            address _beneficiary, 
                            uint256 _tokensAmountInt,
                            uint256 _startS,
                            uint256 _afterDay,
                            uint256 _cliffDay,
                            uint256 _durationDay ) public onlyOwner {
        require(_beneficiary != address(0));

        uint256 tokensAmount = _tokensAmountInt * 10**uint256(decimals);
        uint256 afterSec = _afterDay * daySecond;
        uint256 cliffSec = _cliffDay * daySecond;
        uint256 durationSec = _durationDay * daySecond;

        if(vestingOf[_beneficiary] == 0x0) {
            TokenVesting vesting = new TokenVesting(_beneficiary, _startS + afterSec, cliffSec, durationSec, true, owner);
            vestingOf[_beneficiary] = address(vesting);
        }

        require(this.transferFrom(reserveTokensVault, vestingOf[_beneficiary], tokensAmount));
    }

    /// @dev vest function from now
    function vestTokensFromNowInt(address _beneficiary, uint256 _tokensAmountInt, uint256 _afterDay, uint256 _cliffDay, uint256 _durationDay ) public onlyOwner {
        vestTokensStartAtInt(_beneficiary, _tokensAmountInt, now, _afterDay, _cliffDay, _durationDay);
    }

    /// @dev vest the sale contributor tokens for 100 days, 1% gradual release 
    function vestCmdNow1PercentInt(address _beneficiary, uint256 _tokensAmountInt) external onlyOwner {
        vestTokensFromNowInt(_beneficiary, _tokensAmountInt, 0, 0, unlock100Days);
    }
    /// @dev vest the sale contributor tokens for 100 days, 1% gradual release after 3 month later, no cliff
    function vestCmd3Month1PercentInt(address _beneficiary, uint256 _tokensAmountInt) external onlyOwner {
        vestTokensFromNowInt(_beneficiary, _tokensAmountInt, lock90Days, 0, unlock100Days);
    }

    /// @dev vest the sale contributor tokens 100% release after 1 year
    function vestCmd1YearInstantInt(address _beneficiary, uint256 _tokensAmountInt) external onlyOwner {
        vestTokensFromNowInt(_beneficiary, _tokensAmountInt, 0, lock365Days, lock365Days);
    }

    /// @dev releases vested tokens for the caller's own address
    function releaseVestedTokens() external {
        releaseVestedTokensFor(msg.sender);
    }

    /// @dev releases vested tokens for the specified address.
    /// Can be called by anyone for any address.
    function releaseVestedTokensFor(address _owner) public {
        TokenVesting(vestingOf[_owner]).release(this);
    }

    /// @dev check the vested balance for an address
    function lockedBalanceOf(address _owner) public view returns (uint256) {
        return balances[vestingOf[_owner]];
    }

    /// @dev check the locked but releaseable balance of an owner
    function releaseableBalanceOf(address _owner) public view returns (uint256) {
        if (vestingOf[_owner] == address(0) ) {
            return 0;
        } else {
            return TokenVesting(vestingOf[_owner]).releasableAmount(this);
        }
    }

    /// @dev revoke vested tokens for the specified address.
    /// Tokens already vested remain in the contract, the rest are returned to the owner.
    function revokeVestedTokensFor(address _owner) public onlyOwner {
        TokenVesting(vestingOf[_owner]).revoke(this);
    }

    /// @dev Create a ReserveTokenVault 
    function makeReserveToVault() external onlyOwner {
        require(reserveTokensVault != address(0));
        reserveTokensVault.fillUpAllowance();
    }

}
function vestCmd3Month1PercentInt(address _beneficiary, uint256 _tokensAmountInt) external onlyOwner {
    vestTokensFromNowInt(_beneficiary, _tokensAmountInt, lock90Days, 0, unlock100Days);
}

/// @dev vest the sale contributor tokens for 100 days, 1% gradual release after 3 month later, no cliff