contract ICOCrowdsale is Ownable, Pausable, FinalizableCrowdsale {

  uint256 constant PRESALE_CAP = 2727 ether;
  uint256 constant PRESALE_RATE = 316;
  uint256 constant PRESALE_DURATION = 23 days;

  uint256 constant MAIN_SALE_START = 1527771600;
  uint256 constant BONUS_1_CAP = PRESALE_CAP + 3636 ether;
  uint256 constant BONUS_1_RATE = 292;

  uint256 constant BONUS_2_CAP = BONUS_1_CAP + 7273 ether;
  uint256 constant BONUS_2_RATE = 269;

  uint256 constant BONUS_3_CAP = BONUS_2_CAP + 9091 ether;
  uint256 constant BONUS_3_RATE = 257;

  uint256 constant BONUS_4_CAP = BONUS_3_CAP + 10909 ether;
  uint256 constant BONUS_4_RATE = 245;

  uint256 constant NORMAL_RATE = 234;

  address tokenAddress;

  event LogBountyTokenMinted(address minter, address beneficiary, uint256 amount);

  function ICOCrowdsale(uint256 _startTime, uint256 _endTime, address _wallet, address _tokenAddress) public
    FinalizableCrowdsale()
    Crowdsale(_startTime, _endTime, NORMAL_RATE, _wallet)
  {
    require((_endTime-_startTime) > (15 * 1 days));
    require(_tokenAddress != address(0x0));
    tokenAddress = _tokenAddress;
    token = createTokenContract();
  }

  /**
   * Invoked on initialization of the contract
   */
  function createTokenContract() internal returns (MintableToken) {
    return ICOToken(tokenAddress);
  }

  function finalization() internal {
    super.finalization();

    // Un/Comment this if you have/have not paused the token contract
    ICOToken _token = ICOToken(token);
    if(_token.paused()) {
      _token.unpause();
    }
    _token.transferOwnership(owner);
  }

  function buyTokens(address beneficiary) public payable {
    uint256 minContributionAmount = 1 finney; // 0.001 ETH
    require(msg.value >= minContributionAmount);
    super.buyTokens(beneficiary);
  }

  function getRate() internal constant returns(uint256) {
    // Pre-sale Period
    if (now < (startTime + PRESALE_DURATION)) {
      require(weiRaised <= PRESALE_CAP);
      return PRESALE_RATE;
    }

    // Main sale
    require(now >= MAIN_SALE_START);

    // First Bonus Period
    if (weiRaised <= BONUS_1_CAP) {
        return BONUS_1_RATE;
    }

    // Second Bonus Period
    if (weiRaised <= BONUS_2_CAP) {
        return BONUS_2_RATE;
    }

    // Third Bonus Period
    if (weiRaised <= BONUS_3_CAP) {
        return BONUS_3_RATE;
    }

    // Fourth Bonus Period
    if (weiRaised <= BONUS_4_CAP) {
        return BONUS_4_RATE;
    }

    // Default Period
    return rate;
  }

  function getTokenAmount(uint256 weiAmount) internal constant returns(uint256) {
    uint256 _rate = getRate();
    return weiAmount.mul(_rate);
  }

  function createBountyToken(address beneficiary, uint256 amount) public onlyOwner returns(bool) {
    require(!hasEnded());
    token.mint(beneficiary, amount);
    LogBountyTokenMinted(msg.sender, beneficiary, amount);
    return true;
  }

}
function createTokenContract() internal returns (MintableToken) {
  return ICOToken(tokenAddress);
}

/**
 * Invoked on initialization of the contract
 */