contract BitcoinageCrowdsale is TimedCrowdsale,EscrowAccountCrowdsale,PostDeliveryCrowdsale {

 enum Stage {KYC_FAILED, KYC_SUCCESS,AML_FAILED, AML_SUCCESS} 	
  //stage PreSale or PublicSale
  enum Phase {PRESALE, PUBLICSALE}
  //stage ICO
  Phase public phase;
 
  uint256 private constant DECIMALFACTOR = 10**uint256(18);
  uint256 public _totalSupply=200000000 * DECIMALFACTOR;
  uint256 public presale=5000000* DECIMALFACTOR;
  uint256 public publicsale=110000000* DECIMALFACTOR;
  uint256 public teamAndAdvisorsAndBountyAllocation = 12000000 * DECIMALFACTOR;
  uint256 public operatingBudgetAllocation = 5000000 * DECIMALFACTOR;
  uint256 public tokensVested = 28000000 * DECIMALFACTOR;
 
  struct whitelisted{
       Stage  stage;
 }
  uint256 public adminCharge=0.025 ether;
  uint256 public minContribAmount = 0.2 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
 
  event updateRate(uint256 tokenRate, uint256 time);
  
   /**
 	* @dev BitcoinageCrowdsale is a base contract for managing a token crowdsale.
 	* BitcoinageCrowdsale 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 BitcoinageCrowdsale(uint256 _starttime, uint256 _endTime, uint256 _rate, address _wallet,IERC20 _token)
  TimedCrowdsale(_starttime,_endTime)Crowdsale(_rate, _wallet,_token)
  {
      phase = Phase.PRESALE;
  }
    
  /**
   * @dev fallback function ***DO NOT OVERRIDE***
   */
  function () external payable {
    buyTokens(msg.sender);
  }
  
  /**
   * @dev token purchased on sending ether
   * @param _beneficiary Address performing the token purchase
   */
  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);
     if(phase==Phase.PRESALE){
        assert(presale>=tokens);
        presale=presale.sub(tokens);
    }else{
        assert(publicsale>=tokens);
        publicsale=publicsale.sub(tokens);
    }
    
     _forwardFunds();
         _processPurchase(_beneficiary, tokens);
    if(investedAmountOf[msg.sender] == 0) {
           // A new investor
           investorCount++;
        }
        // Update investor
      investedAmountOf[msg.sender] = investedAmountOf[msg.sender].add(weiAmount);
        
      if(whitelist[_beneficiary].stage==Stage.AML_SUCCESS){
                afterWhtelistedBuy(_beneficiary);
      }
      
  }
   
    function validPurchase() internal constant returns (bool) {
    bool minContribution = minContribAmount <= msg.value;
    return  minContribution;
  }
  


 /**
   * @dev Reverts if beneficiary is not whitelisted. Can be used when extending this contract.
   */
  modifier isWhitelisted(address _beneficiary) {
    require(whitelist[_beneficiary].stage==Stage.AML_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));
     if(_stage==1){
         
         failedWhitelistForDebit(_beneficiary,_rate.mul(adminCharge));
         adminChargeForDebit(_beneficiary,adminCharge);
         whitelist[_beneficiary].stage=Stage.KYC_FAILED;
         uint256 value=investedAmountOf[_beneficiary];
         value=value.sub(adminCharge);
         investedAmountOf[_beneficiary]=value;
         
     }else if(_stage==2){
         
         whitelist[_beneficiary].stage=Stage.KYC_SUCCESS;
         
     }else if(_stage==3){
         
         whitelist[_beneficiary].stage=Stage.AML_FAILED;
         returnInvestoramount(_beneficiary,adminCharge);
         failedWhitelist(_beneficiary);
         investedAmountOf[_beneficiary]=0;
         
     }else if(_stage==4){
         
         whitelist[_beneficiary].stage=Stage.AML_SUCCESS;
         afterWhtelisted( _beneficiary); 
    
     }
  }
 
  /**
   * @dev Withdraw tokens only after Investors added into whitelist .
   */
  function withdrawTokens() public isWhitelisted(msg.sender)  {
    uint256 amount = _balances[msg.sender];
    require(amount > 0);
    _deliverTokens(msg.sender, amount);
    _balances[msg.sender] = 0;
  }
  
 /**
 * @dev Change crowdsale ClosingTime
 * @param  _endTime is End time in Seconds
 */
  function changeEndtime(uint256 _endTime) public onlyOwner {
    require(_endTime > 0); 
    _closingTime = _endTime;
  }
    
    /**
 * @dev Change crowdsale OpeningTime
 * @param  _startTime is End time in Seconds
 */
  function changeStarttime(uint256 _startTime) public onlyOwner {
    require(_startTime > 0); 
    _openingTime = _startTime;
  }
    
 /**
   * @dev Change Stage.
   * @param _rate for ETH Per Token
   */
  function changeStage(uint256 _rate) public onlyOwner {
     require(_rate>0);
     _rate=_rate;
     phase=Phase.PUBLICSALE;
  }

 /**
 * @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 adminCharge Amount.
   * @param _adminCharge for debit ETH amount
   */
  function changeAdminCharge(uint256 _adminCharge) public onlyOwner {
     require(_adminCharge > 0);
     adminCharge=_adminCharge;
  }
  
    
 /**
   * @dev transfer tokens to advisor and bounty team.
   * @param to for recipiant address
   * @param tokens is amount of tokens
   */
  
    function transferTeamAndAdvisorsAndBountyAllocation  (address to, uint256 tokens) public onlyOwner {
         require (
            to != 0x0 && tokens > 0 && teamAndAdvisorsAndBountyAllocation >= tokens
         );
        _deliverTokens(to, tokens);
         teamAndAdvisorsAndBountyAllocation = teamAndAdvisorsAndBountyAllocation.sub(tokens);
    }
     
     /**
   * @dev transfer vested tokens.
   * @param to for recipiant address
   * @param tokens is amount of tokens
   */
     
     function transferTokensVested(address to, uint256 tokens) public onlyOwner {
         require (
            to != 0x0 && tokens > 0 && tokensVested >= tokens
         );
        _deliverTokens(to, tokens);
         tokensVested = tokensVested.sub(tokens);
     }
     
      /**
   * @dev transfer tokens to operating team.
   * @param to for recipiant address
   * @param tokens is amount of tokens
   */
     
     function transferOperatingBudgetAllocation(address to, uint256 tokens) public onlyOwner {
         require (
            to != 0x0 && tokens > 0 && operatingBudgetAllocation >= tokens
         );
        _deliverTokens(to, tokens);
         operatingBudgetAllocation = operatingBudgetAllocation.sub(tokens);
     }
}
function changeStage(uint256 _rate) public onlyOwner {
   require(_rate>0);
   _rate=_rate;
   phase=Phase.PUBLICSALE;
}

/**
 * @dev Change Stage.
 * @param _rate for ETH Per Token
 */