contract CurrentCrowdsale is Pausable, TokenRate {
    using SafeMath for uint256;

    uint256 constant private DECIMALS = 18;
    uint256 constant public HARDCAP_TOKENS_PRE_ICO = 100e6 * (10 ** DECIMALS);
    uint256 constant public HARDCAP_TOKENS_ICO = 499e8 * (10 ** DECIMALS);

    uint256 public startPhase1 = 0;
    uint256 public startPhase2 = 0;
    uint256 public startPhase3 = 0;
    uint256 public endOfPhase3 = 0;

    uint256 public maxcap = 0;

    uint256 public tokensSoldIco = 0;
    uint256 public tokensRemainingIco = HARDCAP_TOKENS_ICO;
    uint256 public tokensSoldTotal = 0;

    uint256 public weiRaisedIco = 0;
    uint256 public weiRaisedTotal = 0;

    address private withdrawalWallet;

    CurrentToken public token;
    Whitelist public whitelist;

    modifier beforeReachingHardCap() {
        require(tokensRemainingIco > 0 && weiRaisedIco < maxcap);
        _;
    }

    modifier whenWhitelisted(address _wallet) {
        require(whitelist.isWhitelisted(_wallet));
        _;
    }

    /**
    * @dev Constructor for CurrentCrowdsale contract.
    * @dev Set the owner who can manage whitelist and token.
    * @param _maxcap The maxcap value.
    * @param _startPhase1 The phase1 ICO start time.
    * @param _startPhase2 The phase2 ICO start time.
    * @param _startPhase3 The phase3 ICO start time.
    * @param _endOfPhase3 The end time of ICO.
    * @param _withdrawalWallet The address to which raised funds will be withdrawn.
    * @param _rate exchange rate for ico.
    * @param _token address of token used for ico.
    * @param _whitelist address of whitelist contract used for ico.
    */
    function CurrentCrowdsale(
        uint256 _maxcap,
        uint256 _startPhase1,
        uint256 _startPhase2,
        uint256 _startPhase3,
        uint256 _endOfPhase3,
        address _withdrawalWallet,
        uint256 _rate,
        CurrentToken _token,
        Whitelist _whitelist
    )  TokenRate(_rate) public
    {
        require(_withdrawalWallet != address(0));
        require(_token != address(0) && _whitelist != address(0));
        require(_startPhase1 >= now);
        require(_endOfPhase3 > _startPhase3);
        require(_maxcap > 0);

        token = _token;
        whitelist = _whitelist;

        startPhase1 = _startPhase1;
        startPhase2 = _startPhase2;
        startPhase3 = _startPhase3;
        endOfPhase3 = _endOfPhase3;

        withdrawalWallet = _withdrawalWallet;

        maxcap = _maxcap;
        tokensSoldTotal = HARDCAP_TOKENS_PRE_ICO;
        weiRaisedTotal = tokensSoldTotal.div(_rate.mul(2));

        pushModifier(RateModifier(200, startPhase1));
        pushModifier(RateModifier(150, startPhase2));
        pushModifier(RateModifier(100, startPhase3));
    }

    /**
    * @dev Fallback function can be used to buy tokens.
    */
    function() public payable {
        if (isIco()) {
            sellTokensIco();
        } else {
            revert();
        }
    }

    /**
    * @dev Check whether the ICO is active at the moment.
    */
    function isIco() public constant returns (bool) {
        return now >= startPhase1 && now <= endOfPhase3;
    }

    function sellTokensIco() beforeReachingHardCap whenWhitelisted(msg.sender) whenNotPaused public payable {
        sellTokens(address(0));
    }

    function sellTokensIcoWithReferal(address referral) beforeReachingHardCap whenWhitelisted(msg.sender) whenNotPaused public payable {
        if (referral != msg.sender && whitelist.isWhitelisted(referral)) {
            sellTokens(referral);
        } else {
            revert();
        }
    }

    /**
    * @dev Manual send tokens to the specified address.
    * @param _beneficiary The address of a investor.
    * @param _tokensAmount Amount of tokens.
    */
    function manualSendTokens(address _beneficiary, uint256 _tokensAmount) public  onlyOwner {
        require(_beneficiary != address(0));
        require(_tokensAmount > 0);

        token.transfer(_beneficiary, _tokensAmount);
        tokensSoldIco = tokensSoldIco.add(_tokensAmount);
        tokensSoldTotal = tokensSoldTotal.add(_tokensAmount);
        tokensRemainingIco = tokensRemainingIco.sub(_tokensAmount);
    }

    /**
    * @dev Sell tokens during ICO with referral.
    */
    function sellTokens(address referral) beforeReachingHardCap whenWhitelisted(msg.sender) whenNotPaused internal {
        require(isIco());
        require(msg.value > 0);

        uint256 weiAmount = msg.value;
        uint256 excessiveFunds = 0;

        uint256 plannedWeiTotal = weiRaisedIco.add(weiAmount);

        if (plannedWeiTotal > maxcap) {
            excessiveFunds = plannedWeiTotal.sub(maxcap);
            weiAmount = maxcap.sub(weiRaisedIco);
        }
        bool isReferred = referral != address(0);
        uint256 tokensForUser = _getTokenAmountForBuyer(weiAmount, isReferred);
        uint256 tokensForReferral = _getTokenAmountForReferral(weiAmount, isReferred);
        uint256 tokensAmount = tokensForUser.add(tokensForReferral);

        if (tokensAmount > tokensRemainingIco) {
            uint256 weiToAccept = _getWeiValueOfTokens(tokensRemainingIco, isReferred);
            tokensForReferral = _getTokenAmountForReferral(weiToAccept, isReferred);
            tokensForUser = tokensRemainingIco.sub(tokensForReferral);
            excessiveFunds = excessiveFunds.add(weiAmount.sub(weiToAccept));

            tokensAmount = tokensRemainingIco;
            weiAmount = weiToAccept;
        }

        tokensSoldIco = tokensSoldIco.add(tokensAmount);
        tokensSoldTotal = tokensSoldTotal.add(tokensAmount);
        tokensRemainingIco = tokensRemainingIco.sub(tokensAmount);

        weiRaisedIco = weiRaisedIco.add(weiAmount);
        weiRaisedTotal = weiRaisedTotal.add(weiAmount);

        token.transfer(msg.sender, tokensForUser);
        if (isReferred) {
            token.transfer(referral, tokensForReferral);
        }

        if (excessiveFunds > 0) {
            msg.sender.transfer(excessiveFunds);
        }

        withdrawalWallet.transfer(this.balance);
    }
}
function() public payable {
    if (isIco()) {
        sellTokensIco();
    } else {
        revert();
    }
}

/**
 * @dev Fallback function can be used to buy tokens.
 */