contract LootDeckOFManyThings is ERC721Enumerable, ReentrancyGuard, Ownable {

    string[] private cardName = [
        "Balance",
        "Comet",
        "Donjon",
        "Euryale",
        "The Fates",
        "Flames",
        "Fool",
        "Gem",
        "Idiot",
        "Jester",
        "Key",
        "Knight",
        "Moon",
        "Rogue",
        "Ruin",
        "Skull",
        "Star",
        "Sun",
        "Talons",
        "Throne",
        "Vizier",
        "The Void"
    ];
    
    string[] private cardFirstDescription = [
        "Your alignment changes to its opposite. ",
        "Defeat the next encounter or monster you meet to gain one level.",
        "You disappear and are imprisoned in an extra dimensional space. Everything ",
        "Suffer a penalty on all saving throws henceforth. Can only be removed by ",
        "Avoid any situation you choose. This card must be in your hand, and ",
        "A powerful devil becomes your enemy and seeks to ruin or plague your life. ",
        "Lose adventuring experience points at your GMs discression and you must ",
        "Gain your choice of twenty-five pieces of jewelry or fifty gems.",
        "Your intelligence is permanently reduced. You must draw one card beyond ",
        "You either gain an amount of adventuring expereince of the GMs choosing ",
        "A rare or rarer magical item that you are proficient in appears in your ",
        "Gain the service of a well trained fighter.",
        "You are granted 1d4 wishes.",
        "A friendly NPC of the GM's choice becomes hostile towards you. ",
        "All forms of wealth other than magical items are lost. All carried non magical items ",
        "You immediately summon an Avatar of Death to you. The avatar attacks and warns others ",
        "Immediately gain a permanent bonus to one ability score or your ",
        "You gain adventuring experince and a wondrous item appears in your hands.",
        "All magic items you are wearing or carrying disintegrate. Artifacts ",
        "You gain additional proficiency in Diplomacy. In addition, you gain ",
        "You can ask a question in meditation and receive a truthful answer ",
        "Your soul is pulled from your body and into an object in a place of the GMs choice. "
    ];

    string[] private cardSecondDescription = [
        "Has no effect on neutral alignments.",
        "",
        "you were carrying stays behind. You draw no more cards at this time.",
        "a Diety, a Wish or 'The Fates' card.",
        "disappears when used.",
        "This continues until you or the devil is dead.",
        "draw again.",
        "",
        "the initial number of draws declared.",
        "or you may draw two cards beyond the initial number of draws declared.",
        "hands. The GM decides the item.",
        "",
        "",
        "Only death or divine intervention can end this hostility.",
        "are destroyed. All deeds, properties and titles you held are also lost.",
        "not to interfere, and if they do, another avatar is summoned to join the fight.",
        "choice.",
        "",
        "vanish but aren't destroyed.",
        "rightful ownership if a small keep somewhere in the world.",
        "The answer comes with the wisdom of how to use it.",
        "You draw no more cards."
    ];
    
    function random(uint256 seed) internal pure returns (uint256) {
        return uint256(keccak256(abi.encodePacked(toString(seed))));
    }

    // Input 0 - 21 if you know what card you want,
    // or any other vaue or address to get a random card (the same random card every time).
    function drawYourCard(uint256 tokenId) public view returns (string memory) {
        uint256 rand = random(tokenId);
        if(tokenId < 22){
            rand = tokenId;
        }
        return string(abi.encodePacked(getCardName(rand), " : ", getCardFirstDescription(rand), getCardSecondDescription(rand)));
    }

    // Input 0 - 21 if you know what card you want,
    // or any other vaue or address to get a random card.
    // You can draw another random card once every eth block update (every 15 seconds or so). 
    function drawACard(uint256 tokenId) public view returns (string memory) {
        if(tokenId < 22){
            return drawYourCard(tokenId);
        } else {
            return drawYourCard(tokenId ^ block.timestamp);
        }
    }

    function getCardName(uint256 rand) internal view returns (string memory) {
        return cardName[rand % cardName.length];
    }

    function getCardFirstDescription(uint256 rand) internal view returns (string memory) {
        return cardFirstDescription[rand % cardName.length];
    }

    function getCardSecondDescription(uint256 rand) internal view returns (string memory) {
        return cardSecondDescription[rand % cardName.length];
    }

    // Input 0 - 21 if you know what card you want,
    // or any other vaue or address to get a random card.
    // You can draw another random card once every eth block update (every 15 seconds or so). 
    function tokenURI(uint256 tokenId) override public view returns (string memory) {
        if(tokenId < 22){
            return yourTokenURI(tokenId);
        } else {
            return yourTokenURI(tokenId ^ block.timestamp);
        }
    }

    // Input 0 - 21 if you know what card you want,
    // or any other vaue or address to get a random card (the same random card every time).
    function yourTokenURI(uint256 tokenId) public view returns (string memory) {
        uint256 rand = random(tokenId);
        if(tokenId < 22){
            rand = tokenId;
        }
        string[7] memory parts;
        parts[0] = '<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin meet" viewBox="0 0 350 350"><style>.heading { fill: white; font-family: serif; font-size: 14px; } .base{ fill: white; font-family: serif; font-size: 9px; }</style><rect width="100%" height="100%" fill="black" /><text x="10" y="20" class="heading">';

        parts[1] = getCardName(rand);

        parts[2] = '</text><text x="10" y="40" class="base">';

        parts[3] = getCardFirstDescription(rand);

        parts[4] = '</text><text x="10" y="60" class="base">';

        parts[5] = getCardSecondDescription(rand);

        parts[6] = '</text></svg>';

        string memory output = string(abi.encodePacked(parts[0], parts[1], parts[2], parts[3], parts[4], parts[5], parts[6]));

        string memory json = Base64.encode(bytes(string(abi.encodePacked('{"name": "Card #', toString(tokenId), '", "description": "The Loot Deck of Many Things is generated and stored on chain. Stats, images, and other functionality are intentionally omitted for others to interpret. Feel free to use the Loot Deck of Many Things in any way you want.", "image": "data:image/svg+xml;base64,', Base64.encode(bytes(output)), '"}'))));
        output = string(abi.encodePacked('data:application/json;base64,', json));

        return output;
    }

    function claim(uint256 tokenId) public nonReentrant {
        require(tokenId >= 0 && tokenId < 21, "Token ID invalid");
        _safeMint(_msgSender(), tokenId);
    }
    
    function ownerClaim(uint256 tokenId) public nonReentrant onlyOwner {
        require(tokenId == 21, "Token ID invalid");
        _safeMint(owner(), tokenId);
    }
    
    function toString(uint256 value) internal pure returns (string memory) {
    // Inspired by OraclizeAPI's implementation - MIT license
    // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol

        if (value == 0) {
            return "0";
        }
        uint256 temp = value;
        uint256 digits;
        while (temp != 0) {
            digits++;
            temp /= 10;
        }
        bytes memory buffer = new bytes(digits);
        while (value != 0) {
            digits -= 1;
            buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
            value /= 10;
        }
        return string(buffer);
    }
    
    constructor() ERC721("Loot Deck of Many Things", "LDOMT") Ownable() {}
}
// Inspired by material published under the Open Game License v1.0a
function yourTokenURI(uint256 tokenId) public view returns (string memory) {
        uint256 rand = random(tokenId);
        if(tokenId < 22){
            rand = tokenId;
        }
        string[7] memory parts;
        parts[0] = '<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin meet" viewBox="0 0 350 350"><style>.heading { fill: white; font-family: serif; font-size: 14px; } .base{ fill: white; font-family: serif; font-size: 9px; }</style><rect width="100%" height="100%" fill="black" /><text x="10" y="20" class="heading">';

        parts[1] = getCardName(rand);

        parts[2] = '</text><text x="10" y="40" class="base">';

        parts[3] = getCardFirstDescription(rand);

        parts[4] = '</text><text x="10" y="60" class="base">';

        parts[5] = getCardSecondDescription(rand);

        parts[6] = '</text></svg>';

        string memory output = string(abi.encodePacked(parts[0], parts[1], parts[2], parts[3], parts[4], parts[5], parts[6]));

        string memory json = Base64.encode(bytes(string(abi.encodePacked('{"name": "Card #', toString(tokenId), '", "description": "The Loot Deck of Many Things is generated and stored on chain. Stats, images, and other functionality are intentionally omitted for others to interpret. Feel free to use the Loot Deck of Many Things in any way you want.", "image": "data:image/svg+xml;base64,', Base64.encode(bytes(output)), '"}'))));
        output = string(abi.encodePacked('data:application/json;base64,', json));

        return output;
    }
// Input 0 - 21 if you know what card you want,
// or any other vaue or address to get a random card (the same random card every time).