contract TokenLocker is Ownable {
    using SafeMath for uint;
    uint internal constant QUARTER = 91 days + 450 minutes;
  
    Auctions public auctions;
    METToken public token;
    bool public locked = false;
  
    uint public deposited;
    uint public lastWithdrawTime;
    uint public quarterlyWithdrawable;
    
    event Withdrawn(address indexed who, uint amount);
    event Deposited(address indexed who, uint amount);

    modifier onlyAuction() {
        require(msg.sender == address(auctions));
        _;
    }

    modifier preLock() { 
        require(!locked);
        _; 
    }

    modifier postLock() { 
        require(locked); 
        _; 
    }

    /// @notice Constructor to initialize TokenLocker contract.
    /// @param _auctions Address of auctions contract
    /// @param _token Address of METToken contract
    function TokenLocker(address _auctions, address _token) public {
        require(_auctions != 0x0);
        require(_token != 0x0);
        auctions = Auctions(_auctions);
        token = METToken(_token);
    }

    /// @notice If auctions is initialized, call to this function will result in
    /// locking of deposited tokens and further deposit of tokens will not be allowed.
    function lockTokenLocker() public onlyAuction {
        require(auctions.initialAuctionEndTime() != 0);
        require(auctions.initialAuctionEndTime() >= auctions.genesisTime()); 
        locked = true;
    }

    /// @notice It will deposit tokens into the locker for given beneficiary.
    /// @param beneficiary Address of the beneficiary, whose tokens are being locked.
    /// @param amount Amount of tokens being locked
    function deposit (address beneficiary, uint amount ) public onlyAuction preLock {
        uint totalBalance = token.balanceOf(this);
        require(totalBalance.sub(deposited) >= amount);
        deposited = deposited.add(amount);
        emit Deposited(beneficiary, amount);
    }

    /// @notice This function will allow token withdraw from locker.
    /// 25% of total deposited tokens can be withdrawn after initial auction end.
    /// Remaining 75% can be withdrawn in equal amount over 12 quarters.
    function withdraw() public onlyOwner postLock {
        require(deposited > 0);
        uint withdrawable = 0; 
        uint withdrawTime = auctions.initialAuctionEndTime();
        if (lastWithdrawTime == 0 && auctions.isInitialAuctionEnded()) {
            withdrawable = withdrawable.add((deposited.mul(25)).div(100));
            quarterlyWithdrawable = (deposited.sub(withdrawable)).div(12);
            lastWithdrawTime = withdrawTime;
        }

        require(lastWithdrawTime != 0);

        if (now >= lastWithdrawTime.add(QUARTER)) {
            uint daysSinceLastWithdraw = now.sub(lastWithdrawTime);
            uint totalQuarters = daysSinceLastWithdraw.div(QUARTER);

            require(totalQuarters > 0);
        
            withdrawable = withdrawable.add(quarterlyWithdrawable.mul(totalQuarters));

            if (now >= withdrawTime.add(QUARTER.mul(12))) {
                withdrawable = deposited;
            }

            lastWithdrawTime = lastWithdrawTime.add(totalQuarters.mul(QUARTER));
        }

        if (withdrawable > 0) {
            deposited = deposited.sub(withdrawable);
            token.transfer(msg.sender, withdrawable);
            emit Withdrawn(msg.sender, withdrawable);
        }
    }
}
/// @title This contract serves as a locker for a founder's tokens
function withdraw() public onlyOwner postLock {
    require(deposited > 0);
    uint withdrawable = 0; 
    uint withdrawTime = auctions.initialAuctionEndTime();
    if (lastWithdrawTime == 0 && auctions.isInitialAuctionEnded()) {
        withdrawable = withdrawable.add((deposited.mul(25)).div(100));
        quarterlyWithdrawable = (deposited.sub(withdrawable)).div(12);
        lastWithdrawTime = withdrawTime;
    }

    require(lastWithdrawTime != 0);

    if (now >= lastWithdrawTime.add(QUARTER)) {
        uint daysSinceLastWithdraw = now.sub(lastWithdrawTime);
        uint totalQuarters = daysSinceLastWithdraw.div(QUARTER);

        require(totalQuarters > 0);
        
        withdrawable = withdrawable.add(quarterlyWithdrawable.mul(totalQuarters));

        if (now >= withdrawTime.add(QUARTER.mul(12))) {
            withdrawable = deposited;
        }

        lastWithdrawTime = lastWithdrawTime.add(totalQuarters.mul(QUARTER));
    }

    if (withdrawable > 0) {
        deposited = deposited.sub(withdrawable);
        token.transfer(msg.sender, withdrawable);
        emit Withdrawn(msg.sender, withdrawable);
    }
}

/// @notice This function will allow token withdraw from locker.
/// 25% of total deposited tokens can be withdrawn after initial auction end.
/// Remaining 75% can be withdrawn in equal amount over 12 quarters.