contract BonusCrowdsale is BaseCrowdsale {

  // Constants
  // The following will be populated by main crowdsale contract
  uint32[] public BONUS_TIMES;
  uint32[] public BONUS_TIMES_VALUES;
  uint128[] public BONUS_AMOUNTS;
  uint32[] public BONUS_AMOUNTS_VALUES;

  /**
  * @dev Retrieve length of bonuses by time array
  * @return Bonuses by time array length
  */
  function bonusesForTimesCount() public view returns(uint) {
    return BONUS_TIMES.length;
  }

  /**
  * @dev Sets bonuses for time
  */
  function setBonusesForTimes(uint32[] times, uint32[] values) public onlyOwner {
    require(times.length == values.length);
    for (uint i = 0; i + 1 < times.length; i++) {
      require(times[i] < times[i+1]);
    }

    BONUS_TIMES = times;
    BONUS_TIMES_VALUES = values;
  }

  /**
  * @dev Retrieve length of bonuses by amounts array
  * @return Bonuses by amounts array length
  */
  function bonusesForAmountsCount() public view returns(uint) {
    return BONUS_AMOUNTS.length;
  }

  /**
  * @dev Sets bonuses for USD amounts
  */
  function setBonusesForAmounts(uint128[] amounts, uint32[] values) public onlyOwner {
    require(amounts.length == values.length);
    for (uint i = 0; i + 1 < amounts.length; i++) {
      require(amounts[i] > amounts[i+1]);
    }

    BONUS_AMOUNTS = amounts;
    BONUS_AMOUNTS_VALUES = values;
  }

  /**
  * @notice Overrided getTokenAmount function of parent Crowdsale contract
    to calculate the token with time and amount bonus.
  * @param weiAmount walelt of investor to receive tokens
  */
  function getTokenAmount(uint256 weiAmount) internal view returns(uint256) {
    // Compute time and amount bonus
    uint256 bonus = computeBonus(weiAmount);
    uint256 rateWithBonus = rate.mul(coeff.add(bonus)).div(coeff);
    return weiAmount.mul(rateWithBonus);
  }

  /**
  * @dev Computes overall bonus based on time of contribution and amount of contribution.
  * The total bonus is the sum of bonus by time and bonus by amount
  * @return bonus percentage scaled by 10
  */
  function computeBonus(uint256 weiAmount) public view returns(uint256) {
    return computeAmountBonus(weiAmount).add(computeTimeBonus());
  }

  /**
  * @dev Computes bonus based on time of contribution relative to the beginning of crowdsale
  * @return bonus percentage scaled by 10
  */
  function computeTimeBonus() public view returns(uint256) {
    require(now >= startTime); // solium-disable-line security/no-block-members

    for (uint i = 0; i < BONUS_TIMES.length; i++) {
      if (now <= BONUS_TIMES[i]) { // solium-disable-line security/no-block-members
        return BONUS_TIMES_VALUES[i];
      }
    }

    return 0;
  }

  /**
  * @dev Computes bonus based on amount of contribution
  * @return bonus percentage scaled by 10
  */
  function computeAmountBonus(uint256 weiAmount) public view returns(uint256) {
    for (uint i = 0; i < BONUS_AMOUNTS.length; i++) {
      if (weiAmount >= BONUS_AMOUNTS[i]) {
        return BONUS_AMOUNTS_VALUES[i];
      }
    }

    return 0;
  }

}
/**
 * @dev Parent crowdsale contract with support for time-based and amount based bonuses
 * Based on references from OpenZeppelin: https://github.com/OpenZeppelin/zeppelin-solidity
 *
 */
function computeAmountBonus(uint256 weiAmount) public view returns(uint256) {
  for (uint i = 0; i < BONUS_AMOUNTS.length; i++) {
    if (weiAmount >= BONUS_AMOUNTS[i]) {
      return BONUS_AMOUNTS_VALUES[i];
    }
  }

  return 0;
}

/**
 * @dev Computes bonus based on amount of contribution
 * @return bonus percentage scaled by 10
 */