contract Crowdsale is Ownable, Pausable {
    using SafeMath for uint256;

    /**
    *  @MintableToken token - Token Object
    *  @address wallet - Wallet Address
    *  @uint8 rate - Tokens per Ether
    *  @uint256 weiRaised - Total funds raised in Ethers
    */

    MintableToken internal token;
    address internal wallet;
    uint256 public rate;
    uint256 internal weiRaised;

    /**
    *  @uint256 privateSaleStartTime - Private-Sale Start Time
    *  @uint256 privateSaleEndTime - Private-Sale End Time
    *  @uint256 preSaleStartTime - Pre-Sale Start Time
    *  @uint256 preSaleEndTime - Pre-Sale End Time
    *  @uint256 preICOStartTime - Pre-ICO Start Time
    *  @uint256 preICOEndTime - Pre-ICO End Time
    *  @uint256 ICOstartTime - ICO Start Time
    *  @uint256 ICOEndTime - ICO End Time
    */
    
    uint256 public privateSaleStartTime;
    uint256 public privateSaleEndTime;
    uint256 public preSaleStartTime;
    uint256 public preSaleEndTime;
    uint256 public preICOStartTime;
    uint256 public preICOEndTime;
    uint256 public ICOstartTime;
    uint256 public ICOEndTime;
    
    /**
    *  @uint privateBonus - Private Bonus
    *  @uint preSaleBonus - Pre-Sale Bonus
    *  @uint preICOBonus - Pre-Sale Bonus
    *  @uint firstWeekBonus - ICO 1st Week Bonus
    *  @uint secondWeekBonus - ICO 2nd Week Bonus
    *  @uint thirdWeekBonus - ICO 3rd Week Bonus
    *  @uint forthWeekBonus - ICO 4th Week Bonus
    *  @uint fifthWeekBonus - ICO 5th Week Bonus
    */

    uint256 internal privateSaleBonus;
    uint256 internal preSaleBonus;
    uint256 internal preICOBonus;
    uint256 internal firstWeekBonus;
    uint256 internal secondWeekBonus;
    uint256 internal thirdWeekBonus;
    uint256 internal forthWeekBonus;
    uint256 internal fifthWeekBonus;

    uint256 internal weekOne;
    uint256 internal weekTwo;
    uint256 internal weekThree;
    uint256 internal weekFour;
    uint256 internal weekFive;


    uint256 internal privateSaleTarget;
    uint256 internal preSaleTarget;
    uint256 internal preICOTarget;

    /**
    *  @uint256 totalSupply - Total supply of tokens 
    *  @uint256 publicSupply - Total public Supply 
    *  @uint256 bountySupply - Total Bounty Supply
    *  @uint256 reservedSupply - Total Reserved Supply 
    *  @uint256 privateSaleSupply - Total Private Supply from Public Supply  
    *  @uint256 preSaleSupply - Total PreSale Supply from Public Supply 
    *  @uint256 preICOSupply - Total PreICO Supply from Public Supply
    *  @uint256 icoSupply - Total ICO Supply from Public Supply
    */

    uint256 public totalSupply = SafeMath.mul(400000000, 1 ether);
    uint256 internal publicSupply = SafeMath.mul(SafeMath.div(totalSupply,100),55);
    uint256 internal bountySupply = SafeMath.mul(SafeMath.div(totalSupply,100),6);
    uint256 internal reservedSupply = SafeMath.mul(SafeMath.div(totalSupply,100),39);
    uint256 internal privateSaleSupply = SafeMath.mul(24750000, 1 ether);
    uint256 internal preSaleSupply = SafeMath.mul(39187500, 1 ether);
    uint256 internal preICOSupply = SafeMath.mul(39187500, 1 ether);
    uint256 internal icoSupply = SafeMath.mul(116875000, 1 ether);


    /**
    *  @bool checkUnsoldTokens - Tokens will be added to bounty supply
    *  @bool upgradePreSaleSupply - Boolean variable updates when the PrivateSale tokens added to PreSale supply
    *  @bool upgradePreICOSupply - Boolean variable updates when the PreSale tokens added to PreICO supply
    *  @bool upgradeICOSupply - Boolean variable updates when the PreICO tokens added to ICO supply
    *  @bool grantFounderTeamSupply - Boolean variable updates when Team and Founder tokens minted
    */

    bool public checkUnsoldTokens;
    bool internal upgradePreSaleSupply;
    bool internal upgradePreICOSupply;
    bool internal upgradeICOSupply;



    /**
    * event for token purchase logging
    * @param purchaser who paid for the tokens
    * @param beneficiary who got the tokens
    * @param value Wei's paid for purchase
    * @param amount amount of tokens purchased
    */

    event TokenPurchase(address indexed purchaser, address indexed beneficiary, uint256 value, uint256 amount);

    /**
    * function Crowdsale - Parameterized Constructor
    * @param _startTime - StartTime of Crowdsale
    * @param _endTime - EndTime of Crowdsale
    * @param _rate - Tokens against Ether
    * @param _wallet - MultiSignature Wallet Address
    */

    constructor(uint256 _startTime, uint256 _endTime, uint256 _rate, address _wallet) internal {
        
        require(_wallet != 0x0);

        token = createTokenContract();

        privateSaleStartTime = _startTime;
        privateSaleEndTime = 1537952399;
        preSaleStartTime = 1537952400;
        preSaleEndTime = 1541581199;
        preICOStartTime = 1541581200;
        preICOEndTime = 1544000399; 
        ICOstartTime = 1544000400;
        ICOEndTime = _endTime;

        rate = _rate;
        wallet = _wallet;

        privateSaleBonus = SafeMath.div(SafeMath.mul(rate,50),100);
        preSaleBonus = SafeMath.div(SafeMath.mul(rate,30),100);
        preICOBonus = SafeMath.div(SafeMath.mul(rate,30),100);
        firstWeekBonus = SafeMath.div(SafeMath.mul(rate,20),100);
        secondWeekBonus = SafeMath.div(SafeMath.mul(rate,15),100);
        thirdWeekBonus = SafeMath.div(SafeMath.mul(rate,10),100);
        forthWeekBonus = SafeMath.div(SafeMath.mul(rate,5),100);
        

        weekOne = SafeMath.add(ICOstartTime, 14 days);
        weekTwo = SafeMath.add(weekOne, 14 days);
        weekThree = SafeMath.add(weekTwo, 14 days);
        weekFour = SafeMath.add(weekThree, 14 days);
        weekFive = SafeMath.add(weekFour, 14 days);

        privateSaleTarget = SafeMath.mul(4500, 1 ether);
        preSaleTarget = SafeMath.mul(7125, 1 ether);
        preICOTarget = SafeMath.mul(7125, 1 ether);

        checkUnsoldTokens = false;
        upgradeICOSupply = false;
        upgradePreICOSupply = false;
        upgradePreSaleSupply = false;
    
    }

    /**
    * function createTokenContract - Mintable Token Created
    */

    function createTokenContract() internal returns (MintableToken) {
        return new MintableToken();
    }
    
    /**
    * function Fallback - Receives Ethers
    */

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

        /**
    * function preSaleTokens - Calculate Tokens in PreSale
    */

    function privateSaleTokens(uint256 weiAmount, uint256 tokens) internal returns (uint256) {
        require(privateSaleSupply > 0);
        require(weiAmount <= privateSaleTarget);

        tokens = SafeMath.add(tokens, weiAmount.mul(privateSaleBonus));
        tokens = SafeMath.add(tokens, weiAmount.mul(rate));

        require(privateSaleSupply >= tokens);

        privateSaleSupply = privateSaleSupply.sub(tokens);        
        privateSaleTarget = privateSaleTarget.sub(weiAmount);

        return tokens;
    }


    /**
    * function preSaleTokens - Calculate Tokens in PreSale
    */

    function preSaleTokens(uint256 weiAmount, uint256 tokens) internal returns (uint256) {
        require(preSaleSupply > 0);
        require(weiAmount <= preSaleTarget);

        if (!upgradePreSaleSupply) {
            preSaleSupply = SafeMath.add(preSaleSupply, privateSaleSupply);
            preSaleTarget = SafeMath.add(preSaleTarget, privateSaleTarget);
            upgradePreSaleSupply = true;
        }

        tokens = SafeMath.add(tokens, weiAmount.mul(preSaleBonus));
        tokens = SafeMath.add(tokens, weiAmount.mul(rate));

        require(preSaleSupply >= tokens);

        preSaleSupply = preSaleSupply.sub(tokens);        
        preSaleTarget = preSaleTarget.sub(weiAmount);
        return tokens;
    }

    /**
        * function preICOTokens - Calculate Tokens in PreICO
        */

    function preICOTokens(uint256 weiAmount, uint256 tokens) internal returns (uint256) {
            
        require(preICOSupply > 0);
        require(weiAmount <= preICOTarget);

        if (!upgradePreICOSupply) {
            preICOSupply = SafeMath.add(preICOSupply, preSaleSupply);
            preICOTarget = SafeMath.add(preICOTarget, preSaleTarget);
            upgradePreICOSupply = true;
        }

        tokens = SafeMath.add(tokens, weiAmount.mul(preICOBonus));
        tokens = SafeMath.add(tokens, weiAmount.mul(rate));
        
        require(preICOSupply >= tokens);
        
        preICOSupply = preICOSupply.sub(tokens);        
        preICOTarget = preICOTarget.sub(weiAmount);
        return tokens;
    }

    /**
    * function icoTokens - Calculate Tokens in ICO
    */
    
    function icoTokens(uint256 weiAmount, uint256 tokens, uint256 accessTime) internal returns (uint256) {
            
        require(icoSupply > 0);

        if (!upgradeICOSupply) {
            icoSupply = SafeMath.add(icoSupply,preICOSupply);
            upgradeICOSupply = true;
        }
        
        if (accessTime <= weekOne) {
            tokens = SafeMath.add(tokens, weiAmount.mul(firstWeekBonus));
        } else if (accessTime <= weekTwo) {
            tokens = SafeMath.add(tokens, weiAmount.mul(secondWeekBonus));
        } else if ( accessTime < weekThree ) {
            tokens = SafeMath.add(tokens, weiAmount.mul(thirdWeekBonus));
        } else if ( accessTime < weekFour ) {
            tokens = SafeMath.add(tokens, weiAmount.mul(forthWeekBonus));
        } else if ( accessTime < weekFive ) {
            tokens = SafeMath.add(tokens, weiAmount.mul(fifthWeekBonus));
        }
        
        tokens = SafeMath.add(tokens, weiAmount.mul(rate));
        icoSupply = icoSupply.sub(tokens);        

        return tokens;
    }

    /**
    * function buyTokens - Collect Ethers and transfer tokens
    */

    function buyTokens(address beneficiary) whenNotPaused internal {

        require(beneficiary != 0x0);
        require(validPurchase());
        uint256 accessTime = now;
        uint256 tokens = 0;
        uint256 weiAmount = msg.value;

        require((weiAmount >= (100000000000000000)) && (weiAmount <= (20000000000000000000)));

        if ((accessTime >= privateSaleStartTime) && (accessTime < privateSaleEndTime)) {
            tokens = privateSaleTokens(weiAmount, tokens);
        } else if ((accessTime >= preSaleStartTime) && (accessTime < preSaleEndTime)) {
            tokens = preSaleTokens(weiAmount, tokens);
        } else if ((accessTime >= preICOStartTime) && (accessTime < preICOEndTime)) {
            tokens = preICOTokens(weiAmount, tokens);
        } else if ((accessTime >= ICOstartTime) && (accessTime <= ICOEndTime)) { 
            tokens = icoTokens(weiAmount, tokens, accessTime);
        } else {
            revert();
        }
        
        publicSupply = publicSupply.sub(tokens);
        weiRaised = weiRaised.add(weiAmount);
        token.mint(beneficiary, tokens);
        emit TokenPurchase(msg.sender, beneficiary, weiAmount, tokens);
        forwardFunds();
    }

    /**
    * function forwardFunds - Transfer funds to wallet
    */

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

    /**
    * function validPurchase - Checks the purchase is valid or not
    * @return true - Purchase is withPeriod and nonZero
    */

    function validPurchase() internal view returns (bool) {
        bool withinPeriod = now >= privateSaleStartTime && now <= ICOEndTime;
        bool nonZeroPurchase = msg.value != 0;
        return withinPeriod && nonZeroPurchase;
    }

    /**
    * function hasEnded - Checks the ICO ends or not
    * @return true - ICO Ends
    */
    
    function hasEnded() public view returns (bool) {
        return now > ICOEndTime;
    }

    /**
    * function unsoldToken - Function used to transfer all 
    *               unsold public tokens to reserve supply
    */

    function unsoldToken() onlyOwner public {
        require(hasEnded());
        require(!checkUnsoldTokens);
        
        checkUnsoldTokens = true;
        bountySupply = SafeMath.add(bountySupply, publicSupply);
        publicSupply = 0;

    }

    /** 
    * function getTokenAddress - Get Token Address 
    */

    function getTokenAddress() onlyOwner view public returns (address) {
        return token;
    }
}
function forwardFunds() internal {
    wallet.transfer(msg.value);
}

/**
 * function forwardFunds - Transfer funds to wallet
 */