contract Crowdsale is Ownable {

    using SafeMath for uint256;

    TestToken public token;
    uint256 public tokensForPreICO;
    uint256 public tokenCapForFirstMainStage;
    uint256 public tokenCapForSecondMainStage;
    uint256 public tokenCapForThirdMainStage;
    uint256 public tokenCapForFourthMainStage;
    uint256 public totalTokensForSale;
    uint256 public startTime;
    uint256 public endTime;
    address public wallet;
    uint256 public rate;
    uint256 public weiRaised;

    uint256[4] public ICObonusStages;

    uint256 public preICOduration;
    bool public mainSaleActive;

    uint256 public tokensSold;

    /**
    * event for token purchase logging
    * @param purchaser who paid for the tokens
    * @param beneficiary who got the tokens
    * @param value weis paid for purchase
    * @param amount amount of tokens purchased
    */
    event TokenPurchase(address indexed purchaser, address indexed beneficiary, uint256 value, uint256 amount);
    event ICOSaleExtended(uint256 newEndTime);

    function Crowdsale() public {
        token = new TestToken();  //msg.sender is passed as the founder wallet which will recieve the 15,000,000 tokens
        startTime = now; 
        rate = 1160;
        wallet = 0xc85095257946D88585ab12050890bdd6f07a80F0;
        tokensForPreICO = 4500000e18;

        tokenCapForFirstMainStage = 11500000e18;  //7,000,000 + 4,500,000
        tokenCapForSecondMainStage = 18500000e18;  //11,500,000 + 7,000,000
        tokenCapForThirdMainStage = 25500000e18;  //18,500,000 + 7,000,000
        tokenCapForFourthMainStage = 35000000e18;  //25,500,000 + 9,500,000

        totalTokensForSale = 35000000e18;
        tokensSold = 0;

        preICOduration = now.add(30 minutes);
        endTime = preICOduration;

        mainSaleActive = false;
    }

    function() external payable {
        buyTokens(msg.sender);
    }

    function buyTokens(address _addr) public payable {
        require(validPurchase() && tokensSold < totalTokensForSale);
        require(_addr != 0x0 && msg.value > 100 finney);  
        uint256 toMint;
        if(now <= preICOduration) {
            if(tokensSold >= tokensForPreICO) { revert(); }
            toMint = msg.value.mul(rate.mul(2));
        } else {
            if(!mainSaleActive) { revert(); }
            toMint = msg.value.mul(getRateWithBonus());
        }
        tokensSold = tokensSold.add(toMint);
        token.mint(_addr, toMint);
        forwardFunds();
    }

    function forwardFunds() internal {
        wallet.transfer(msg.value);
    }

    function processOfflinePurchase(address _to, uint256 _toMint) public onlyOwner {
        require(tokensSold.add(_toMint) <= totalTokensForSale);
        require(_toMint > 0 && _to != 0x0);
        tokensSold = tokensSold.add(_toMint);
        token.mint(_to, _toMint);
    }

    function validPurchase() internal view returns (bool) {
        bool withinPeriod = now >= startTime && now <= endTime;                                 // Dins periode total ICO
        bool nonZeroPurchase = msg.value != 0;                                                  // ETH enviats diferent de 0
        return withinPeriod && nonZeroPurchase;
    }


    function getRateWithBonus() internal view returns (uint256 rateWithDiscount) {
        if (now > preICOduration && tokensSold < totalTokensForSale) {
            return rate.mul(getCurrentBonus()).div(100).add(rate);
            return rateWithDiscount;
        }
        return rate;
    }

    /**
    * Function is called when the buy function is invoked  only after the pre sale duration and returns 
    * the current discount in percentage. In this example, the discount starts at 20% and reduces by 5% for 
    * every discount stage that has passed. After the 8th week, the discount percentage will be set to 0.
    *
    * day 31 - 37   / week 1: 20%
    * day 38 - 44   / week 2: 15%
    * day 45 - 51   / week 3: 10%
    * day 52 - 58   / week 4:  0%
    */
    function getCurrentBonus() internal view returns (uint256 discount) {
        require(tokensSold < tokenCapForFourthMainStage);
        uint256 timeStamp = now;
        uint256 stage;

        for (uint i = 0; i < ICObonusStages.length; i++) {
            if (timeStamp <= ICObonusStages[i]) {
                stage = i + 1;
                break;
            } 
        } 

        if(stage == 1 && tokensSold < tokenCapForFirstMainStage) { discount = 20; }
        if(stage == 1 && tokensSold >= tokenCapForFirstMainStage) { discount = 15; }
        if(stage == 1 && tokensSold >= tokenCapForSecondMainStage) { discount = 10; }
        if(stage == 1 && tokensSold >= tokenCapForThirdMainStage) { discount = 0; }

        if(stage == 2 && tokensSold < tokenCapForSecondMainStage) { discount = 15; }
        if(stage == 2 && tokensSold >= tokenCapForSecondMainStage) { discount = 10; }
        if(stage == 2 && tokensSold >= tokenCapForThirdMainStage) { discount = 0; }

        if(stage == 3 && tokensSold < tokenCapForThirdMainStage) { discount = 10; }
        if(stage == 3 && tokensSold >= tokenCapForThirdMainStage) { discount = 0; }

        if(stage == 4) { discount = 0; }

        return discount;
    }


    
    /**
    * Function activates the main ICO only when the duration of the preICO hass finished. This function
    * can only be called by the owner of the contract. Once called, the bonus stages will be set as such:
    * week 1 will have 20% bonus, week 2 will have 15% bonus, week 3 will have 10% bonus and week 4 will 
    * have no bonus.
    **/
    function activateMainSale() public onlyOwner {
        require(now > preICOduration || tokensSold >= tokensForPreICO);
        require(!mainSaleActive);
        if(now < preICOduration) { preICOduration = now; }
        mainSaleActive = true;
        ICObonusStages[0] = now.add(7 minutes);

        for (uint y = 1; y < ICObonusStages.length; y++) {
            ICObonusStages[y] = ICObonusStages[y - 1].add(7 minutes);
        }

        endTime = ICObonusStages[3];
    }

    function extendDuration(uint256 _newEndTime) public onlyOwner {
        require(endTime < _newEndTime && mainSaleActive);
        endTime = _newEndTime;
        ICOSaleExtended(_newEndTime);
    }


    function hasEnded() public view returns (bool) { 
        return now > endTime;
    }

    /**
    * Allows the owner of the ICO contract to unpause the token contract. This function is needed
    * because the ICO contract deploys a new instance of the token contract, and by default the 
    * ETH address which deploys a contract which is Ownable is assigned ownership of the contract,
    * so the ICO contract is the owner of the token contract. Since unpause is a function which can
    * only be executed by the owner, by adding this function here, then the owner of the ICO contract
    * can call this and then the ICO contract will invoke the unpause function of the token contract
    * and thus the token contract will successfully unpause as its owner the ICO contract invokend
    * the the function. 
    */
    function unpauseToken() public onlyOwner {
        token.unpause();
    }
}
function getCurrentBonus() internal view returns (uint256 discount) {
    require(tokensSold < tokenCapForFourthMainStage);
    uint256 timeStamp = now;
    uint256 stage;

    for (uint i = 0; i < ICObonusStages.length; i++) {
        if (timeStamp <= ICObonusStages[i]) {
            stage = i + 1;
            break;
        } 
    } 

    if(stage == 1 && tokensSold < tokenCapForFirstMainStage) { discount = 20; }
    if(stage == 1 && tokensSold >= tokenCapForFirstMainStage) { discount = 15; }
    if(stage == 1 && tokensSold >= tokenCapForSecondMainStage) { discount = 10; }
    if(stage == 1 && tokensSold >= tokenCapForThirdMainStage) { discount = 0; }

    if(stage == 2 && tokensSold < tokenCapForSecondMainStage) { discount = 15; }
    if(stage == 2 && tokensSold >= tokenCapForSecondMainStage) { discount = 10; }
    if(stage == 2 && tokensSold >= tokenCapForThirdMainStage) { discount = 0; }

    if(stage == 3 && tokensSold < tokenCapForThirdMainStage) { discount = 10; }
    if(stage == 3 && tokensSold >= tokenCapForThirdMainStage) { discount = 0; }

    if(stage == 4) { discount = 0; }

    return discount;
}

/**
 * Function is called when the buy function is invoked  only after the pre sale duration and returns 
 * the current discount in percentage. In this example, the discount starts at 20% and reduces by 5% for 
 * every discount stage that has passed. After the 8th week, the discount percentage will be set to 0.
 *
 * day 31 - 37   / week 1: 20%
 * day 38 - 44   / week 2: 15%
 * day 45 - 51   / week 3: 10%
 * day 52 - 58   / week 4:  0%
 */