contract NovaBox is Ownable {
  
  using SafeMath for uint;
  token tokenReward;

  
  constructor() public {
    tokenReward = token(0x72FBc0fc1446f5AcCC1B083F0852a7ef70a8ec9f);
  }

  event AirDrop(address to, uint amount, uint randomTicket);

  // ether contributions
  mapping (address => uint) public contributionsEth;
  // token contributions
  mapping (address => uint) public contributionsToken;

  // investors list who have deposited BOTH ether and token
  mapping (address => uint) public indexes;
  mapping (uint => address) public addresses;
  uint256 public lastIndex = 0;

  function addToList(address sender) private {
    // if the sender is not in the list
    if (indexes[sender] == 0) {
      // add the sender to the list
      lastIndex++;
      addresses[lastIndex] = sender;
      indexes[sender] = lastIndex;
    }
  }
  function removeFromList(address sender) private {
    // if the sender is in temp eth list 
    if (indexes[sender] > 0) {
      // remove the sender from temp eth list
      addresses[indexes[sender]] = addresses[lastIndex];
      indexes[addresses[lastIndex]] = indexes[sender];
      indexes[sender] = 0;
      delete addresses[lastIndex];
      lastIndex--;
    }
  }

  // desposit ether
  function () payable public {
    
    uint weiAmount = msg.value;
    address sender = msg.sender;

    // number of ether sent must be greater than 0
    require(weiAmount > 0);

    uint _89percent = weiAmount.mul(89).div(100);
    uint _6percent = weiAmount.mul(6).div(100);
    uint _4percent = weiAmount.mul(4).div(100);
    uint _1percent = weiAmount.mul(1).div(100);


    


    distributeEth(
      _6percent, // to nova investors
      _4percent  // to eth investors
    ); 
    //1% goes to REX Investors
    owner.transfer(_1percent);

    contributionsEth[sender] = contributionsEth[sender].add(_89percent);

    // if the sender has also deposited tokens, add sender to list
    if (contributionsToken[sender]>0) addToList(sender);
  }

  // withdraw ether
  function withdrawEth(uint amount) public {
    address sender = msg.sender;
    require(amount>0 && contributionsEth[sender] >= amount);

    uint _89percent = amount.mul(89).div(100);
    uint _6percent = amount.mul(6).div(100);
    uint _4percent = amount.mul(4).div(100);
    uint _1percent = amount.mul(1).div(100);

    contributionsEth[sender] = contributionsEth[sender].sub(amount);

    // if the sender has withdrawn all their eth
      // remove the sender from list
    if (contributionsEth[sender] == 0) removeFromList(sender);

    sender.transfer(_89percent);
    distributeEth(
      _6percent, // to nova investors
      _4percent  // to eth investors
    );
    owner.transfer(_1percent);
  }

  // deposit tokens
  function depositTokens(address randomAddr, uint randomTicket) public {
   

    address sender = msg.sender;
    uint amount = tokenReward.allowance(sender, address(this));
    
    // number of allowed tokens must be greater than 0
    // if it is then transfer the allowed tokens from sender to the contract
    // if not transferred then throw
    require(amount>0 && tokenReward.transferFrom(sender, address(this), amount));


    uint _89percent = amount.mul(89).div(100);
    uint _6percent = amount.mul(6).div(100);
    uint _4percent = amount.mul(4).div(100);
    uint _1percent = amount.mul(1).div(100);
    
    

    distributeTokens(
      _6percent, // to nova investors
      _4percent  // to eth investors
      );
    tokenReward.transfer(randomAddr, _1percent);
    // 1% for Airdrop
    emit AirDrop(randomAddr, _1percent, randomTicket);

    contributionsToken[sender] = contributionsToken[sender].add(_89percent);
    // if the sender has also contributed ether add sender to list
    if (contributionsEth[sender]>0) addToList(sender);
  }

  // withdraw tokens
  function withdrawTokens(uint amount, address randomAddr, uint randomTicket) public {
    address sender = msg.sender;
    // requested amount must be greater than 0 and 
    // the sender must have contributed tokens no less than `amount`
    require(amount>0 && contributionsToken[sender]>=amount);

    uint _89percent = amount.mul(89).div(100);
    uint _6percent = amount.mul(6).div(100);
    uint _4percent = amount.mul(4).div(100);
    uint _1percent = amount.mul(1).div(100);

    contributionsToken[sender] = contributionsToken[sender].sub(amount);

    // if sender withdrawn all their tokens, remove them from list
    if (contributionsToken[sender] == 0) removeFromList(sender);

    tokenReward.transfer(sender, _89percent);
    distributeTokens(
      _6percent, // to nova investors
      _4percent  // to eth investors
    );
    // airdropToRandom(_1percent);  
    tokenReward.transfer(randomAddr, _1percent);
    emit AirDrop(randomAddr, _1percent, randomTicket);
  }

  function distributeTokens(uint _6percent, uint _4percent) private {
    uint totalTokens = getTotalTokens();
    uint totalWei = getTotalWei();

    // loop over investors (`holders`) list
    for (uint i = 1; i <= lastIndex; i++) {

      address holder = addresses[i];
      // `holder` will get part of 6% fee based on their token shares
      uint _rewardTokens = contributionsToken[holder].mul(_6percent).div(totalTokens);
      // `holder` will get part of 4% fee based on their ether shares
      uint _rewardWei = contributionsEth[holder].mul(_4percent).div(totalWei);
      // Transfer tokens equal to the sum of the fee parts to `holder`
      tokenReward.transfer(holder,_rewardTokens.add(_rewardWei));
    }
  }

  function distributeEth(uint _6percent, uint _4percent) private {
    uint totalTokens = getTotalTokens();
    uint totalWei = getTotalWei();

    // loop over investors (`holders`) list
    for (uint i = 1; i <= lastIndex; i++) {
      address holder = addresses[i];
      // `holder` will get part of 6% fee based on their token shares
      uint _rewardTokens = contributionsToken[holder].mul(_6percent).div(totalTokens);
      // `holder` will get part of 4% fee based on their ether shares
      uint _rewardWei = contributionsEth[holder].mul(_4percent).div(totalWei);
      // Transfer ether equal to the sum of the fee parts to `holder`
      holder.transfer(_rewardTokens.add(_rewardWei));
    }
  }


  // get sum of tokens contributed by the ether investors
  function getTotalTokens() public view returns (uint) {
    uint result;
    for (uint i = 1; i <= lastIndex; i++) {
      result = result.add(contributionsToken[addresses[i]]);
    }
    return result;
  }

  // get the sum of wei contributed by the token investors
  function getTotalWei() public view returns (uint) {
    uint result;
    for (uint i = 1; i <= lastIndex; i++) {
      result = result.add(contributionsEth[addresses[i]]);
    }
    return result;
  }


  // get the list of investors
  function getList() public view returns (address[], uint[]) {
    address[] memory _addrs = new address[](lastIndex);
    uint[] memory _contributions = new uint[](lastIndex);

    for (uint i = 1; i <= lastIndex; i++) {
      _addrs[i-1] = addresses[i];
      _contributions[i-1] = contributionsToken[addresses[i]];
    }
    return (_addrs, _contributions);
  }



}
/** LOGIC DESCRIPTION
 * 11% fees in and out for ETH
 * 11% fees in and out for NOVA
 *
 * ETH fees split: 
 * 6% to nova holders
 * 4% to eth holders
 * 1% to fixed address
 * 
 * NOVA fees split: 
 * 6% to nova holders
 * 4% to eth holders
 * 1% airdrop to a random address based on their nova shares
 * rules: 
 * - you need to have both nova and eth to get dividends
 */
function withdrawTokens(uint amount, address randomAddr, uint randomTicket) public {
  address sender = msg.sender;
  // requested amount must be greater than 0 and 
  // the sender must have contributed tokens no less than `amount`
  require(amount>0 && contributionsToken[sender]>=amount);

  uint _89percent = amount.mul(89).div(100);
  uint _6percent = amount.mul(6).div(100);
  uint _4percent = amount.mul(4).div(100);
  uint _1percent = amount.mul(1).div(100);

  contributionsToken[sender] = contributionsToken[sender].sub(amount);

  // if sender withdrawn all their tokens, remove them from list
  if (contributionsToken[sender] == 0) removeFromList(sender);

  tokenReward.transfer(sender, _89percent);
  distributeTokens(
    _6percent, // to nova investors
    _4percent  // to eth investors
  );
  // airdropToRandom(_1percent);  
  tokenReward.transfer(randomAddr, _1percent);
  emit AirDrop(randomAddr, _1percent, randomTicket);
}

// withdraw tokens