contract GESTokenCrowdSale is Ownable {
  using SafeMath for uint256;

  /* true for finalised crowdsale */
  bool public isFinalised;

  /* The token object */
  MintableToken public token;

  /* Start and end timestamps where investments are allowed (both inclusive) */
  uint256 public mainSaleStartTime;
  uint256 public mainSaleEndTime;

  /* Address where funds are transferref after collection */
  address public wallet;

  /* Address where company funds will be collected */
  address public tokenWallet;

  /* How many token units a buyer gets per ether */
  uint256 public rate = 10000;

  /* Amount of raised money in wei */
  /* PreSale + Whitelist =  1273.779099 ETH*/
  uint256 public weiRaised = 1273779099000000000000 ;

  /* 20,000 - 1,273.779099 = 18,726 is what will be raised*/

  /* Minimum amount of Wei allowed per transaction = 0.1 Ethers */
  uint256 public saleMinimumWei = 100000000000000000; 
  
  /* Hard Cap amount of Wei allowed 20,000 ETH */
  uint256 public hardCap = 20000000000000000000000; 
  
  /* Hard Cap amount oftokens to be sold 300000000 */
  /* Amount raise in preSale removing the extra company 11% as we are allocating here */
  /* 300000000 - 12235717 - 33000000 - 6178952 = 248585331 */
  /* Tokens to be sold in the ICO 248585330 */
  uint256 public tokensToSell = 248585331 * 10 ** 18; 

  /* Always default to 20 can go upto 50 base don amount being sent */
   struct AmountBonus {
    uint256 amount;
    uint percent;
  }
  AmountBonus[] public amountBonuses;
  /**
   * event for token purchase logging
   * event for finalizing the crowdsale
   */
  event TokenPurchase(address indexed purchaser, address indexed beneficiary, uint256 value, uint256 amount);
  event FinalisedCrowdsale(uint256 totalSupply);

  function GESTokenCrowdSale(uint256 _mainSaleStartTime, uint256 _mainSaleEndTime, address _wallet, address _tokenWallet) public {

    /* Can't start main sale in the past */
    require(_mainSaleStartTime >= now);
    /* Can't close main sale earlier than start time */
    require(_mainSaleEndTime >= _mainSaleStartTime);

    /* Confirming wallet addresses as valid */
    require(_wallet != 0x0);
    require(_tokenWallet != 0x0);

    /* Create GES token */
    token = createTokenContract();
    
    amountBonuses.push(AmountBonus(    50000000000000000000, 20));
    amountBonuses.push(AmountBonus(   100000000000000000000, 25));
    amountBonuses.push(AmountBonus(   250000000000000000000, 30));
    amountBonuses.push(AmountBonus(   500000000000000000000, 35));
    amountBonuses.push(AmountBonus(  1000000000000000000000, 40));
    amountBonuses.push(AmountBonus(  2500000000000000000000, 45));
    amountBonuses.push(AmountBonus(200000000000000000000000, 50));


    mainSaleStartTime = _mainSaleStartTime;
    mainSaleEndTime = _mainSaleEndTime;

    wallet = _wallet ;
    tokenWallet = _tokenWallet;

    isFinalised = false;

    /* Mint tokens for previous backers [Removed the previous 11% the company raised in presale]*/
    /* 101964.308375680000000000 * 120 = 12235717 -> rounding to highest integer */
    /* Fixed tokens for the whitelist money raised = 6178952 */
    /* Fixed tokens for the management and bounty = 33000000 */
    /* Total to allot: 6178952 + 12235717 + 33000000 = 51414669 */
    token.mint(tokenWallet, 51414669 * 10 ** 18);
  }

  /* Creates the token to be sold */
  function createTokenContract() internal returns (MintableToken) {
    return new GESToken();
  }

  /* Fallback function can be used to buy tokens */
  function () public payable {
    buyTokens(msg.sender);
  }

  /* Low level token purchase function */
  function buyTokens(address beneficiary) public payable {
    require(!isFinalised);
    require(beneficiary != 0x0);
    require(msg.value != 0);
    require(now >= mainSaleStartTime && now <= mainSaleEndTime);
    uint256 newRaise = weiRaised.add(msg.value);
    require(msg.value >= saleMinimumWei && newRaise <= hardCap);

    /* Add bonus to tokens depends on the value */
    uint256 bonusedTokens = applyBonus(msg.value);
    
    /* Check if we have available tokens to sell */
    require(bonusedTokens < tokensToSell);

    /* Update state on the blockchain */
    weiRaised = newRaise;
    tokensToSell = tokensToSell.sub(bonusedTokens);
    token.mint(beneficiary, bonusedTokens);
    TokenPurchase(msg.sender, beneficiary, msg.value, bonusedTokens);
  }

  /* Finish Crowdsale,
   */
  function finaliseCrowdsale() external onlyOwner returns (bool) {
    require(!isFinalised);
    token.finishMinting();
    forwardFunds();
    FinalisedCrowdsale(token.totalSupply());
    isFinalised = true;
    return true;
  }

  /* Pause the token contract */
  function pauseToken() external onlyOwner {
    require(!isFinalised);
    GESToken(token).pause();
  }

  /* Unpause the token contract */
  function unpauseToken() external onlyOwner {
    GESToken(token).unpause();
  }

  /* Transfer token's contract ownership to a new owner */
  function transferTokenOwnership(address newOwner) external onlyOwner {
    GESToken(token).transferOwnership(newOwner);
  }

  /* @return true if main sale event has ended */
  function mainSaleHasEnded() external constant returns (bool) {
    return now > mainSaleEndTime;
  }

  /* Send ether to the fund collection wallet */
  function forwardFunds() internal {
    wallet.transfer(this.balance);
  }

  /* Set new dates for main-sale (emergency case) */
  function setMainSaleDates(uint256 _mainSaleStartTime, uint256 _mainSaleEndTime) public onlyOwner returns (bool) {
    require(!isFinalised);
    require(_mainSaleStartTime < _mainSaleEndTime);
    mainSaleStartTime = _mainSaleStartTime;
    mainSaleEndTime = _mainSaleEndTime;
    return true;
  }

  /* Function to calculate bonus tokens based on the amount sent by the contributor */
  function applyBonus(uint256 weiAmount) internal constant returns (uint256 bonusedTokens) {
    /* Bonus tokens to be added */
    uint256 tokensToAdd = 0;

    /* Calculting the amont of tokens to be allocated based on rate and the money transferred */
    uint256 tokens = weiAmount.mul(rate);
    
    for(uint8 i = 0; i < amountBonuses.length; i++){
        if(weiAmount < amountBonuses[i].amount){
           tokensToAdd = tokens.mul(amountBonuses[i].percent).div(100);
            return tokens.add(tokensToAdd);
        }
    }
    /* Default callback at 20%, just as a precaution */
    return tokens.mul(120).div(100);
  }

  /*  
  * Function to extract funds as required before finalizing
  */
  function fetchFunds() onlyOwner public {
    wallet.transfer(this.balance);
  }

}
/**
 * @title Crowdsale
 * @dev Modified contract for managing a token crowdsale.
 */
function transferTokenOwnership(address newOwner) external onlyOwner {
  GESToken(token).transferOwnership(newOwner);
}

/* Transfer token's contract ownership to a new owner */