contract Sale is NIX{

    using SafeMath for uint256;
    // To indicate Sale status; saleStatus=0 => sale not started; saleStatus=1=> sale started; saleStatus=2=> sale finished
    uint256 public saleStatus; 
    // To store sale type; saleType=0 => private sale ,saleType=1 => PreSale; saleType=2 => public sale
    uint256 public saleType; 
    //price of token in cents
    uint256 public tokenCostPrivate = 8; //5 cents i.e., .5$
    //price of token in cents
    uint256 public tokenCostPre = 9; //5 cents i.e., .5$
    //price of token in cents
    uint256 public tokenCostPublic = 10; //5 cents i.e., .5$
    //1 eth = usd in cents, eg: 1 eth = 107.91$ so, 1 eth = =107,91 cents
    uint256 public ETH_USD;
    //min contribution 
    uint256 public minContribution = 1000000; //10000,00 cents i.e., 10000.00$
    //coinbase account
    address public wallet;
    //soft cap
    uint256 public softCap = 500000000; //$5 million
    //hard cap
    uint256 public hardCap = 1500000000; //$15 million
    //store total wei raised
    uint256 public weiRaised;
    //initially whitelisting is false  
    bool public whitelistingEnabled = false;

    //Structure to store token sent and wei received by the buyer of tokens
    struct Investor {
        uint256 weiReceived;
        uint256 tokenSent;
    }

    //investors indexed by their ETH address
    mapping(address => Investor) public investors;
    //whitelisting address
    mapping (address => bool) public whitelisted;

    
    /*
    * constructor invoked with wallet and eth_usd parameter
    */
    constructor (address _wallet, uint256 _ETH_USD) public{
      require(_wallet != address(0x0), "wallet address must not be zero");
      wallet = _wallet;
      ETH_USD = _ETH_USD;
    }
    
    /*
    * fallback function to create tokens
    */
    function () external payable{
        createTokens(msg.sender);
    }

    /*
    * function to change wallet
    */
    function changeWallet(address _wallet) public onlyOwner{
      require(_wallet != address(0x0), "wallet address must not be zero");
      wallet = _wallet;
    }

    /*
    * drain ethers from contract
    */
    function drain() external onlyOwner{
      wallet.transfer(address(this).balance);
    }

    /*
    * function to change whitelisting status
    */
    function toggleWhitelist() public onlyOwner{
        whitelistingEnabled = !whitelistingEnabled;
    }

    /*
    * function to change eth usd rate
    */
    function changeETH_USD(uint256 _ETH_USD) public onlyOwner{
        ETH_USD = _ETH_USD;
    }

    /*
    * function to add user to whitelist
    */
    function whitelistAddress(address investor) public onlyOwner{
        require(!whitelisted[investor], "users is already whitelisted");
        whitelisted[investor] = true;
    }

    /*
    * To start private sale
    */
    function startPrivateSale(uint256 _ETH_USD) public onlyOwner{
      require (saleStatus == 0);
      ETH_USD = _ETH_USD;
      saleStatus = 1;
    }

    /*
    * To start pre sale
    */
    function startPreSale(uint256 _ETH_USD) public onlyOwner{
      require (saleStatus == 1 && saleType == 0);
      ETH_USD = _ETH_USD;
      saleType = 1;
    }

    /*
    * To start public sale
    */
    function startPublicSale(uint256 _ETH_USD) public onlyOwner{
      require (saleStatus == 1 && saleType == 1);
      ETH_USD = _ETH_USD;
      saleType = 2;
    }

    /**
    * Set new minimum contribution
    * _minContribution minimum contribution in cents
    *
    */
    function changeMinContribution(uint256 _minContribution) public onlyOwner {
        require(_minContribution > 0, "min contribution should be greater than 0");
        minContribution = _minContribution;
    }

    /*
    * To create NIX Token and assign to transaction initiator
    */
    function createTokens(address _beneficiary) internal {
       _preValidatePurchase(_beneficiary, msg.value);
      //Calculate NIX Token to send
      uint256 totalNumberOfTokenTransferred = _getTokenAmount(msg.value);

      //transfer tokens to investor
      transferTokens(_beneficiary, totalNumberOfTokenTransferred);

      //initializing structure for the address of the beneficiary
      Investor storage _investor = investors[_beneficiary];
      //Update investor's balance
      _investor.tokenSent = _investor.tokenSent.add(totalNumberOfTokenTransferred);
      _investor.weiReceived = _investor.weiReceived.add(msg.value);
      weiRaised = weiRaised.add(msg.value);
      wallet.transfer(msg.value);
    }
    
    function transferTokens(address toAddr, uint256 value) private{
        balances[owner] = balances[owner].sub(value);
        balances[toAddr] = balances[toAddr].add(value);
        emit Transfer(owner, toAddr, value);
    }

    /**
    * function to create number of tokens to be transferred
    *
    */

    function _getTokenAmount(uint256 _weiAmount) internal view returns (uint256) {
      if(saleType == 0){
        return (_weiAmount.mul(ETH_USD).mul(100)).div((tokenCostPrivate).mul(80)); // 20% discount
      }else if(saleType == 1){
        return (_weiAmount.mul(ETH_USD).mul(100)).div((tokenCostPrivate).mul(90)); // 10% discount
      }else if (saleType == 2){
        return (_weiAmount.mul(ETH_USD).mul(100)).div((tokenCostPrivate).mul(95)); //5% discount
      }
    }

    /**
    * validatess sale requirement before creating tokens
    *
    */
    function _preValidatePurchase(address _beneficiary, uint256 _weiAmount) whenNotPaused internal view{
      require(_beneficiary != address(0), "beneficiary address must not be zero");
      require(whitelistingEnabled == false || whitelisted[_beneficiary],
                "whitelisting should be disabled or users should be whitelisted");
      //Make sure Sale is running
      assert(saleStatus == 1);
      require(_weiAmount >= getMinContributionInWei(), "amount is less than min contribution" );
    }

    /**
    * gives minimum contribution in wei
    * the min contribution value in wei
    *
    */
    function getMinContributionInWei() public view returns(uint256){
      return (minContribution.mul(1e18)).div(ETH_USD);
    }

    /**
    * gives usd raised based on wei raised
    * the usd value in cents
    *
    */
    function usdRaised() public view returns (uint256) {
      return weiRaised.mul(ETH_USD).div(1e18);
    }

    /**
    * tell soft cap reached or not
    * bool true=> if reached
    *
    */
    function isSoftCapReached() public view returns (bool) {
      if(usdRaised() >= softCap){
        return true;
      }
    }

    /**
    * tell hard cap reached or not
    * bool true=> if reached
    *
    */
    function isHardCapReached() public view returns (bool) {
      if(usdRaised() >= hardCap){
        return true;
      }
    }

}
function isSoftCapReached() public view returns (bool) {
  if(usdRaised() >= softCap){
    return true;
  }
}

/**
 * tell soft cap reached or not
 * bool true=> if reached
 *
 */