contract PostDeliveryCrowdsale is TimedCrowdsale {
    using SafeMath for uint256;
    uint contractCreationTime;
    uint monthTime =  2592000;
    uint public timeToWait;
    uint public initialEmission;
    uint public months;
    uint public lastFunctioncalled;

    mapping(address => uint256) private _balances;
    mapping(address => uint256) private _totalClaimed;
    
    __unstable__TokenVault private _vault;

    constructor(uint _initialEmission, uint _timeToWait, uint _months) public {
        require(_timeToWait>0, "TimeToWait is 0");
        uint closingTime = closingTime();
        contractCreationTime = closingTime;
        initialEmission = _initialEmission;
        timeToWait = _timeToWait ;
        months = _months;
        _vault = new __unstable__TokenVault();
    }
    
    function currentRetractableTokens(address beneficiary) view public returns(uint){
        require(hasClosed(), "PostDeliveryCrowdsale: not closed");
        uint256 amount = _balances[beneficiary];
        uint realAmount;
        if(amount<=0){
            return 0;
        }
        uint since = block.timestamp - contractCreationTime;
        
        if(initialEmission==100){
            return amount;
        }
        
        else if((since/monthTime)< timeToWait){
            realAmount = ((initialEmission*(amount+_totalClaimed[beneficiary]))/100) - _totalClaimed[beneficiary];
            return realAmount;
        }
        
        else {
            uint restEmission = 100 - initialEmission;
            realAmount = ((initialEmission*(amount+_totalClaimed[beneficiary]))/100) + (restEmission*(amount + _totalClaimed[beneficiary])*((since/monthTime) + 1 - timeToWait)/months/100) - _totalClaimed[beneficiary];
            if(realAmount==0){
                return 0;
            }
            if(realAmount>_balances[beneficiary]){
                realAmount = _balances[beneficiary];
            }
            return realAmount;
        }
    }

   
    /**
     * @dev Withdraw tokens only after crowdsale ends.
     * @param beneficiary Whose tokens will be withdrawn.
     */
    function withdrawTokens(address beneficiary) public {
        require(hasClosed(), "PostDeliveryCrowdsale: not closed");
        uint256 amount = _balances[beneficiary];
        uint realAmount;
        require(amount > 0, "PostDeliveryCrowdsale: beneficiary is not due any tokens");
        uint since = block.timestamp - contractCreationTime;
        
        if(initialEmission==100){
            _balances[beneficiary] = 0;
            _vault.transfer(token(), beneficiary, amount);
            _totalClaimed[beneficiary] += amount;
            lastFunctioncalled = 1;
        }
        
        else if((since/monthTime)< timeToWait){
            realAmount = ((initialEmission*amount)/100) - _totalClaimed[beneficiary];
            require(realAmount>0,"Current Retractable Amount 0");
            _balances[beneficiary] -= realAmount ;
            _vault.transfer(token(), beneficiary, realAmount);
            _totalClaimed[beneficiary] += realAmount;
            lastFunctioncalled =2;
            
        }
        
        else {
            uint restEmission = 100 - initialEmission;
            realAmount = ((initialEmission*(amount+_totalClaimed[beneficiary]))/100) + (restEmission*(amount + _totalClaimed[beneficiary])*((since/monthTime) + 1 - timeToWait)/months/100) - _totalClaimed[beneficiary];
            require(realAmount>0,"Current Retractable Amount 0");
            if(realAmount>_balances[beneficiary]){
                _totalClaimed[beneficiary] += _balances[beneficiary];
                realAmount = _balances[beneficiary];
                lastFunctioncalled = 3;
            }
            _balances[beneficiary] -= realAmount ;
            _vault.transfer(token(), beneficiary, realAmount);
            _totalClaimed[beneficiary] += realAmount;
            lastFunctioncalled = 4;
        }
    }

    /**
     * @return the balance of an account.
     */
    function balanceOf(address account) public view returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev Overrides parent by storing due balances, and delivering tokens to the vault instead of the end user. This
     * ensures that the tokens will be available by the time they are withdrawn (which may not be the case if
     * `_deliverTokens` was called later).
     * @param beneficiary Token purchaser
     * @param tokenAmount Amount of tokens purchased
     */
    function _processPurchase(address beneficiary, uint256 tokenAmount) internal {
        _balances[beneficiary] = _balances[beneficiary].add(tokenAmount);
        _deliverTokens(address(_vault), tokenAmount);
    }
}
/**
 * @title PostDeliveryCrowdsale
 * @dev Crowdsale that locks tokens from withdrawal until it ends.
 */
function balanceOf(address account) public view returns (uint256) {
    return _balances[account];
}

/**
 * @return the balance of an account.
 */