contract MestCrowdsale is RefundableCrowdsale,CappedCrowdsale {


//decimal value
 uint256 public constant decimalFactor = 10 ** uint256(18);
 
//Available tokens for PublicAllocation
uint256 public publicAllocation = 80000000 *decimalFactor; //80%
//Available token for FounderAllocation
uint256 public _founder = 20000000* decimalFactor; //20%

FounderAllocation founderAllocation;

// How much ETH each address has invested to this crowdsale
mapping (address => uint256) public investedAmountOf;
// How many distinct addresses have invested
uint256 public investorCount;
uint256 public minContribAmount = 0.1 ether; // minimum contribution amount is 0.1 ether

event Burn(address indexed burner, uint256 value);
uint256 public whitelistMaxContribAmount = 2.5 ether; // 2.5 ether

  

//status to find  whitelist investor's max contribution amount
struct whiteListInStruct{
uint256 status;

}

//investor claim their amount  between refunding Starttime && refunding Endtime
uint256 public refundingStarttime;
uint256 public refundingEndtime=90 days;

//To store whitelist investors address and status
  
mapping(address => whiteListInStruct[]) whiteList;



// Constructor
// Token Creation and presale starts
//Start time end time should be given in unix timestamps
function MestCrowdsale(uint256 _startTime, uint256 _endTime, uint256 _rate, address _wallet, uint256 _goal, uint256 _cap)

    Crowdsale (_startTime, _endTime, _rate, _wallet)  RefundableCrowdsale(_goal*decimalFactor) CappedCrowdsale(_cap*decimalFactor)
  {

  }
  function createTokenContract() internal returns (MintableToken) {
    return new Mest();
  }

  // low level token purchase function
  // @notice buyTokens
  // @param beneficiary The address of the beneficiary
  // @return the transaction address and send the event as TokenPurchase
 function buyTokens(address beneficiary) public payable {
      require(publicAllocation > 0);
       require(validPurchase());
      uint256  weiAmount = msg.value;
          require(isVerified(beneficiary,weiAmount));
       // calculate token amount to be created
    uint256 tokens = weiAmount.mul(rate);

    uint256 Bonus = tokens.mul(getVolumBonusRate()).div(100);

    tokens = tokens.add(Bonus);



       if(investedAmountOf[beneficiary] == 0) {
           // A new investor
           investorCount++;
        }
        // Update investor
        investedAmountOf[beneficiary] = investedAmountOf[beneficiary].add(weiAmount);

            assert (tokens <= publicAllocation);
            publicAllocation = publicAllocation.sub(tokens);


       forwardFunds();
       weiRaised = weiRaised.add(weiAmount);
       token.mint(beneficiary, tokens);
       TokenPurchase(msg.sender, beneficiary, weiAmount, tokens);
    
     }


     // @return true if the transaction can buy tokens
    function validPurchase() internal constant returns (bool) {
        bool minContribution = minContribAmount <= msg.value;
        bool withinPeriod = now >= startTime && now <= endTime;
        bool nonZeroPurchase = msg.value != 0;
        bool Publicsale =publicAllocation !=0;
        return withinPeriod && minContribution && nonZeroPurchase && Publicsale;
    }
   // @return  current time
    function getNow() public constant returns (uint) {
        return (now);
    }

    // ------------------------------------------------------------------------
  // Add to whitelist
  // ------------------------------------------------------------------------

    function addtoWhitelist(address _to, uint256 _status)public onlyOwner returns (bool){

    if(whiteList[_to].length==0) {

    whiteList[_to].push(whiteListInStruct(uint256(_status)));
     return true;

    }else if(whiteList[_to].length>0){

        for (uint i = 0; i < whiteList[_to].length; i++){
            whiteList[_to][i].status=_status;

        }

         return true;

    }
}

//whiteList verification

function isVerified(address _address, uint256 _amt)internal  returns  ( bool){

   if(whiteList[_address].length > 0) {
    for (uint i = 0; i < whiteList[_address].length; i++){
    if(whiteList[_address][i].status==0 ){
        if( whitelistMaxContribAmount>=_amt+ investedAmountOf[_address])return true;

    }
         if(whiteList[_address][i].status==1){
             return true;
         }

         }

   }
}




       // Get the Volume-based bonus rate
       function getVolumBonusRate() internal constant returns (uint256) {
        uint256 bonusRate = 0;
        if(!goalReached()){
            bonusRate=10;

        }
           return bonusRate;
       }
    //if the user not claim after 90days, owner revoke the ether to wallet
     function revoke() public onlyOwner {
         require(getNow()>refundingEndtime);
          require(isFinalized);
          vault.close();
     }
     
     
// if crowdsale is unsuccessful, investors can claim refunds here
  function claimRefund() public {
        require(getNow()<=refundingEndtime);
        require(isFinalized);
        require(!goalReached());
      
         vault.refund(msg.sender);
      
      
  }
  
     
 
  //it will call when   crowdsale unsuccessful  if crowdsale  completed
  function finalization() internal {
        refundingStarttime=getNow();
        refundingEndtime=refundingEndtime.add(getNow());
       
       if(goalReached()){
        founderAllocation = new FounderAllocation();
        token.mint(address(founderAllocation), _founder);
        _founder=_founder.sub(_founder);
       }else if(!goalReached()){
           
           
            Burn(msg.sender, _founder);
             _founder=0;
       }
        
        token.finishMinting();
        super.finalization();
         
  }

 
  // Change crowdsale Starttime 
  function changeStarttime(uint256 _startTime) public onlyOwner {

           
            startTime = _startTime;
        }
        
          // Change the changeminContribAmount
      function changeminContribAmount(uint256 _minContribAmount) public onlyOwner {
        require(_minContribAmount != 0);
         minContribAmount = _minContribAmount;

      }
        
  // Change crowdsale  Endtime 
  function changeEndtime(uint256 _endTime) public onlyOwner {

            endTime = _endTime;
           
        }

        // Change the token price
       function changeRate(uint256 _rate) public onlyOwner {
         require(_rate != 0);
          rate = _rate;

       }

       // Change the goal
      function changeGoal(uint256 _softcap) public onlyOwner {
        require(_softcap != 0);
         goal = _softcap;

      }


      // Change the whiteList Maximum contribution amount
     function changeMaximumContribution(uint256 _whitelistMaxContribAmount) public onlyOwner {
       require(_whitelistMaxContribAmount != 0);
        whitelistMaxContribAmount = _whitelistMaxContribAmount;
        
     }


  
            
      //change  Publicallocation
    function changePublicallocation (uint256  _value) onlyOwner  {
        publicAllocation = _value.mul(decimalFactor);
       
    }
        
        
        
    //change  wallet address
    function changeWallet (address _wallet) onlyOwner  {
        wallet = _wallet;
       
    }
        
            
        //Burns a specific amount of tokens
    function burnToken(uint256 _value) onlyOwner {
        require(_value > 0 &&_value <= publicAllocation);
         publicAllocation = publicAllocation.sub(_value.mul(decimalFactor));

        
        Burn(msg.sender, _value);
    }}
function MestCrowdsale(uint256 _startTime, uint256 _endTime, uint256 _rate, address _wallet, uint256 _goal, uint256 _cap)

    Crowdsale (_startTime, _endTime, _rate, _wallet)  RefundableCrowdsale(_goal*decimalFactor) CappedCrowdsale(_cap*decimalFactor)
  {

  }

// Constructor
// Token Creation and presale starts
//Start time end time should be given in unix timestamps