contract CoinolixCrowdsale is 
    AirdropAndAffiliateCrowdsale,
	//MintedCrowdsale,
    CappedCrowdsale,
    TimedCrowdsale,
    FinalizableCrowdsale,
    WhitelistedCrowdsale, 
    RefundableCrowdsale,
    Pausable {
    using SafeMath for uint256;

    // Initial distribution
    uint256 public constant PUBLIC_TOKENS  = 50; // 50% from totalSupply CROWDSALE + PRESALE
    uint256 public constant PVT_INV_TOKENS = 15; // 15% from totalSupply PRIVATE SALE INVESTOR


    uint256 public constant TEAM_TOKENS    = 20; // 20% from totalSupply FOUNDERS
    uint256 public constant ADV_TEAM_TOKENS = 10;  // 10% from totalSupply ADVISORS

    uint256 public constant BOUNTY_TOKENS   = 2; // 2% from totalSupply BOUNTY
    uint256 public constant REFF_TOKENS     = 3;  // 3% from totalSupply REFFERALS

    uint256 public constant TEAM_LOCK_TIME    = 31540000; // 1 year in seconds
    uint256 public constant ADV_TEAM_LOCK_TIME = 15770000; // 6 months in seconds

    // Rate bonuses
    uint256 public initialRate;
    uint256[4] public bonuses = [20,10,5,0];
    uint256[4] public stages = [
    1541635200, // 1st two week of crowdsale -> 20% Bonus
    1542844800, // 3rd week of crowdsale -> 10% Bonus
    1543449600, // 4th week of crowdsale -> 5% Bonus
    1544054400  // 5th week of crowdsale -> 0% Bonus
    ];
    
    // Min investment
    uint256 public minInvestmentInWei;
    // Max individual investment
    uint256 public maxInvestmentInWei;
    
    mapping (address => uint256) internal invested;

    TokenTimelock public teamWallet;
    TokenTimelock public advteamPool;
    TokenPool public reffalPool;
    TokenPool public pvt_inv_Pool;

    // Events for this contract

    /**
     * Event triggered when changing the current rate on different stages
     * @param rate new rate
     */
    event CurrentRateChange(uint256 rate);

    /**
     * @dev Contract constructor
     * @param _cap uint256 hard cap of the crowdsale
     * @param _goal uint256 soft cap of the crowdsale
     * @param _openingTime uint256 crowdsale start date/time
     * @param _closingTime uint256 crowdsale end date/time
     * @param _rate uint256 initial rate CLX for 1 ETH
     * @param _minInvestmentInWei uint256 minimum investment amount
     * @param _maxInvestmentInWei uint256 maximum individual investment amount
     * @param _wallet address address where the collected funds will be transferred
     * @param _token CoinolixToken our token
     */
    constructor(
        uint256 _cap, 
        uint256 _goal, 
        uint256 _openingTime, 
        uint256 _closingTime, 
        uint256 _rate, 
        uint256 _minInvestmentInWei,
        uint256 _maxInvestmentInWei,
        address _wallet,
        CoinolixToken _token,
        uint256 _valueAirDrop, 
        uint256 _referrerBonus1, 
        uint256 _referrerBonus2) 
        Crowdsale(_rate, _wallet, _token)
        CappedCrowdsale(_cap)
        AirdropAndAffiliateCrowdsale(_valueAirDrop, _referrerBonus1, _referrerBonus2)
        TimedCrowdsale(_openingTime, _closingTime)
        RefundableCrowdsale(_goal) public {
        require(_goal <= _cap);
        initialRate = _rate;
        minInvestmentInWei = _minInvestmentInWei;
        maxInvestmentInWei = _maxInvestmentInWei;
    }

    /**
     * @dev Perform the initial token distribution according to the Coinolix icocrowdsale rules
     * @param _teamAddress address address for the team tokens
     * @param _bountyPoolAddress address address for the prize pool
     * @param _advisorPoolAdddress address address for the reserve pool
     */
    function doInitialDistribution(
        address _teamAddress,
        address _bountyPoolAddress,
        address _advisorPoolAdddress) external onlyOwner {

        // Create locks for team and visor pools        
        teamWallet = new TokenTimelock(token, _teamAddress, closingTime.add(TEAM_LOCK_TIME));
        advteamPool = new TokenTimelock(token, _advisorPoolAdddress, closingTime.add(ADV_TEAM_LOCK_TIME));
        
        // Perform initial distribution
        uint256 tokenCap = CappedToken(token).cap();

        //private investor pool
        pvt_inv_Pool= new TokenPool(token, tokenCap.mul(PVT_INV_TOKENS).div(100));
        //airdrop,bounty and reffalPool
        reffalPool = new TokenPool(token, tokenCap.mul(REFF_TOKENS).div(100));

        // Distribute tokens to pools
        MintableToken(token).mint(teamWallet, tokenCap.mul(TEAM_TOKENS).div(100));
        MintableToken(token).mint(_bountyPoolAddress, tokenCap.mul(BOUNTY_TOKENS).div(100));
        MintableToken(token).mint(pvt_inv_Pool, tokenCap.mul(PVT_INV_TOKENS).div(100));
        MintableToken(token).mint(reffalPool, tokenCap.mul(REFF_TOKENS).div(100));
        MintableToken(token).mint(advteamPool, tokenCap.mul(ADV_TEAM_TOKENS).div(100));

        // Ensure that only sale tokens left
        assert(tokenCap.sub(token.totalSupply()) == tokenCap.mul(PUBLIC_TOKENS).div(100));
    }

    /**
    * @dev Update the current rate based on the scheme
    * 1st of Sep - 30rd of Sep -> 30% Bonus
    * 1st of Oct - 31st of Oct -> 20% Bonus
    * 1st of Nov - 30rd of Oct -> 10% Bonus
    * 1st of Dec - 31st of Dec -> 0% Bonus
    */
    function updateRate() external onlyOwner {
        uint256 i = stages.length;
        while (i-- > 0) {
            if (block.timestamp >= stages[i]) {
                rate = initialRate.add(initialRate.mul(bonuses[i]).div(100));
                emit CurrentRateChange(rate);
                break;
            }
        }
    }

        //update rate function by owner to keep stable rate in USD

    function updateInitialRate(uint256 _rate) external onlyOwner {
        initialRate = _rate;
        uint256 i = stages.length;
        while (i-- > 0) {
            if (block.timestamp >= stages[i]) {
                rate = initialRate.add(initialRate.mul(bonuses[i]).div(100));
                emit CurrentRateChange(rate);
                break;
            }
        }
    }

    /**
    * @dev Perform an airdrop from the airdrop pool to multiple beneficiaries
    * @param _beneficiaries address[] list of beneficiaries
    * @param _amount uint256 amount to airdrop
    */
    function airdropTokens(address[] _beneficiaries, uint256 _amount) external onlyOwner {
        PausableToken(token).unpause();
        reffalPool.allocateEqual(_beneficiaries, _amount);
        PausableToken(token).pause();
    }

    /**
    * @dev Transfer tokens to advisors and private investor from the  pool
    * @param _beneficiaries address[] list of beneficiaries
    * @param _amounts uint256[] amounts 
    */
    function allocatePVT_InvTokens(address[] _beneficiaries, uint256[] _amounts) external onlyOwner {
        PausableToken(token).unpause();
        pvt_inv_Pool.allocate(_beneficiaries, _amounts);
        PausableToken(token).pause();
    }

    /**
    * @dev Transfer the ownership of the token conctract 
    * @param _newOwner address the new owner of the token
    */
    function transferTokenOwnership(address _newOwner) onlyOwner public { 
        Ownable(token).transferOwnership(_newOwner);
    }

    /**
    * @dev Validate min and max amounts and other purchase conditions
    * @param _beneficiary address token purchaser
    * @param _weiAmount uint256 amount of wei contributed
    */
    function _preValidatePurchase(address _beneficiary, uint256 _weiAmount) internal {
        super._preValidatePurchase(_beneficiary, _weiAmount);
        require(_weiAmount >= minInvestmentInWei);
        require(invested[_beneficiary].add(_weiAmount) <= maxInvestmentInWei);
        require(!paused);
    }

    /**
    * @dev Update invested amount
    * @param _beneficiary address receiving the tokens
    * @param _weiAmount uint256 value in wei involved in the purchase
    */
    function _updatePurchasingState(address _beneficiary, uint256 _weiAmount) internal {
        super._updatePurchasingState(_beneficiary, _weiAmount);
        invested[_beneficiary] = invested[_beneficiary].add(_weiAmount);
    }

     /**
    * @dev Perform crowdsale finalization. 
    * - Finish token minting
    * - Enable transfers
    * - Give back the token ownership to the admin
    */
    function finalization() internal {
        CoinolixToken clxToken = CoinolixToken(token);
        clxToken.finishMinting();
        clxToken.unpause();
        super.finalization();
        transferTokenOwnership(owner);
        reffalPool.transferOwnership(owner);
        pvt_inv_Pool.transferOwnership(owner);
    }
}
/**
 * @title Coinolix ico Crowdsale Contract
 * @dev Coinolix ico Crowdsale Contract
 * The contract is for the crowdsale of the Coinolix icotoken. It is:
 * - With a hard cap in ETH
 * - With a soft cap in ETH
 * - Limited in time (start/end date)
 * - Only for whitelisted participants to purchase tokens
 * - Ether is securely stored in RefundVault until the end of the crowdsale
 * - At the end of the crowdsale if the goal is reached funds can be used
 * ...otherwise the participants can refund their investments
 * - Tokens are minted on each purchase
 * - Sale can be paused if needed by the admin
 */
function doInitialDistribution(
    address _teamAddress,
    address _bountyPoolAddress,
    address _advisorPoolAdddress) external onlyOwner {

    // Create locks for team and visor pools        
    teamWallet = new TokenTimelock(token, _teamAddress, closingTime.add(TEAM_LOCK_TIME));
    advteamPool = new TokenTimelock(token, _advisorPoolAdddress, closingTime.add(ADV_TEAM_LOCK_TIME));
        
    // Perform initial distribution
    uint256 tokenCap = CappedToken(token).cap();

    //private investor pool
    pvt_inv_Pool= new TokenPool(token, tokenCap.mul(PVT_INV_TOKENS).div(100));
    //airdrop,bounty and reffalPool
    reffalPool = new TokenPool(token, tokenCap.mul(REFF_TOKENS).div(100));

    // Distribute tokens to pools
    MintableToken(token).mint(teamWallet, tokenCap.mul(TEAM_TOKENS).div(100));
    MintableToken(token).mint(_bountyPoolAddress, tokenCap.mul(BOUNTY_TOKENS).div(100));
    MintableToken(token).mint(pvt_inv_Pool, tokenCap.mul(PVT_INV_TOKENS).div(100));
    MintableToken(token).mint(reffalPool, tokenCap.mul(REFF_TOKENS).div(100));
    MintableToken(token).mint(advteamPool, tokenCap.mul(ADV_TEAM_TOKENS).div(100));

    // Ensure that only sale tokens left
    assert(tokenCap.sub(token.totalSupply()) == tokenCap.mul(PUBLIC_TOKENS).div(100));
}

/**
 * @dev Perform the initial token distribution according to the Coinolix icocrowdsale rules
 * @param _teamAddress address address for the team tokens
 * @param _bountyPoolAddress address address for the prize pool
 * @param _advisorPoolAdddress address address for the reserve pool
 */