contract HoldSaleContract is Ownable {
  using SafeMath for uint256;
  // Addresses and contracts
  OpportyToken public OppToken;

  struct Holder {
    bool isActive;
    uint tokens;
    uint holdPeriodTimestamp;
    bool withdrawed;
  }

  mapping(address => Holder) public holderList;
  mapping(uint => address) private holderIndexes;

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

  uint private holderIndex;
  uint private holderWithdrawIndex;

  uint private tokenAddHold;
  uint private tokenWithdrawHold;

  event TokensTransfered(address contributor , uint amount);
  event Hold(address sender, address contributor, uint amount, uint holdPeriod);

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

  /* constructor */
  function HoldSaleContract(address _OppToken) public {
    OppToken = OpportyToken(_OppToken);
    addAssetsOwner(msg.sender);
  }

  function addHolder(address holder, uint tokens, uint timest) onlyAssetsOwners external {
    if (holderList[holder].isActive == false) {
      holderList[holder].isActive = true;
      holderList[holder].tokens = tokens;
      holderList[holder].holdPeriodTimestamp = timest;
      holderIndexes[holderIndex] = holder;
      holderIndex++;
    } else {
      holderList[holder].tokens += tokens;
      holderList[holder].holdPeriodTimestamp = timest;
    }
    tokenAddHold += tokens;
    Hold(msg.sender, holder, tokens, timest);
  }

  function getBalance() public constant returns (uint) {
    return OppToken.balanceOf(this);
  }

  function unlockTokens() external {
    address contributor = msg.sender;

    if (holderList[contributor].isActive && !holderList[contributor].withdrawed) {
      if (now >= holderList[contributor].holdPeriodTimestamp) {
        if ( OppToken.transfer( msg.sender, holderList[contributor].tokens ) ) {
          TokensTransfered(contributor,  holderList[contributor].tokens);
          tokenWithdrawHold += holderList[contributor].tokens;
          holderList[contributor].withdrawed = true;
          holderWithdrawIndex++;
        }
      } else {
        revert();
      }
    } else {
      revert();
    }
  }

  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 getOverTokens() public onlyOwner {
    require(getBalance() > (tokenAddHold - tokenWithdrawHold));
    uint balance = getBalance() - (tokenAddHold - tokenWithdrawHold);
    if(balance > 0) {
      if(OppToken.transfer(msg.sender, balance)) {
        TokensTransfered(msg.sender,  balance);
      }
    }
  }

  function getTokenAddHold() onlyOwner public constant returns (uint) {
    return tokenAddHold;
  }
  function getTokenWithdrawHold() onlyOwner public constant returns (uint) {
    return tokenWithdrawHold;
  }
  function getHolderIndex() onlyOwner public constant returns (uint) {
    return holderIndex;
  }
  function getHolderWithdrawIndex() onlyOwner public constant returns (uint) {
    return holderWithdrawIndex;
  }
}
function HoldSaleContract(address _OppToken) public {
  OppToken = OpportyToken(_OppToken);
  addAssetsOwner(msg.sender);
}

/* constructor */