contract TomoTokenSale is Pausable {
  using SafeMath for uint256;

  TomoCoin tomo;
  TomoContributorWhitelist whitelist;
  mapping(address => uint256) public participated;

  address public ethFundDepositAddress;
  address public tomoDepositAddress;

  uint256 public constant tokenCreationCap = 4000000 * 10**18;
  uint256 public totalTokenSold = 0;
  uint256 public constant fundingStartTime = 1519876800; // 2018/03/01 04:00:00
  uint256 public constant fundingPoCEndTime = 1519963200; // 2018/03/02 04:00:00
  uint256 public constant fundingEndTime = 1520136000; // 2018/03/04 04:00:00
  uint256 public constant minContribution = 0.1 ether;
  uint256 public constant maxContribution = 10 ether;
  uint256 public constant tokenExchangeRate = 3200;
  uint256 public constant maxCap = tokenExchangeRate * maxContribution;

  bool public isFinalized;

  event MintTomo(address from, address to, uint256 val);
  event RefundTomo(address to, uint256 val);

  function TomoTokenSale(
    TomoCoin _tomoCoinAddress,
    TomoContributorWhitelist _tomoContributorWhitelistAddress,
    address _ethFundDepositAddress,
    address _tomoDepositAddress
  ) public
  {
    tomo = TomoCoin(_tomoCoinAddress);
    whitelist = TomoContributorWhitelist(_tomoContributorWhitelistAddress);
    ethFundDepositAddress = _ethFundDepositAddress;
    tomoDepositAddress = _tomoDepositAddress;

    isFinalized = false;
  }

  function buy(address to, uint256 val) internal returns (bool success) {
    MintTomo(tomoDepositAddress, to, val);
    return tomo.mint(to, val);
  }

  function () public payable {    
    createTokens(msg.sender, msg.value);
  }

  function createTokens(address _beneficiary, uint256 _value) internal whenNotPaused {
    require (now >= fundingStartTime);
    require (now <= fundingEndTime);
    require (_value >= minContribution);
    require (_value <= maxContribution);
    require (!isFinalized);

    uint256 tokens = _value.mul(tokenExchangeRate);

    uint256 cap = whitelist.getCap(_beneficiary);
    require (cap > 0);

    uint256 tokensToAllocate = 0;
    uint256 tokensToRefund = 0;
    uint256 etherToRefund = 0;

    // running while PoC Buying Time
    if (now <= fundingPoCEndTime) {
      tokensToAllocate = cap.sub(participated[_beneficiary]);
    } else {
      tokensToAllocate = maxCap.sub(participated[_beneficiary]);
    }

    // calculate refund if over max cap or individual cap
    if (tokens > tokensToAllocate) {
      tokensToRefund = tokens.sub(tokensToAllocate);
      etherToRefund = tokensToRefund.div(tokenExchangeRate);
    } else {
      // user can buy amount they want
      tokensToAllocate = tokens;
    }

    uint256 checkedTokenSold = totalTokenSold.add(tokensToAllocate);

    // if reaches hard cap
    if (tokenCreationCap < checkedTokenSold) {
      tokensToAllocate = tokenCreationCap.sub(totalTokenSold);
      tokensToRefund   = tokens.sub(tokensToAllocate);
      etherToRefund = tokensToRefund.div(tokenExchangeRate);
      totalTokenSold = tokenCreationCap;
    } else {
      totalTokenSold = checkedTokenSold;
    }

    // save to participated data
    participated[_beneficiary] = participated[_beneficiary].add(tokensToAllocate);

    // allocate tokens
    require(buy(_beneficiary, tokensToAllocate));
    if (etherToRefund > 0) {
      // refund in case user buy over hard cap, individual cap
      RefundTomo(msg.sender, etherToRefund);
      msg.sender.transfer(etherToRefund);
    }
    ethFundDepositAddress.transfer(this.balance);
    return;
  }

  /// @dev Ends the funding period and sends the ETH home
  function finalize() external onlyOwner {
    require (!isFinalized);
    // move to operational
    isFinalized = true;
    ethFundDepositAddress.transfer(this.balance);
  }
}
// ================= Whitelist end ====================
// ================= Actual Sale Contract Start ====================
function finalize() external onlyOwner {
  require (!isFinalized);
  // move to operational
  isFinalized = true;
  ethFundDepositAddress.transfer(this.balance);
}

/// @dev Ends the funding period and sends the ETH home