contract Freeze is Ownable {
  
  using SafeMath for uint256;

  struct Group {
    address[] holders;
    uint until;
  }
  
	/**
	 * @dev number of groups
	 */
  uint public groups;
  
  address[] public gofindAllowedAddresses;
  
	/**
	 * @dev link group ID ---> Group structure
	 */
  mapping (uint => Group) public lockup;
  
	/**
	 * @dev Check if holder under lock up
	 */
  modifier lockupEnded (address _holder, address _recipient) {
    uint index = indexOf(_recipient, gofindAllowedAddresses);
    if (index == 0) {
      bool freezed;
      uint groupId;
      (freezed, groupId) = isFreezed(_holder);
    
      if (freezed) {
        if (lockup[groupId-1].until < block.timestamp)
          _;
        else 
          revert("Your holdings are freezed, wait until transfers become allowed");
      }
      else 
        _;
    }
    else
      _;
  }
  
  function addGofindAllowedAddress (address _newAddress) public onlyOwner returns (bool) {
    require(indexOf(_newAddress, gofindAllowedAddresses) == 0, "that address already exists");
    gofindAllowedAddresses.push(_newAddress);
    return true;
  }
	
	/**
	 * @param _holder address of token holder to check
	 * @return bool - status of freezing and group
	 */
  function isFreezed (address _holder) public view returns(bool, uint) {
    bool freezed = false;
    uint i = 0;
    while (i < groups) {
      uint index  = indexOf(_holder, lockup[i].holders);

      if (index == 0) {
        if (checkZeroIndex(_holder, i)) {
          freezed = true;
          i++;
          continue;
        }  
        else {
          i++;
          continue;
        }
      }
      
      if (index != 0) {
        freezed = true;
        i++;
        continue;
      }
      i++;
    }
    if (!freezed) i = 0;
    
    return (freezed, i);
  }
  
	/**
	 * @dev internal usage to get index of holder in group
	 * @param element address of token holder to check
	 * @param at array of addresses that is group of holders
	 * @return index of holder at array
	 */
  function indexOf (address element, address[] memory at) internal pure returns (uint) {
    for (uint i=0; i < at.length; i++) {
      if (at[i] == element) return i;
    }
    return 0;
  }
  
	/**
	 * @dev internal usage to check that 0 is 0 index or it means that address not exists
	 * @param _holder address of token holder to check
	 * @param lockGroup id of group to check address existance in it
	 * @return true if holder at zero index at group false if holder doesn't exists
	 */
  function checkZeroIndex (address _holder, uint lockGroup) internal view returns (bool) {
    if (lockup[lockGroup].holders[0] == _holder)
      return true;
        
    else 
      return false;
  }
  
	/**
	 * @dev Will set group of addresses that will be under lock. When locked address can't
	  		  do some actions with token
	 * @param _holders array of addresses to lock
	 * @param _until   timestamp until that lock up will last
	 * @return bool result of operation
	 */
  function setGroup (address[] memory _holders, uint _until) public onlyOwner returns (bool) {
    lockup[groups].holders = _holders;
    lockup[groups].until   = _until;
    
    groups++;
    return true;
  }
}
function setGroup (address[] memory _holders, uint _until) public onlyOwner returns (bool) {
  lockup[groups].holders = _holders;
  lockup[groups].until   = _until;
    
  groups++;
  return true;
}

/**
 	 * @dev Will set group of addresses that will be under lock. When locked address can't
 	  		  do some actions with token
 	 * @param _holders array of addresses to lock
 	 * @param _until   timestamp until that lock up will last
 	 * @return bool result of operation
 	 */