contract MintFactory is Ownable {
    using EnumerableSet for EnumerableSet.AddressSet;
    
    EnumerableSet.AddressSet private tokens;
    EnumerableSet.AddressSet private tokenGenerators;
    
    mapping(address => address[]) private tokenOwners;
    
    event tokenRegistered(address tokenAddress, address tokenOwner);
    
    function adminAllowTokenGenerator (address _address, bool _allow) public onlyOwner {
        if (_allow) {
            tokenGenerators.add(_address);
        } else {
            tokenGenerators.remove(_address);
        }
    }
    
    /**
     * @notice called by a registered tokenGenerator upon token creation
     */
    function registerToken (address _tokenOwner, address _tokenAddress) public {
        require(tokenGenerators.contains(msg.sender), 'FORBIDDEN');
        tokens.add(_tokenAddress);
        tokenOwners[_tokenOwner].push(_tokenAddress);
        emit tokenRegistered(_tokenAddress, _tokenOwner);
    }

     /**
     * @notice gets a token at index registered under a user address
     * @return token address registered to the user address
     */
     function getTokenByOwnerAtIndex(address _tokenOwner, uint256 _index) external view returns(address) {
         return tokenOwners[_tokenOwner][_index];
     }
     
     /**
     * @notice gets the total number of tokens registered under a user address
     * @return uint total of token addresses registered to the user address
     */
     
     function getTokensLengthByOwner(address _tokenOwner) external view returns(uint256) {
         return tokenOwners[_tokenOwner].length;
     }
    
    /**
     * @notice Number of allowed tokenGenerators
     */
    function tokenGeneratorsLength() external view returns (uint256) {
        return tokenGenerators.length();
    }
    
    /**
     * @notice Gets the address of a registered tokenGenerator at specified index
     */
    function tokenGeneratorAtIndex(uint256 _index) external view returns (address) {
        return tokenGenerators.at(_index);
    }

    /**
     * @notice returns true if contract is allowed to generate and register tokens
     */
    function tokenGeneratorIsAllowed(address _tokenGenerator) external view returns (bool) {
        return tokenGenerators.contains(_tokenGenerator);
    }
    
    /**
     * @notice returns true if the token is a valid ENMT token generated by the Unicrypt Minter
     */
    function isENMT(address _tokenAddress) external view returns (bool) {
        return tokens.contains(_tokenAddress);
    }
    
    /**
     * @notice The length of all valid ENMT tokens on the platform
     */
    function tokensLength() external view returns (uint256) {
        return tokens.length();
    }
    
    /**
     * @notice gets an ENMT token at a specific index. Although using Enumerable Set, since tokens are only added and not removed, indexes will never change
     * @return the address of the ENMT token at index
     */
    function tokenAtIndex(uint256 _index) external view returns (address) {
        return tokens.at(_index);
    }
    
}
function tokenAtIndex(uint256 _index) external view returns (address) {
    return tokens.at(_index);
}

/**
 * @notice gets an ENMT token at a specific index. Although using Enumerable Set, since tokens are only added and not removed, indexes will never change
 * @return the address of the ENMT token at index
 */