contract BTHRTokenSale is FinalizableCrowdsale {
    using SafeMath for uint256;

    // Define sale
    uint public constant RATE = 17500;
    uint public constant TOKEN_SALE_LIMIT = 25000 * 1000000000000000000;

    uint256 public constant TOKENS_FOR_OPERATIONS = 400000000*(10**18);
    uint256 public constant TOKENS_FOR_SALE = 600000000*(10**18);

    uint public constant TOKENS_FOR_PRESALE = 315000000*(1 ether / 1 wei);

    uint public constant FRST_CRWDSALE_RATIO = TOKENS_FOR_PRESALE + 147875000*(1 ether / 1 wei);//30% bonus
    uint public constant SCND_CRWDSALE_RATIO = FRST_CRWDSALE_RATIO + 110687500*(1 ether / 1 wei);//15% bonus

    enum Phase {
        Created,//Inital phase after deploy
        PresaleRunning, //Presale phase
        Paused, //Pause phase between pre-sale and main token sale or emergency pause function
        ICORunning, //Crowdsale phase
        FinishingICO //Final phase when crowdsale is closed and time is up
    }

    Phase public currentPhase = Phase.Created;

    event LogPhaseSwitch(Phase phase);

    // Constructor
    function BTHRTokenSale(uint256 _end, address _wallet)
    FinalizableCrowdsale()
    Crowdsale(_end, _wallet) {
    }

    /// @dev Lets buy you some tokens.
    function buyTokens(address _buyer) public payable {
        // Available only if presale or crowdsale is running.
        require((currentPhase == Phase.PresaleRunning) || (currentPhase == Phase.ICORunning));
        require(_buyer != address(0));
        require(msg.value > 0);
        require(validPurchase());

        uint tokensWouldAddTo = 0;
        uint weiWouldAddTo = 0;
        
        uint256 weiAmount = msg.value;
        
        uint newTokens = msg.value.mul(RATE);
        
        weiWouldAddTo = weiRaised.add(weiAmount);
        
        require(weiWouldAddTo <= TOKEN_SALE_LIMIT);

        newTokens = addBonusTokens(token.totalSupply(), newTokens);
        
        tokensWouldAddTo = newTokens.add(token.totalSupply());
        require(tokensWouldAddTo <= TOKENS_FOR_SALE);
        
        token.mint(_buyer, newTokens);
        TokenPurchase(msg.sender, _buyer, weiAmount, newTokens);
        
        weiRaised = weiWouldAddTo;
        forwardFunds();
        if (weiRaised == TOKENS_FOR_SALE){
            weiCapReached = true;
        }
    }

    // @dev Adds bonus tokens by token supply bought by user
    // @param _totalSupply total supply of token bought during pre-sale/crowdsale
    // @param _newTokens tokens currently bought by user
    function addBonusTokens(uint256 _totalSupply, uint256 _newTokens) internal view returns (uint256) {

        uint returnTokens = 0;
        uint tokensToAdd = 0;
        uint tokensLeft = _newTokens;

        if(currentPhase == Phase.PresaleRunning){
            if(_totalSupply < TOKENS_FOR_PRESALE){
                if(_totalSupply + tokensLeft + tokensLeft.mul(50).div(100) > TOKENS_FOR_PRESALE){
                    tokensToAdd = TOKENS_FOR_PRESALE.sub(_totalSupply);
                    tokensToAdd = tokensToAdd.mul(100).div(150);
                    
                    returnTokens = returnTokens.add(tokensToAdd);
                    returnTokens = returnTokens.add(tokensToAdd.mul(50).div(100));
                    tokensLeft = tokensLeft.sub(tokensToAdd);
                    _totalSupply = _totalSupply.add(tokensToAdd.add(tokensToAdd.mul(50).div(100)));
                } else { 
                    returnTokens = returnTokens.add(tokensLeft).add(tokensLeft.mul(50).div(100));
                    tokensLeft = tokensLeft.sub(tokensLeft);
                }
            }
        } 
        
        if (tokensLeft > 0 && _totalSupply < FRST_CRWDSALE_RATIO) {
            
            if(_totalSupply + tokensLeft + tokensLeft.mul(30).div(100)> FRST_CRWDSALE_RATIO){
                tokensToAdd = FRST_CRWDSALE_RATIO.sub(_totalSupply);
                tokensToAdd = tokensToAdd.mul(100).div(130);
                returnTokens = returnTokens.add(tokensToAdd).add(tokensToAdd.mul(30).div(100));
                tokensLeft = tokensLeft.sub(tokensToAdd);
                _totalSupply = _totalSupply.add(tokensToAdd.add(tokensToAdd.mul(30).div(100)));
                
            } else { 
                returnTokens = returnTokens.add(tokensLeft);
                returnTokens = returnTokens.add(tokensLeft.mul(30).div(100));
                tokensLeft = tokensLeft.sub(tokensLeft);
            }
        }
        
        if (tokensLeft > 0 && _totalSupply < SCND_CRWDSALE_RATIO) {
            
            if(_totalSupply + tokensLeft + tokensLeft.mul(15).div(100) > SCND_CRWDSALE_RATIO){

                tokensToAdd = SCND_CRWDSALE_RATIO.sub(_totalSupply);
                tokensToAdd = tokensToAdd.mul(100).div(115);
                returnTokens = returnTokens.add(tokensToAdd).add(tokensToAdd.mul(15).div(100));
                tokensLeft = tokensLeft.sub(tokensToAdd);
                _totalSupply = _totalSupply.add(tokensToAdd.add(tokensToAdd.mul(15).div(100)));
            } else { 
                returnTokens = returnTokens.add(tokensLeft);
                returnTokens = returnTokens.add(tokensLeft.mul(15).div(100));
                tokensLeft = tokensLeft.sub(tokensLeft);
            }
        }
        
        if (tokensLeft > 0)  {
            returnTokens = returnTokens.add(tokensLeft);
            tokensLeft = tokensLeft.sub(tokensLeft);
        }
        return returnTokens;
    }

    function validPurchase() internal view returns (bool) {
        bool withinPeriod = now <= endTime;
        bool nonZeroPurchase = msg.value != 0;
        bool isRunning = ((currentPhase == Phase.ICORunning) || (currentPhase == Phase.PresaleRunning));
        return withinPeriod && nonZeroPurchase && isRunning;
    }

    function setSalePhase(Phase _nextPhase) public onlyOwner {
    
        bool canSwitchPhase
        =  (currentPhase == Phase.Created && _nextPhase == Phase.PresaleRunning)
        || (currentPhase == Phase.PresaleRunning && _nextPhase == Phase.Paused)
        || ((currentPhase == Phase.PresaleRunning || currentPhase == Phase.Paused)
        && _nextPhase == Phase.ICORunning)
        || (currentPhase == Phase.ICORunning && _nextPhase == Phase.Paused)
        || (currentPhase == Phase.Paused && _nextPhase == Phase.PresaleRunning)
        || (currentPhase == Phase.Paused && _nextPhase == Phase.FinishingICO)
        || (currentPhase == Phase.ICORunning && _nextPhase == Phase.FinishingICO);

        require(canSwitchPhase);
        currentPhase = _nextPhase;
        LogPhaseSwitch(_nextPhase);
    }

    // Finalize
    function finalization() internal {
        uint256 toMint = TOKENS_FOR_OPERATIONS;
        token.mint(wallet, toMint);
        token.finishMinting();
        token.transferOwnership(wallet);
    }
}
function addBonusTokens(uint256 _totalSupply, uint256 _newTokens) internal view returns (uint256) {

        uint returnTokens = 0;
        uint tokensToAdd = 0;
        uint tokensLeft = _newTokens;

        if(currentPhase == Phase.PresaleRunning){
            if(_totalSupply < TOKENS_FOR_PRESALE){
                if(_totalSupply + tokensLeft + tokensLeft.mul(50).div(100) > TOKENS_FOR_PRESALE){
                    tokensToAdd = TOKENS_FOR_PRESALE.sub(_totalSupply);
                    tokensToAdd = tokensToAdd.mul(100).div(150);
                    
                    returnTokens = returnTokens.add(tokensToAdd);
                    returnTokens = returnTokens.add(tokensToAdd.mul(50).div(100));
                    tokensLeft = tokensLeft.sub(tokensToAdd);
                    _totalSupply = _totalSupply.add(tokensToAdd.add(tokensToAdd.mul(50).div(100)));
                } else { 
                    returnTokens = returnTokens.add(tokensLeft).add(tokensLeft.mul(50).div(100));
                    tokensLeft = tokensLeft.sub(tokensLeft);
                }
            }
        } 
        
        if (tokensLeft > 0 && _totalSupply < FRST_CRWDSALE_RATIO) {
            
            if(_totalSupply + tokensLeft + tokensLeft.mul(30).div(100)> FRST_CRWDSALE_RATIO){
                tokensToAdd = FRST_CRWDSALE_RATIO.sub(_totalSupply);
                tokensToAdd = tokensToAdd.mul(100).div(130);
                returnTokens = returnTokens.add(tokensToAdd).add(tokensToAdd.mul(30).div(100));
                tokensLeft = tokensLeft.sub(tokensToAdd);
                _totalSupply = _totalSupply.add(tokensToAdd.add(tokensToAdd.mul(30).div(100)));
                
            } else { 
                returnTokens = returnTokens.add(tokensLeft);
                returnTokens = returnTokens.add(tokensLeft.mul(30).div(100));
                tokensLeft = tokensLeft.sub(tokensLeft);
            }
        }
        
        if (tokensLeft > 0 && _totalSupply < SCND_CRWDSALE_RATIO) {
            
            if(_totalSupply + tokensLeft + tokensLeft.mul(15).div(100) > SCND_CRWDSALE_RATIO){

                tokensToAdd = SCND_CRWDSALE_RATIO.sub(_totalSupply);
                tokensToAdd = tokensToAdd.mul(100).div(115);
                returnTokens = returnTokens.add(tokensToAdd).add(tokensToAdd.mul(15).div(100));
                tokensLeft = tokensLeft.sub(tokensToAdd);
                _totalSupply = _totalSupply.add(tokensToAdd.add(tokensToAdd.mul(15).div(100)));
            } else { 
                returnTokens = returnTokens.add(tokensLeft);
                returnTokens = returnTokens.add(tokensLeft.mul(15).div(100));
                tokensLeft = tokensLeft.sub(tokensLeft);
            }
        }
        
        if (tokensLeft > 0)  {
            returnTokens = returnTokens.add(tokensLeft);
            tokensLeft = tokensLeft.sub(tokensLeft);
        }
        return returnTokens;
    }
// @dev Adds bonus tokens by token supply bought by user
// @param _totalSupply total supply of token bought during pre-sale/crowdsale
// @param _newTokens tokens currently bought by user