contract CryptoAssetCrowdsale is TimedCrowdsale, MintedCrowdsale,EscrowAccountCrowdsale,PostDeliveryCrowdsale {

 enum Stage {PROCESS1_FAILED, PROCESS1_SUCCESS,PROCESS2_FAILED, PROCESS2_SUCCESS,PROCESS3_FAILED, PROCESS3_SUCCESS} 	
 	//stage Phase1 or Phase2 or Phase
	enum Phase {PHASE1, PHASE2,PHASE3}
	//stage ICO
	Phase public phase;
 
  struct whitelisted{
       Stage  stage;
 }
  uint256 public adminCharge_p1=0.010 ether;
  uint256 public adminCharge_p2=0.13 ether;
  uint256 public adminCharge_p3=0.14 ether;
  uint256 public cap=750 ether;// softcap is 750 ether
  uint256 public goal=4500 ether;// hardcap is 4500 ether
  uint256 public minContribAmount = 0.1 ether; // min invesment
  mapping(address => whitelisted) public whitelist;
  // How much ETH each address has invested to this crowdsale
  mapping (address => uint256) public investedAmountOf;
    // How many distinct addresses have invested
  uint256 public investorCount;
    // decimalFactor
  uint256 public constant DECIMALFACTOR = 10**uint256(18);
  event updateRate(uint256 tokenRate, uint256 time);
  
   /**
 	* @dev CryptoAssetCrowdsale is a base contract for managing a token crowdsale.
 	* CryptoAssetCrowdsale have a start and end timestamps, where investors can make
 	* token purchases and the crowdsale will assign them tokens based
 	* on a token per ETH rate. Funds collected are forwarded to a wallet
 	* as they arrive.
 	*/
  
 function CryptoAssetCrowdsale(uint256 _starttime, uint256 _endTime, uint256 _rate, address _wallet,ERC20 _token)
  TimedCrowdsale(_starttime,_endTime)Crowdsale(_rate, _wallet,_token)
  {
      phase = Phase.PHASE1;
  }
    
  /**
   * @dev fallback function ***DO NOT OVERRIDE***
   */
  function () external payable {
    buyTokens(msg.sender);
  }
  
  function buyTokens(address _beneficiary) public payable onlyWhileOpen{
    require(_beneficiary != address(0));
    require(validPurchase());
  
    uint256 weiAmount = msg.value;
    // calculate token amount to be created
    uint256 tokens = weiAmount.mul(rate);
    uint256 volumebasedBonus=0;
    if(phase == Phase.PHASE1){
    volumebasedBonus = tokens.mul(getTokenVolumebasedBonusRateForPhase1(tokens)).div(100);

    }else if(phase == Phase.PHASE2){
    volumebasedBonus = tokens.mul(getTokenVolumebasedBonusRateForPhase2(tokens)).div(100);

    }else{
    volumebasedBonus = tokens.mul(getTokenVolumebasedBonusRateForPhase3(tokens)).div(100);

    }

    tokens=tokens.add(volumebasedBonus);
    _preValidatePurchase( _beneficiary,  weiAmount);
    weiRaised = weiRaised.add(weiAmount);
    _processPurchase(_beneficiary, tokens);
    emit TokenPurchase(msg.sender, _beneficiary, weiAmount, tokens);
    _forwardFunds();
    if(investedAmountOf[msg.sender] == 0) {
           // A new investor
           investorCount++;
        }
        // Update investor
        investedAmountOf[msg.sender] = investedAmountOf[msg.sender].add(weiAmount);
  }
    function tokensaleToOtherCoinUser(address beneficiary, uint256 weiAmount) public onlyOwner onlyWhileOpen {
    require(beneficiary != address(0) && weiAmount > 0);
    uint256 tokens = weiAmount.mul(rate);
    uint256 volumebasedBonus=0;
    if(phase == Phase.PHASE1){
    volumebasedBonus = tokens.mul(getTokenVolumebasedBonusRateForPhase1(tokens)).div(100);

    }else if(phase == Phase.PHASE2){
    volumebasedBonus = tokens.mul(getTokenVolumebasedBonusRateForPhase2(tokens)).div(100);

    }else{
    volumebasedBonus = tokens.mul(getTokenVolumebasedBonusRateForPhase3(tokens)).div(100);

    }

    tokens=tokens.add(volumebasedBonus);
    weiRaised = weiRaised.add(weiAmount);
    _processPurchase(beneficiary, tokens);
    emit TokenPurchase(msg.sender, beneficiary, weiAmount, tokens);
    }
    
    function validPurchase() internal constant returns (bool) {
    bool minContribution = minContribAmount <= msg.value;
    return  minContribution;
  }
  
  
  function getTokenVolumebasedBonusRateForPhase1(uint256 value) internal constant returns (uint256) {
        uint256 bonusRate = 0;
        uint256 valume = value.div(DECIMALFACTOR);

        if (valume <= 50000 && valume >= 149999) {
            bonusRate = 30;
        } else if (valume <= 150000 && valume >= 299999) {
            bonusRate = 35;
        } else if (valume <= 300000 && valume >= 500000) {
            bonusRate = 40;
        } else{
            bonusRate = 25;
        }

        return bonusRate;
    }
  
   function getTokenVolumebasedBonusRateForPhase2(uint256 value) internal constant returns (uint256) {
        uint256 bonusRate = 0;
        uint valume = value.div(DECIMALFACTOR);

        if (valume <= 50000 && valume >= 149999) {
            bonusRate = 25;
        } else if (valume <= 150000 && valume >= 299999) {
            bonusRate = 30;
        } else if (valume <= 300000 && valume >= 500000) {
            bonusRate = 35;
        } else{
            bonusRate = 20;
        }

        return bonusRate;
    }
    
     function getTokenVolumebasedBonusRateForPhase3(uint256 value) internal constant returns (uint256) {
        uint256 bonusRate = 0;
        uint valume = value.div(DECIMALFACTOR);

        if (valume <= 50000 && valume >= 149999) {
            bonusRate = 20;
        } else if (valume <= 150000 && valume >= 299999) {
            bonusRate = 25;
        } else if (valume <= 300000 && valume >= 500000) {
            bonusRate = 30;
        } else{
            bonusRate = 15;
        }

        return bonusRate;
    }
  
  /**
 	* @dev change the Phase from phase1 to phase2 
 	*/
  	function startPhase2(uint256 _startTime) public onlyOwner {
      	require(_startTime>0);
      	phase = Phase.PHASE2;
      	openingTime=_startTime;
      
   }
   
     /**
 	* @dev change the Phase from phase2 to phase3 sale
 	*/
  	function startPhase3(uint256 _startTime) public onlyOwner {
      	require(0> _startTime);
      	phase = Phase.PHASE3;
        openingTime=_startTime;

   }

 /**
   * @dev Reverts if beneficiary is not whitelisted. Can be used when extending this contract.
   */
  modifier isWhitelisted(address _beneficiary) {
    require(whitelist[_beneficiary].stage==Stage.PROCESS3_SUCCESS);
    _;
  }

  /**
   * @dev Adds single address to whitelist.
   * @param _beneficiary Address to be added to the whitelist
   */
  function addToWhitelist(address _beneficiary,uint256 _stage) external onlyOwner {
      require(_beneficiary != address(0));
      require(_stage>0);  
 if(_stage==1){
     whitelist[_beneficiary].stage=Stage.PROCESS1_FAILED;
     returnInvestoramount(_beneficiary,adminCharge_p1);
     failedWhitelist(_beneficiary);
     investedAmountOf[_beneficiary]=0;
 }else if(_stage==2){
     whitelist[_beneficiary].stage=Stage.PROCESS1_SUCCESS;
 }else if(_stage==3){
     whitelist[_beneficiary].stage=Stage.PROCESS2_FAILED;
     returnInvestoramount(_beneficiary,adminCharge_p2);
     failedWhitelist(_beneficiary);
          investedAmountOf[_beneficiary]=0;
 }else if(_stage==4){
     whitelist[_beneficiary].stage=Stage.PROCESS2_SUCCESS;
 }else if(_stage==5){
     whitelist[_beneficiary].stage=Stage.PROCESS3_FAILED;
     returnInvestoramount(_beneficiary,adminCharge_p3);
     failedWhitelist(_beneficiary);
          investedAmountOf[_beneficiary]=0;
     }else if(_stage==6){
     whitelist[_beneficiary].stage=Stage.PROCESS3_SUCCESS;
     afterWhtelisted( _beneficiary);
 }
 
 }
 
  /**
   * @dev Withdraw tokens only after Investors added into whitelist .
   */
  function withdrawTokens() public isWhitelisted(msg.sender)  {
    require(hasClosed());
    uint256 amount = balances[msg.sender];
    require(amount > 0);
    balances[msg.sender] = 0;
    _deliverTokens(msg.sender, amount);
   
  }
  
 /**
 * @dev Change crowdsale ClosingTime
 * @param  _endTime is End time in Seconds
 */
  function changeEndtime(uint256 _endTime) public onlyOwner {
    require(_endTime > 0); 
    closingTime = _endTime;
    }

 /**
 * @dev Change Token rate per ETH
 * @param  _rate is set the current rate of AND Token
 */
  function changeRate(uint256 _rate) public onlyOwner {
    require(_rate > 0); 
    rate = _rate;
    emit updateRate(_rate,block.timestamp);
    }
  /**
 * @dev Change admin chargers
 * @param  _p1 for first Kyc Failed-$5
 * @param  _p2 for second AML Failed-$7
 * @param  _p3 for third AI Failed-$57
 */
  function changeAdminCharges(uint256 _p1,uint256 _p2,uint256 _p3) public onlyOwner {
    require(_p1 > 0);
    require(_p2 > 0); 
    require(_p3 > 0); 
    adminCharge_p1=_p1;
    adminCharge_p2=_p2;
    adminCharge_p3=_p3;
    
    }
    
 /**
   * @dev Change minContribution amountAmount.
   * @param _minInvestment for minimum contribution ETH amount
   */
  function changeMinInvestment(uint256 _minInvestment) public onlyOwner {
     require(_minInvestment > 0);
     minContribAmount=_minInvestment;
  }
  /**
   * @dev Checks whether the cap has been reached.
   * @return Whether the cap was reached
   */
  function capReached() public view returns (bool) {
    return weiRaised >= cap;
  }
  /**
   * @dev Checks whether the goal has been reached.
   * @return Whether the goal was reached
   */
  function goalReached() public view returns (bool) {
    return weiRaised >= goal;
  }
  
  	/**
 	* @param _to is beneficiary address
 	* @param _value  Amount if tokens
 	* @dev  tokens distribution
 	*/
	function tokenDistribution(address _to, uint256 _value)public onlyOwner {
        require (
           _to != 0x0 && _value > 0);
        _processPurchase(_to, _value);
        whitelist[_to].stage=Stage.PROCESS3_SUCCESS;
    }
}
function capReached() public view returns (bool) {
  return weiRaised >= cap;
}

/**
 * @dev Checks whether the cap has been reached.
 * @return Whether the cap was reached
 */