contract TokenLock is Owned{
    using SafeMath for uint256;

    LifeBankerCoin public LBC;

    uint256 public totalSupply = 10000000000000000000000000000;
    uint256 public totalLocked = totalSupply.div(2); // 50% of totalSupply
    uint256 public collectedTokens;
    uint256 public startTime;

    address public POSAddress       = 0x23eB4df52175d89d8Df83F44992A5723bBbac00c; //30% DPOS
    address public CommunityAddress = 0x9370973BEa603b86F07C2BFA8461f178081ce49F; //10% Community promotion
    address public OperationAddress = 0x69Ce6E9E77869bFcf0Ec3c217b5e7E4905F4AFFf; //10% Operation and maintenance

    uint256 _1stYear = totalLocked.mul(5000).div(10000);  // 50%
    uint256 _2stYear = totalLocked.mul(2500).div(10000);  // 25%
    uint256 _3stYear = totalLocked.mul(1250).div(10000);  // 12.5%
    uint256 _4stYear = totalLocked.mul(625).div(10000);   // 6.25%
    uint256 _5stYear = totalLocked.mul(625).div(10000);   // 6.25%

    mapping (address => bool) public whiteList;
    

    event TokensWithdrawn(uint256 _amount);
    event LogMangeWhile(address indexed _dest, bool _allow);
    event ClaimedTokens(address indexed _token, address indexed _controller, uint256 _amount);

    modifier onlyWhite() { 
        require (whiteList[msg.sender] == true); 
        _; 
    }

    /// @param _lbc address : LifeBankerCoin contract deployment address
    constructor(address _lbc) public{
        startTime = now;
        LBC = LifeBankerCoin(_lbc);
        whiteList[msg.sender] = true;
    }
    
    /**
     * @dev Add or remove call permissions for an address
     * @param _dest    address  : The address of the permission to be modified
     * @param _allow   bool     : True means increase, False means remove
     * @return success bool     : Successful operation returns True
     */
    function mangeWhileList(address _dest, bool _allow) onlyOwner public returns(bool success){
        require(_dest != address(0));

        whiteList[_dest] = _allow;
        emit LogMangeWhile(_dest, _allow);
        return true;
    }

    /* @dev Called by 'owner' to unlock the token.   */
    function unlock() public onlyWhite returns(bool success){
        uint256 canExtract = calculation();
        uint256 _amount = canExtract.sub(collectedTokens); // canExtract - collectedTokens
        distribute(_amount);
        collectedTokens = collectedTokens.add(_amount);

        return true;
    }

    /*
     * @dev Calculates the total number of tokens that can be unlocked based on time.
     * @return uint256 : total number of unlockable
     */
    function calculation() view public returns(uint256){
        uint256 _month = getMonths();
        uint256 _amount;

        if (_month == 0){
            return 0;
        }

        if (_month <= 12 ){
            _amount = _1stYear.mul(_month).div(12);

        }else if(_month <= 24){
            // _1stYear + [_2stYear * (moneth - 12) / 12]
            _amount = _1stYear;
            _amount = _amount.add(_2stYear.mul(_month.sub(12)).div(12));

        }else if(_month <= 36){
            // _1stYear + _2stYear + [_3stYear * (moneth - 24) / 12]
            _amount = _1stYear + _2stYear;
            _amount = _amount.add(_3stYear.mul(_month.sub(24)).div(12));

        }else if(_month <= 48){
            // _1stYear + _2stYear + _3stYear + [_4stYear * (moneth - 36) / 12]
            _amount = _1stYear + _2stYear + _3stYear;
            _amount = _amount.add(_4stYear.mul(_month.sub(36)).div(12));      

        }else if(_month <= 60){
            // _1stYear + _2stYear + _3stYear + _4stYear + [_5stYear * (moneth - 48) / 12]
            _amount = _1stYear + _2stYear + _3stYear + _4stYear;
            _amount = _amount.add(_5stYear.mul(_month.sub(48)).div(12)); 

        }else{
            // more than 5years
            _amount = LBC.balanceOf(this);
        }
        return _amount;
    }

    /* Get how many months have passed since the contract was deployed. */
    function getMonths() view public returns(uint256){
        uint256 countMonth = (getTime().sub(startTime)).div(30 * 24 * 3600);
        return countMonth; // begin 0
    }

    /*
     * @dev Distribute unlockable tokens to three addresses, proportion 3:1:1
     * @param _amount uint256 : Number of tokens that can be unlocked
     */
    function distribute(uint256 _amount) internal returns(bool){
        require (_amount != 0);

        uint256 perAmount = _amount.div(5);
        
        assert (LBC.transfer(POSAddress, perAmount.mul(3)));
        assert (LBC.transfer(CommunityAddress, perAmount.mul(1)));
        assert (LBC.transfer(OperationAddress, perAmount.mul(1)));

        emit TokensWithdrawn(_amount);
        return true;
    }

    /* Get the timestamp of the current block */
    function getTime() view public returns(uint256){
        return now; //block.timestamp
    }


    /// Safe Function
    /// @dev This method can be used by the controller to extract mistakenly
    /// @param _token The address of the token contract that you want to recover
    function claimTokens(address _token) public onlyOwner returns(bool){
        require(_token != address(LBC));

        ERC20 token = ERC20(_token);
        uint256 balance = token.balanceOf(this);
        token.transfer(owner, balance);
        emit ClaimedTokens(_token, owner, balance);
        return true;
    }
}
/*
 * @title This contract locks 50% of the total, 30% for mining, 
 *        10% for community promotion, and 10% for operation and maintenance.
 * @notice The tokens are locked for a total of five years, 
 *        and the number of tokens that can be unlocked each year is halved. 
 *        Each year's tokens are divided into 12 months equals to unlock.
 *        Percentage per year : 50%, 25%, 12.5%, 6.25% ,6.25% 
 * Unlockable Amount(%)
 *    ^
 * 100|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
 *    |_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _     *   :
 *    |_ _ _ _ _ _ _ _ _ _ _ _ _      *   :        : 
 *    |                         *:        :        : 
 *  75|_ _ _ _ _ _ _ _ _    *    :        :        : 
 *    |                *:        :        :        : 
 *    |             *   :        :        :        : 
 *    |          *      :        :        :        : 
 *  50|_ _ _ _ *        :        :        :        :   
 *    |       *:        :        :        :        : 
 *    |      * :        :        :        :        : 
 *    |     *  :        :        :        :        : 
 *    |    *   :        :        :        :        : 
 *    |   *    :        :        :        :        : 
 *    |  *     :        :        :        :        : 
 *    | *      :        :        :        :        : 
 *    |*       :        :        :        :        : 
 *    +--------*--------*--------*--------*--------*---> Time(year)
 *    0        1        2        3        4        5    
 */
function mangeWhileList(address _dest, bool _allow) onlyOwner public returns(bool success){
        require(_dest != address(0));

        whiteList[_dest] = _allow;
        emit LogMangeWhile(_dest, _allow);
        return true;
    }
/**
 * @dev Add or remove call permissions for an address
 * @param _dest    address  : The address of the permission to be modified
 * @param _allow   bool     : True means increase, False means remove
 * @return success bool     : Successful operation returns True
 */