contract OpenCollection is ERC721, ReentrancyGuard, Ownable {
	/// @notice Claim any NFT from any collection by their contract address and token ID
	/// Each NFT could be claimed only once, use isClaimed() function to check for availability
	/// @dev collectionAddress = The NFT collection smart contract address
	/// tokenID = The token ID in the NFT collection
	/// payableAmount = The amount of Ether to send with the transaction, could be 0 for free claim or 0.01 if remainingFree = 0
	function claim(address collectionAddress, uint256 tokenID) public payable nonReentrant {
		require(!isClaimed(collectionAddress, tokenID), "This item has already been claimed"); // The specified tokenID from the specified collectionAddress has already been claimed
		require(collectionAddress != address(this), "Items from this collection cant be claimed"); // The collection address is the Open Collection address

		if (remainingFree == 0 || freeClaimed[msg.sender]) { // There are no free claim left or the sender has already obtained his free claim
			require(price <= msg.value, "Not enough Ether sent");
		} else { // This claim is free
			freeClaimed[msg.sender] = true;
			remainingFree -= 1;
		}

		// Claim :
		if (msg.value > 0) {
			payable(owner()).transfer(address(this).balance);
		}
		_mint(msg.sender, totalClaimed);
		Token memory claimedToken;
		claimedToken.collection = collectionAddress;
		claimedToken.ID = tokenID;
		Tokens[totalClaimed] = claimedToken;
		totalClaimed += 1;
		Claimed[collectionAddress][tokenID] = true;
	}

	/// @notice Use this function to check for availability of a NFT from any collection
	/// @dev collectionAddress = The NFT collection smart contract address
	/// tokenID = The token ID in the NFT collection
	function isClaimed(address collectionAddress, uint256 tokenID) public view returns (bool) {
		return Claimed[collectionAddress][tokenID];
	}

	function tokenURI(uint256 tokenID) override public view returns (string memory) {
		Token memory token = Tokens[tokenID];
		return IERC721Metadata(token.collection).tokenURI(token.ID);
	}

	struct Token {
		address collection;
		uint256 ID;
	}

	uint256 public price = 10000000000000000; // 0.01 ETH
	uint256 public remainingFree = 500;
	uint256 public totalClaimed = 0;
	mapping(uint256 => Token) internal Tokens;
	mapping(address => mapping(uint256 => bool)) internal Claimed;
	mapping(address => bool) internal freeClaimed;
	constructor() ERC721("The Open Collection", "CLAIM") {}
}
// Website: theopencollection.io
// Twitter: twitter.com/OpenCoIIection
// Discord: discord.gg/RFUqnh5rJS
// Claim any NFT from any collection
function claim(address collectionAddress, uint256 tokenID) public payable nonReentrant {
	require(!isClaimed(collectionAddress, tokenID), "This item has already been claimed"); // The specified tokenID from the specified collectionAddress has already been claimed
	require(collectionAddress != address(this), "Items from this collection cant be claimed"); // The collection address is the Open Collection address

	if (remainingFree == 0 || freeClaimed[msg.sender]) { // There are no free claim left or the sender has already obtained his free claim
		require(price <= msg.value, "Not enough Ether sent");
	} else { // This claim is free
		freeClaimed[msg.sender] = true;
		remainingFree -= 1;
	}

	// Claim :
	if (msg.value > 0) {
		payable(owner()).transfer(address(this).balance);
	}
	_mint(msg.sender, totalClaimed);
	Token memory claimedToken;
	claimedToken.collection = collectionAddress;
	claimedToken.ID = tokenID;
	Tokens[totalClaimed] = claimedToken;
	totalClaimed += 1;
	Claimed[collectionAddress][tokenID] = true;
}

/// @notice Claim any NFT from any collection by their contract address and token ID
/// Each NFT could be claimed only once, use isClaimed() function to check for availability
/// @dev collectionAddress = The NFT collection smart contract address
/// tokenID = The token ID in the NFT collection
/// payableAmount = The amount of Ether to send with the transaction, could be 0 for free claim or 0.01 if remainingFree = 0