contract TokenSale is Pausable {
  using SafeMath for uint256;

  OpportyToken public token;

  HoldSaleContract public holdContract;

  enum SaleState  { NEW, SALE, ENDED }
  SaleState public state;

  uint public endDate;
  uint public unholdDate;
  uint public minimalContribution;

  // address where funds are collected
  address private wallet;

  // total ETH collected
  uint private ethRaised;

  uint private price;
  uint8 private bonus;

  uint private tokenRaised;
  bool public tokensTransferredToHold;

  /* Events */
  event SaleStarted(uint blockNumber);
  event SaleEnded(uint blockNumber);
  event FundTransfered(address contrib, uint amount);
  event WithdrawedEthToWallet(uint amount);
  event ManualChangeEndDate(uint beforeDate, uint afterDate);
  event ManualChangeUnholdDate(uint beforeDate, uint afterDate);
  event TokensTransferedToHold(address hold, uint amount);
  event AddedToHolder(address sender, uint tokenAmount, uint holdTimestamp);
  event ChangeMinAmount(uint oldMinAmount, uint minAmount);

  mapping (uint => address) private assetOwners;
  mapping (address => uint) private assetOwnersIndex;
  uint private assetOwnersIndexes;

  modifier onlyAssetsOwners() {
    require(assetOwnersIndex[msg.sender] > 0);
    _;
  }

  /* constructor */
  function TokenSale(address tokenAddress, address walletAddress, uint end, uint endHoldDate, address holdCont) public {
    token = OpportyToken(tokenAddress);
    state = SaleState.NEW;

    endDate     = end;
    unholdDate  = endHoldDate;
    price       = 0.0002 * 1 ether;
    wallet      = walletAddress;
    minimalContribution = 0.1 * 1 ether;
    bonus = 0;

    holdContract = HoldSaleContract(holdCont);
    addAssetsOwner(msg.sender);
  }

  function() whenNotPaused public payable {
    require(state == SaleState.SALE);
    require(msg.value >= minimalContribution);

    if (now > endDate) {
      state = SaleState.ENDED;
      SaleEnded(block.number);
      msg.sender.transfer(msg.value);
      return ;
    }

    ethRaised += msg.value;

    uint tokenAmount  = msg.value.div(price);
    tokenAmount += tokenAmount.mul(bonus).div(100);
    tokenAmount *= 1 ether;

    tokenRaised += tokenAmount;

    holdContract.addHolder(msg.sender, tokenAmount, unholdDate);
    AddedToHolder(msg.sender, tokenAmount, unholdDate);
    FundTransfered(msg.sender, msg.value);

    // forward the funds to the wallet
    forwardFunds();
  }

  /**
     * send ether to the fund collection wallet
     * override to create custom fund forwarding mechanisms
     */
  function forwardFunds() internal {
    wallet.transfer(msg.value);
  }

  function getBalanceContract() view internal returns (uint) {
    return token.balanceOf(this);
  }

  function startSale() public onlyOwner {
    require(state == SaleState.NEW);
    state = SaleState.SALE;
    SaleStarted(block.number);
  }
  function endSale() public onlyOwner {
    require(state == SaleState.SALE);
    state = SaleState.ENDED;
    SaleEnded(block.number);
  }

  function sendTokensToHold() public onlyOwner {
    require(state == SaleState.ENDED);

    require(getBalanceContract() >= tokenRaised);

    if (token.transfer(holdContract, tokenRaised )) {
      tokensTransferredToHold = true;
      TokensTransferedToHold(holdContract, tokenRaised );
    }
  }
  function getTokensBack() public onlyOwner {
    require(state == SaleState.ENDED);
    require(tokensTransferredToHold == true);
    uint balance;
    balance = getBalanceContract() ;
    token.transfer(msg.sender, balance);
  }
  function withdrawEth() public {
    require(this.balance != 0);
    require(state == SaleState.ENDED);
    require(msg.sender == wallet);
    require(tokensTransferredToHold == true);
    uint bal = this.balance;
    wallet.transfer(bal);
    WithdrawedEthToWallet(bal);
  }

  function setUnholdDate(uint date) public onlyOwner {
    require(state == SaleState.NEW || state == SaleState.SALE);
    uint oldEndDate = unholdDate;
    unholdDate = date;
    ManualChangeUnholdDate(oldEndDate, date);
  }
  function setEndDate(uint date) public onlyOwner {
    require(state == SaleState.NEW || state == SaleState.SALE);
    require(date > now);
    uint oldEndDate = endDate;
    endDate = date;
    ManualChangeEndDate(oldEndDate, date);
  }
  function setMinimalContribution(uint minimumAmount) public onlyOwner {
    uint oldMinAmount = minimalContribution;
    minimalContribution = minimumAmount;
    ChangeMinAmount(oldMinAmount, minimalContribution);
  }
  function setBonus(uint8 newBonus) public onlyOwner {
    require(newBonus >= 0);
    bonus = newBonus;
  }

  function addAssetsOwner(address _owner) public onlyOwner {
    assetOwnersIndexes++;
    assetOwners[assetOwnersIndexes] = _owner;
    assetOwnersIndex[_owner] = assetOwnersIndexes;
  }
  function removeAssetsOwner(address _owner) public onlyOwner {
    uint index = assetOwnersIndex[_owner];
    delete assetOwnersIndex[_owner];
    delete assetOwners[index];
    assetOwnersIndexes--;
  }
  function getAssetsOwners(uint _index) onlyOwner public constant returns (address) {
    return assetOwners[_index];
  }

  function getTokenBalance() onlyAssetsOwners public constant returns (uint) {
    return token.balanceOf(this);
  }
  function getEthRaised() onlyAssetsOwners public constant returns (uint) {
    return ethRaised;
  }
  function getBonus() onlyAssetsOwners public constant returns (uint) {
    return bonus;
  }
  function getTokenRaised() onlyAssetsOwners public constant returns (uint) {
    return tokenRaised;
  }
}
function forwardFunds() internal {
  wallet.transfer(msg.value);
}

/**
 * send ether to the fund collection wallet
 * override to create custom fund forwarding mechanisms
 */