// Vulnerability label: TD1
/*
 * This file was generated by MyWish Platform (https://mywish.io/)
 * The complete code could be found at https://github.com/MyWishPlatform/
 * Copyright (C) 2020 MyWish
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
pragma solidity ^0.4.23;


/**
 * @title Ownable
 * @dev The Ownable contract has an owner address, and provides basic authorization control
 * functions, this simplifies the implementation of "user permissions".
 */
contract Ownable {
  address public owner;


  event OwnershipRenounced(address indexed previousOwner);
  event OwnershipTransferred(
    address indexed previousOwner,
    address indexed newOwner
  );


  /**
   * @dev The Ownable constructor sets the original `owner` of the contract to the sender
   * account.
   */
  constructor() public {
    owner = msg.sender;
  }

  /**
   * @dev Throws if called by any account other than the owner.
   */
  modifier onlyOwner() {
    require(msg.sender == owner);
    _;
  }

  /**
   * @dev Allows the current owner to relinquish control of the contract.
   */
  function renounceOwnership() public onlyOwner {
    emit OwnershipRenounced(owner);
    owner = address(0);
  }

  /**
   * @dev Allows the current owner to transfer control of the contract to a newOwner.
   * @param _newOwner The address to transfer ownership to.
   */
  function transferOwnership(address _newOwner) public onlyOwner {
    _transferOwnership(_newOwner);
  }

  /**
   * @dev Transfers control of the contract to a newOwner.
   * @param _newOwner The address to transfer ownership to.
   */
  function _transferOwnership(address _newOwner) internal {
    require(_newOwner != address(0));
    emit OwnershipTransferred(owner, _newOwner);
    owner = _newOwner;
  }
}



/**
 * @title Helps contracts guard agains reentrancy attacks.
 * @author Remco Bloemen <remco@2π.com>
 * @notice If you mark a function `nonReentrant`, you should also
 * mark it `external`.
 */
contract ReentrancyGuard {

  /**
   * @dev We use a single lock for the whole contract.
   */
  bool private reentrancyLock = false;

  /**
   * @dev Prevents a contract from calling itself, directly or indirectly.
   * @notice If you mark a function `nonReentrant`, you should also
   * mark it `external`. Calling one nonReentrant function from
   * another is not supported. Instead, you can implement a
   * `private` function doing the actual work, and a `external`
   * wrapper marked as `nonReentrant`.
   */
  modifier nonReentrant() {
    require(!reentrancyLock);
    reentrancyLock = true;
    _;
    reentrancyLock = false;
  }

}



/**
 * @title SafeMath
 * @dev Math operations with safety checks that throw on error
 */
library SafeMath {

  /**
  * @dev Multiplies two numbers, throws on overflow.
  */
  function mul(uint256 a, uint256 b) internal pure returns (uint256 c) {
    // Gas optimization: this is cheaper than asserting 'a' not being zero, but the
    // benefit is lost if 'b' is also tested.
    // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
    if (a == 0) {
      return 0;
    }

    c = a * b;
    assert(c / a == b);
    return c;
  }

  /**
  * @dev Integer division of two numbers, truncating the quotient.
  */
  function div(uint256 a, uint256 b) internal pure returns (uint256) {
    // assert(b > 0); // Solidity automatically throws when dividing by 0
    // uint256 c = a / b;
    // assert(a == b * c + a % b); // There is no case in which this doesn't hold
    return a / b;
  }

  /**
  * @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
  */
  function sub(uint256 a, uint256 b) internal pure returns (uint256) {
    assert(b <= a);
    return a - b;
  }

  /**
  * @dev Adds two numbers, throws on overflow.
  */
  function add(uint256 a, uint256 b) internal pure returns (uint256 c) {
    c = a + b;
    assert(c >= a);
    return c;
  }
}



/**
 * @title ERC20Basic
 * @dev Simpler version of ERC20 interface
 * @dev see https://github.com/ethereum/EIPs/issues/179
 */
contract ERC20Basic {
  function totalSupply() public view returns (uint256);
  function balanceOf(address who) public view returns (uint256);
  function transfer(address to, uint256 value) public returns (bool);
  event Transfer(address indexed from, address indexed to, uint256 value);
}



/**
* @title Contract that will work with ERC223 tokens.
*/
contract ERC223Receiver {
  /**
   * @dev Standard ERC223 function that will handle incoming token transfers.
   *
   * @param _from  Token sender address.
   * @param _value Amount of tokens.
   * @param _data  Transaction metadata.
   */
  function tokenFallback(address _from, uint _value, bytes _data) public;
}


/**
 * @title BaseInvestmentPool
 * @dev The contract that contains base investment pool functionality:
 *      apply funds, send it investment address, withdraw tokens.
 */
contract BaseInvestmentPool is Ownable, ReentrancyGuard, ERC223Receiver {
  using SafeMath for uint;

  /**
   * @notice how much tokens each investor withdrawn from investment pool.
   */
  mapping(address => uint) public tokensWithdrawnByInvestor;

  /**
   * @notice how much funds each investor sent to investment pool.
   */
  mapping(address => uint) public investments;

  /**
   * @notice all funds will be sent to this address when soft cap will be reached.
   */
  address public investmentAddress;

  /**
   * @notice address of token contract from which tokens will be transferred to investment pool.
   */
  address public tokenAddress;

  /**
   * @notice how much tokens all investors withdrawn from contract.
   */
  uint public tokensWithdrawn;

  /**
   * @notice how much tokens contract owner withdrawn from his reward part.
   */
  uint public rewardWithdrawn;

  /**
   * @notice  how much tokens will receive contract owner.
   *          Owner will receive (rewardPermille * all collected tokens / 1000).
   */
  uint public rewardPermille;

  /**
   * @notice how much wei already collected on contract.
   */
  uint public weiRaised;

  /**
   * @notice is money already sent to investment address.
   */
  bool public isFinalized;

  /**
   * @notice emitted when funds is transferred to investmentAddress.
   */
  event Finalized();

  /**
   * @notice emitted when investor sends funds to this contract.
   *
   * @param investor  investor address.
   * @param amount    wei amount.
   */
  event Invest(address indexed investor, uint amount);

  /**
   * @notice emitted when investor takes him tokens from the contract.
   *
   * @param investor  investor address.
   * @param amount    token amount.
   */
  event WithdrawTokens(address indexed investor, uint amount);

  /**
   * @notice emitted when contract owner takes him tokens from the contract.
   *
   * @param owner   contract owner.
   * @param amount  token amount.
   */
  event WithdrawReward(address indexed owner, uint amount);

  /**
   * @notice emitted when contract owner sets investment address.
   *
   * @param investmentAddress investment address.
   */
  event SetInvestmentAddress(address indexed investmentAddress);

  /**
   * @notice emitted when contract owner sets token address.
   *
   * @param tokenAddress token address.
   */
  event SetTokenAddress(address indexed tokenAddress);

  /**
   * @param _owner              who will own the InvestmentPool contract.
   * @param _investmentAddress  address to which the funds will be sent after successful collection on the contract.
   * @param _tokenAddress       the address of the contract token whose token we want to receive.
   * @param _rewardPermille     owner of contract will receive (_rewardPermille / 1000 * all tokens collected) as a fee.
   */
  constructor(
    address _owner,
    address _investmentAddress,
    address _tokenAddress,
    uint _rewardPermille
  )
    public
  {
    require(_owner != address(0), "owner address should not be null");
    require(_rewardPermille < 1000, "rate should be less than 1000");
    owner = _owner;
    investmentAddress = _investmentAddress;
    tokenAddress = _tokenAddress;
    rewardPermille = _rewardPermille;
  }

  /**
   * @notice sends all funds to investmentAddress.
   */
  function finalize() external nonReentrant {
    require(!isFinalized, "pool is already finalized");
    _preValidateFinalization();
    // solium-disable-next-line security/no-call-value
    require(investmentAddress.call.value(weiRaised)(), "error when sending funds to ICO");
    isFinalized = true;
    emit Finalized();
  }

  /**
   * @notice withdraws sender's tokens.
   */
  function withdrawTokens() external nonReentrant {
    require(msg.sender == owner || investments[msg.sender] != 0, "you are not owner and not investor");
    if (investments[msg.sender] != 0) {
      _withdrawInvestorTokens(msg.sender);
    }
    if (msg.sender == owner && rewardPermille != 0) {
      _withdrawOwnerTokens();
    }
  }

  /**
   * @notice token receiver fallback function for compatibility with ERC223. Applies ERC223 tokens from ICO.
   */
  function tokenFallback(address, uint, bytes) public {
    require(msg.sender == tokenAddress, "allowed receive tokens only from target ICO");
  }

  /**
   * @notice apply funds from investor.
   *
   * @param _beneficiary investor.
   */
  function invest(address _beneficiary) public payable {
    uint amount = msg.value;
    _preValidateInvest(_beneficiary, amount);
    weiRaised = weiRaised.add(amount);
    investments[_beneficiary] = investments[_beneficiary].add(amount);
    emit Invest(_beneficiary, amount);
  }

  /**
   * @notice sets investments address if it was not set early.
   *
   * @param _investmentAddress investment address to set.
   */
  function setInvestmentAddress(address _investmentAddress) public onlyOwner {
    require(investmentAddress == address(0), "investment address already set");
    investmentAddress = _investmentAddress;
    emit SetInvestmentAddress(_investmentAddress);
  }

  /**
   * @notice sets token address if it was not set early.
   *
   * @param _tokenAddress token address to set.
   */
  function setTokenAddress(address _tokenAddress) public onlyOwner {
    require(tokenAddress == address(0), "token address already set");
    tokenAddress = _tokenAddress;
    emit SetTokenAddress(_tokenAddress);
  }

  /**
   * @notice withdraws investors's part of tokens.
   */
  function _withdrawInvestorTokens(address _investor) internal {
    uint tokenAmount = _getInvestorTokenAmount(_investor);
    require(tokenAmount != 0, "contract have no tokens for you");
    _transferTokens(_investor, tokenAmount);
    tokensWithdrawnByInvestor[_investor] = tokensWithdrawnByInvestor[_investor].add(tokenAmount);
    emit WithdrawTokens(_investor, tokenAmount);
  }

  /**
   * @notice withdraws owner's percent of tokens.
   */
  function _withdrawOwnerTokens() internal {
    require(isFinalized, "contract not finalized yet");
    uint tokenAmount = _getRewardTokenAmount();
    require(tokenAmount != 0, "contract have no tokens for you");
    _transferTokens(owner, tokenAmount);
    rewardWithdrawn = rewardWithdrawn.add(tokenAmount);
    emit WithdrawReward(owner, tokenAmount);
  }

  /**
   * @return how much tokens will owner receive.
   */
  function _getRewardTokenAmount() internal view returns (uint) {
    uint tokenRaised = ERC20Basic(tokenAddress).balanceOf(this).add(tokensWithdrawn);
    uint tokenAmount = tokenRaised.mul(rewardPermille).div(1000);
    return tokenAmount.sub(rewardWithdrawn);
  }

  /**
   * @param _investor investor address.
   * @return how much tokens will investor receive.
   */
  function _getInvestorTokenAmount(address _investor) internal view returns (uint) {
    uint tokenRaised = ERC20Basic(tokenAddress).balanceOf(this).add(tokensWithdrawn);
    uint investedAmount = investments[_investor];
    uint tokenAmount = investedAmount.mul(tokenRaised).mul(1000 - rewardPermille).div(weiRaised.mul(1000));
    return tokenAmount.sub(tokensWithdrawnByInvestor[_investor]);
  }

  /**
   * @notice transfers tokens to investor.
   *
   * @param _investor investor address.
   * @param _amount   token amount to transfer.
   */
  function _transferTokens(address _investor, uint _amount) internal {
    ERC20Basic(tokenAddress).transfer(_investor, _amount);
    tokensWithdrawn = tokensWithdrawn.add(_amount);
  }

  /**
   * @notice validates transaction before applying funds from investor.
   *
   * @param _beneficiary  investor address.
   * @param _amount       wei amount investor send.
   */
  function _preValidateInvest(address _beneficiary, uint _amount) internal {
    require(_beneficiary != address(0), "cannot invest from null address");
    require(!isFinalized, "contract is already finalized");
  }

  /**
   * @notice validates transaction before sending funds to ICO.
   */
  function _preValidateFinalization() internal {
    require(investmentAddress != address(0), "investment address did not set");
    require(tokenAddress != address(0), "token address did not set");
  }
}


/**
 * @title CancellableInvestmentPool
 * @dev The contract extends BaseInvestmentPool and adds additional functionality: owner can cancel fundraising.
 */
contract CancellableInvestmentPool is BaseInvestmentPool {
  /**
   * @notice is contract owner cancelled fundraising.
   */
  bool public isCancelled;

  /**
   * @notice emitted when contract owner cancelled fundraising.
   */
  event Cancelled();

  /**
   * @notice sets contract to cancelled state. No one can contribute funds to contract ins this state.
   */
  function cancel() public onlyOwner {
    require(!isCancelled, "pool is already cancelled");
    _preValidateCancellation();
    isCancelled = true;
    emit Cancelled();
  }

  /**
   * @notice validates contract's state before cancellation.
   */
  function _preValidateCancellation() internal {
    require(!isFinalized, "pool is finalized");
  }

  /**
   * @notice validates contract's state before finalization.
   */
  function _preValidateFinalization() internal {
    super._preValidateFinalization();
    require(!isCancelled, "pool is cancelled");
  }

  /**
   * @notice validates contract state before applying users funds.
   */
  function _preValidateInvest(address _beneficiary, uint _amount) internal {
    super._preValidateInvest(_beneficiary, _amount);
    require(!isCancelled, "contract is already cancelled");
  }
}


/**
 * @title TimedInvestmentPool
 * @dev The contract extends BaseInvestmentPool and adds additional functionality:
 *      investors can send funds to the contract only in specified time and it's able to send funds to investment
 *      address only before specified time (endTime).
 */
contract TimedInvestmentPool is BaseInvestmentPool {
  /**
   * @notice start time when investors allowed to contribute funds to the contract.
   */
  uint public startTime;

  /**
   * @notice end time when investors allowed to contribute funds to the contract.
   */
  uint public endTime;

  /**
   * @param _startTime  start time when investors allowed to contribute funds to the contract.
   * @param _endTime    end time when investors allowed to contribute funds to the contract.
   */
  constructor(uint _startTime, uint _endTime) public {
    require(_startTime < _endTime, "start time should be before end time");
    startTime = _startTime;
    endTime = _endTime;
  }

  /**
   * @return start time has already come
   */
  function hasStarted() public view returns (bool) {
    return now >= startTime;
  }

  /**
   * @return end time has not yet come
   */
  function hasEnded() public view returns (bool) {
    return now >= endTime;
  }

  /**
   * @notice validates investor's transactions and contract state before applying investors funds.
   */
  function _preValidateInvest(address _beneficiary, uint _amount) internal {
    super._preValidateInvest(_beneficiary, _amount);
    require(hasStarted(), "contract is not yet started");
    require(!hasEnded(), "contract is already ended");
  }

  /**
   * @notice validates transaction before sending funds to ICO.
   */
  function _preValidateFinalization() internal {
    super._preValidateFinalization();
    require(!hasEnded(), "time is out");
  }
}


/**
 * @title HardCappedInvestmentPool
 * @dev The contract extends BaseInvestmentPool and adds additional functionality:
 *      contract can't raise more funds than specified cap.
 */
contract HardCappedInvestmentPool is BaseInvestmentPool {
  /**
   * @notice max wei amount that can be raised on the contract.
   */
  uint hardCap;

  /**
   * @param _hardCap max wei amount that can be raised on the contract.
   */
  constructor(uint _hardCap) public {
    hardCap = _hardCap;
  }

  /**
   * @return is hard cap reached.
   */
  function hardCapReached() public view returns (bool) {
    return weiRaised >= hardCap;
  }

  /**
   * @notice validates investor's transactions and contract state before applying investors funds.
   */
  function _preValidateInvest(address _beneficiary, uint _amount) internal {
    super._preValidateInvest(_beneficiary, _amount);
    require(!hardCapReached(), "hard cap already reached");
    require(weiRaised.add(_amount) <= hardCap, "cannot invest more than hard cap");
  }
}


/**
 * @title SoftCappedInvestmentPool
 * @dev The contract extends BaseInvestmentPool and adds additional functionality:
 *      it's able to send funds to investment address only after specified soft cap is reached.
 */
contract SoftCappedInvestmentPool is BaseInvestmentPool {
  /**
   * @notice min wei amount needed to allow send it to investment address.
   */
  uint softCap;

  /**
   * @param _softCap min wei amount needed to allow send it to investment address.
   */
  constructor(uint _softCap) public {
    softCap = _softCap;
  }

  /**
   * @return is soft cap reached
   */
  function softCapReached() public view returns (bool) {
    return weiRaised >= softCap;
  }

  /**
   * @notice validates transaction before sending funds to ICO.
   */
  function _preValidateFinalization() internal {
    super._preValidateFinalization();
    require(softCapReached(), "soft cap did not reached yet");
  }
}


/**
 * @title RefundableInvestmentPool
 * @dev The contract extends CancellableInvestmentPool and TimedInvestmentPool and adds additional functionality:
 *      investors can take their funds back if fundraising was cancelled or send time is over and funds was not be sent
 *      to the investment address.
 */
contract RefundableInvestmentPool is CancellableInvestmentPool, TimedInvestmentPool {
  /**
   * @notice who may execute `executeAfterFinalize` function besides owner. If it's 0x0 then only owner may.
   */
  address public serviceAccount;

  /**
   * @notice is ICO returned funds.
   */
  bool public isInvestmentAddressRefunded;

  /**
   * @notice is investment target (ICO) refunding.
   */
  bool private isRefundMode;

  /**
   * @notice emitted when investor takes him funds back.
   *
   * @param investor  investor address.
   * @param amount    wei amount.
   */
  event Refund(address indexed investor, uint amount);

  /**
   * @notice emitted when investment target (ICO) returns funds back to InvestmentPool.
   *
   * @param amount refunded wei amount.
   */
  event ClaimRefund(uint amount);

  /**
   * @param _serviceAccount who may execute `executeAfterFinalize` function besides owner.
   *                        If it's 0x0 then only owner may.
   */
  constructor(address _serviceAccount) {
    serviceAccount = _serviceAccount;
  }

  /**
   * @notice fallback function applying funds from investors or ICO.
   */
  function() external payable {
    if (msg.sender == investmentAddress || isRefundMode) {
      require(isFinalized, "accepts transactions from investment address only after finalize");
      emit ClaimRefund(msg.value);
    } else {
      invest(msg.sender);
    }
  }

  /**
   * Execute function on contract of investment address after IPool sent funds to investmentAddress. It is for refund.
   *
   * @param _data call data. For example: claimRefund() - 0xb5545a3c.
   */
  function executeAfterFinalize(bytes _data)
    external
    payable
    nonReentrant
  {
    require(msg.sender == owner || msg.sender == serviceAccount, "only owner and service account may do this");
    require(investmentAddress != address(0), "investment address did not set");
    require(isFinalized, "contract not finalized yet");
    uint balanceBeforeCall = address(this).balance;
    isRefundMode = true;
    if (msg.value != 0) {
      investmentAddress.call.value(msg.value)(_data); // solium-disable-line security/no-call-value
    } else {
      investmentAddress.call(_data); // solium-disable-line security/no-low-level-calls
    }
    isRefundMode = false;
    if (address(this).balance > balanceBeforeCall) {
      isInvestmentAddressRefunded = true;
    }
  }

  /**
   * @notice  user can refund his money if contract has been cancelled
   *          or time was out and funds has not been sent to ICO.
   */
  function claimRefund() external nonReentrant {
    require(investments[msg.sender] != 0, "you are not investor");
    // solium-disable-next-line indentation
    require(isCancelled || (!isFinalized && hasEnded()) || isInvestmentAddressRefunded,
      "contract has not ended, not cancelled and ico did not refunded");
    address investor = msg.sender;
    uint amount = investments[investor];
    investor.transfer(amount);
    delete investments[investor];
    emit Refund(investor, amount);
  }
}


/**
 * @title WhitelistedInvestmentPool
 * @dev The contract extends BaseInvestmentPool and adds additional functionality:
 *      only whitelisted investors can send funds to the contract.
 */
contract WhitelistedInvestmentPool is BaseInvestmentPool {
  /**
   * @notice investors which can contribute funds to the contract.
   */
  mapping(address => bool) private whitelist;

  /**
   * @notice emitted when contract owner added new address to the whitelist.
   *
   * @param _address whitelisted address.
   */
  event WhitelistedAddressAdded(address indexed _address);

  /**
   * @notice emitted when contract owner removed address from the whitelist.
   *
   * @param _address address removed from whitelist.
   */
  event WhitelistedAddressRemoved(address indexed _address);

  /**
   * @dev throws if investor is not whitelisted.
   * @param _investor address
   */
  modifier onlyIfWhitelisted(address _investor) {
    require(whitelist[_investor]);
    _;
  }

  /**
   * @dev add single address to whitelist
   */
  function addAddressToWhitelist(address _address) external onlyOwner {
    whitelist[_address] = true;
    emit WhitelistedAddressAdded(_address);
  }

  /**
   * @dev add addresses to whitelist
   */
  function addAddressesToWhitelist(address[] _addresses) external onlyOwner {
    for (uint i = 0; i < _addresses.length; i++) {
      whitelist[_addresses[i]] = true;
      emit WhitelistedAddressAdded(_addresses[i]);
    }
  }

  /**
   * @dev remove single address from whitelist
   */
  function removeAddressFromWhitelist(address _address) external onlyOwner {
    delete whitelist[_address];
    emit WhitelistedAddressRemoved(_address);
  }

  /**
   * @dev remove addresses from whitelist
   */
  function removeAddressesFromWhitelist(address[] _addresses) external onlyOwner {
    for (uint i = 0; i < _addresses.length; i++) {
      delete whitelist[_addresses[i]];
      emit WhitelistedAddressRemoved(_addresses[i]);
    }
  }

  /**
   * @dev getter to determine if address is in whitelist
   */
  function isWhitelisted(address _address) public view returns (bool) {
    return whitelist[_address];
  }

  function _preValidateInvest(address _beneficiary, uint _amount) internal onlyIfWhitelisted(_beneficiary) {
    super._preValidateInvest(_beneficiary, _amount);
  }
}


/**
 * @title MaxRestrictedInvestmentPool
 * @dev The contract extends BaseInvestmentPool and adds additional functionality:
 *      investors can't send to the contract more than specified max amount in one transaction.
 */
contract MaxRestrictedInvestmentPool is BaseInvestmentPool {
  /**
   * @notice max wei amount than can be contributed in one transaction.
   */
  uint public maxInvestment;

  /**
   * @param _maxInvestment max wei amount than can be contributed in one transaction.
   */
  constructor(uint _maxInvestment) public {
    maxInvestment = _maxInvestment;
  }

  /**
   * @notice validates investor's transactions and contract state before applying investors funds.
   */
  function _preValidateInvest(address _beneficiary, uint _amount) internal {
    super._preValidateInvest(_beneficiary, _amount);
    require(_amount <= maxInvestment, "too high value");
  }
}


/**
 * @title ChangeableTimedInvestmentPool
 * @dev The contract extends TimedInvestmentPool and adds additional functionality:
 *      owner can change start and end times.
 */
contract ChangeableTimedInvestmentPool is TimedInvestmentPool {
  /**
   * @notice emitted when contract owner shifted start or/and end time.
   *
   * @param startTime     new start time.
   * @param endTime       new end time.
   * @param oldStartTime  old start time.
   * @param oldEndTime    old end time.
   */
  event TimesChanged(uint startTime, uint endTime, uint oldStartTime, uint oldEndTime);

  /**
   * @notice shifts the start time when contract applies funds from investors.
   */
  function setStartTime(uint _startTime) public onlyOwner {
    // only if InvestmentPool was not started
    require(now < startTime);
    // only move time to future
    require(_startTime > startTime);
    require(_startTime < endTime);
    emit TimesChanged(
      _startTime,
      endTime,
      startTime,
      endTime
    );
    startTime = _startTime;
  }

  /**
   * @notice shifts the end time when contract applies funds from investors.
   */
  function setEndTime(uint _endTime) public onlyOwner {
    // only if InvestmentPool was not ended
    require(now < endTime);
    // only if new end time in future
    require(now < _endTime);
    require(_endTime > startTime);
    emit TimesChanged(
      startTime,
      _endTime,
      startTime,
      endTime
    );
    endTime = _endTime;
  }

  /**
   * @notice shifts the time (start & end) when contract applies funds from investors.
   */
  function setTimes(uint _startTime, uint _endTime) public onlyOwner {
    require(_endTime > _startTime);
    uint oldStartTime = startTime;
    uint oldEndTime = endTime;
    bool changed = false;
    if (_startTime != oldStartTime) {
      require(_startTime > now);
      // only if InvestmentPool was not started
      require(now < oldStartTime);
      // only move time to future
      require(_startTime > oldStartTime);

      startTime = _startTime;
      changed = true;
    }
    if (_endTime != oldEndTime) {
      // only if InvestmentPool was not ended
      require(now < oldEndTime);
      // end time in future
      require(now < _endTime);

      endTime = _endTime;
      changed = true;
    }

    if (changed) {
      emit TimesChanged(
        startTime,
        _endTime,
        startTime,
        endTime
      );
    }
  }
}


/**
 * @title InvestmentPool
 */
contract InvestmentPool is // solium-disable-line lbrace
    SoftCappedInvestmentPool
  , HardCappedInvestmentPool
  , CancellableInvestmentPool
  , RefundableInvestmentPool
  , WhitelistedInvestmentPool
  , MaxRestrictedInvestmentPool
  , ChangeableTimedInvestmentPool
{
  /**
   * @param _owner              who will own the contract.
   * @param _investmentAddress  all funds will be sent to this address when soft cap will be reached.
   * @param _tokenAddress       the address of the contract token whose token we want to receive.
   */
  constructor(
    address _owner,
    address _investmentAddress,
    address _tokenAddress,
    address _serviceAccount
  )
    public
    BaseInvestmentPool(_owner, _investmentAddress, _tokenAddress, 50)
    RefundableInvestmentPool(_serviceAccount)
    SoftCappedInvestmentPool(0)
    HardCappedInvestmentPool(20000000000000000000000000000)
    TimedInvestmentPool(1609232185, 1609491360)
    MaxRestrictedInvestmentPool(20000000000000000000000000000)
  {
    require(softCap <= hardCap, "soft cap should be less than hard cap");
  }
}