contract CCXSale is WhitelistDaonomicCrowdsale, RefundableDaonomicCrowdsale {

  event UsdEthRateChange(uint256 rate);
  event Withdraw(address to, uint256 value);

  uint256 constant public SOFT_CAP = 50000000 * 10 ** 18;
  uint256 constant public HARD_CAP = 225000000 * 10 ** 18;
  uint256 constant public MINIMAL_CCX = 1000 * 10 ** 18;
  uint256 constant public START = 1539820800; // 18 oct 2018 00:00:00
  uint256 constant public END = 1549152000; // 3 feb 2019 00:00:00

  CCXToken public token;
  uint256 public sold;
  uint256 public rate;
  address public operator;

  constructor(CCXToken _token, Whitelist _whitelist, uint256 _usdEthRate, address _operator)
  WhitelistDaonomicCrowdsale(_whitelist) public {
    token = _token;
    operator = _operator;
    setUsdEthRate(_usdEthRate);
    //needed for Daonomic UI
    emit RateAdd(address(0));
  }

  function _preValidatePurchase(
    address _beneficiary,
    uint256 _weiAmount,
    uint256 _tokens,
    uint256 _bonus
  ) internal {
    super._preValidatePurchase(_beneficiary, _weiAmount, _tokens, _bonus);
    require(now >= START);
    require(now < END);
    require(_tokens.add(_bonus) > MINIMAL_CCX);
  }

  function setUsdEthRate(uint256 _usdEthRate) onlyOperatorOrOwner public {
    rate = _usdEthRate.mul(100).div(9);
    emit UsdEthRateChange(_usdEthRate);
  }

  modifier onlyOperatorOrOwner() {
    require(msg.sender == operator || msg.sender == owner);
    _;
  }

  function withdrawEth(address _to, uint256 _value) onlyOwner public {
    _to.transfer(_value);
    emit Withdraw(_to, _value);
  }

  function setOperator(address _operator) onlyOwner public {
    operator = _operator;
  }

  function pauseToken() onlyOwner public {
    token.pause();
  }

  function unpauseToken() onlyOwner public {
    token.unpause();
  }

  function _deliverTokens(
    address _beneficiary,
    uint256 _tokenAmount
  ) internal {
    token.mint(_beneficiary, _tokenAmount);
  }

  function _getTokenAmount(uint256 _weiAmount) internal view returns (uint256 tokens, uint256 weiLeft) {
    tokens = _weiAmount.mul(rate);
    if (sold.add(tokens) > HARD_CAP) {
      tokens = HARD_CAP.sub(sold);
      //alternative to Math.ceil(tokens / rate)
      uint256 weiSpent = (tokens.add(rate).sub(1)).div(rate);
      weiLeft =_weiAmount.sub(weiSpent);
    } else {
      weiLeft = 0;
    }
  }

  function _getBonus(uint256 _tokens) internal view returns (uint256) {
    uint256 possibleBonus = getTimeBonus(_tokens) + getAmountBonus(_tokens);
    if (sold.add(_tokens).add(possibleBonus) > HARD_CAP) {
      return HARD_CAP.sub(sold).sub(_tokens);
    } else {
      return possibleBonus;
    }
  }

  function getTimeBonus(uint256 _tokens) public view returns (uint256) {
    if (now < 1542931200) { //23 nov 2018 00:00:00
      return _tokens.mul(15).div(100);
    } else if (now < 1546041600) { // 29 dec 2018 00:00:00
      return _tokens.mul(7).div(100);
    } else {
      return 0;
    }
  }

  function getAmountBonus(uint256 _tokens) public pure returns (uint256) {
    if (_tokens < 10000 * 10 ** 18) {
      return 0;
    } else if (_tokens < 100000 * 10 ** 18) {
      return _tokens.mul(3).div(100);
    } else if (_tokens < 1000000 * 10 ** 18) {
      return _tokens.mul(5).div(100);
    } else if (_tokens < 10000000 * 10 ** 18) {
      return _tokens.mul(7).div(100);
    } else {
      return _tokens.mul(10).div(100);
    }
  }

  function _updatePurchasingState(
    address _beneficiary,
    uint256 _weiAmount,
    uint256 _tokens
  ) internal {
    super._updatePurchasingState(_beneficiary, _weiAmount, _tokens);

    sold = sold.add(_tokens);
  }

  function isRefundable() public view returns (bool) {
    return now > END && sold < SOFT_CAP;
  }

  /**
   * @dev function for Daonomic UI
   */
  function getRate(address _token) public view returns (uint256) {
    if (_token == address(0)) {
      return rate * 10 ** 18;
    } else {
      return 0;
    }
  }

  /**
   * @dev function for Daonomic UI
   */
  function start() public pure returns (uint256) {
    return START;
  }

  /**
   * @dev function for Daonomic UI
   */
  function end() public pure returns (uint256) {
    return END;
  }

}
function start() public pure returns (uint256) {
  return START;
}

/**
 * @dev function for Daonomic UI
 */