contract Potions is ERC721Enumerable, ReentrancyGuard, Ownable {
    uint256 public lootersPrice = 10000000000000000; // 0.01 ETH
    uint256 public price = 20000000000000000; //0.02 ETH

    //Loot Contract
    address public lootAddress = 0xFF9C1b15B16263C61d017ee9F65C50e4AE0113D7;
    LootInterface public lootContract = LootInterface(lootAddress);

    string[] private base = [
        "Potion",
        "Potion",
        "Flask",
        "Elixir",
        "Glowing Vial",
        "Magical Oil"
    ];

    string[] private namePrefixes = [
        "The Alchemists",
        "The Warriors",
        "The Magicians",
        "The Priests",
        "The Hunters",
        "The Bards",
        "The Forsaken",
        "The Technicians",
        "The Merchants",
        "The Ethereal",
        "The Concentrated",
        "The Engineers",
        "The Forgotten",
        "The Flatulent",
        "The Forbidden",
        "The Perpetual",
        "The Mercenaries",
        "The Kings",
        "The Queens",
        "The Jesters",
        "The Princes",
        "The Princesses"
    ];

    string[] private nameSuffixes = [
        "of Strength",
        "of Dexterity",
        "of Endurance",
        "of Intellect",
        "of Wisdom",
        "of Charisma",
        "of Divine Strength",
        "of Divine Dexterity",
        "of Divine Endurance",
        "of Divine Intellect",
        "of Divine Wisdom",
        "of Divine Charisma",
        "of Aggression",
        "of Evasion", 
        "of Healing",
        "of Malice",
        "of Recovery",
        "of Speed",
        "of Thorns",
        "of Invisibility",
        "of Invulnerability",
        "of Fortune",
        "of Armor",
        "of Wild Visions"
    ];

    string[] private origin = [
        "Newly Discovered",
        "Unopened",
        "Half Full",
        "Half Empty",
        "Old",
        "Fermented",
        "Decaying",
        "Rotten",
        "Curdled",
        "Mysterious",
        "Ancient",
        "Unknown",
        "Alien"
    ];

    string[] private appearance = [
        "Red",
        "Bright Red",
        "Translucent Red",
        "Blue",
        "Bright Blue",
        "Translucent Blue",
        "Green",
        "Bright Green",
        "Translucent Green",
        "Yellow",
        "Bright Yellow",
        "Translucent Yellow",
        "Orange",
        "Bright Orange",
        "Translucent Orange",
        "Purple",
        "Bright Purple",
        "Translucent Purple",
        "Black",
        "Translucent Black",
        "White",
        "Bright White",
        "Translucent White",
        "Pink",
        "Bright Pink",
        "Translucent Pink",
        "Brown",
        "Dark Brown",
        "Translucent Brown",
        "Transparent",
        "Iridescent",
        "Glowing",
        "Stinky",
        "Explosive",
        "Radioactive",
        "Psychedelic",
        "Unknown"
    ];

    string[] private magic = [
        "Mighty",
        "Love",
        "Lust",
        "Envy",
        "Divine",
        "Divine Love",
        "Enduring",
        "Insight",
        "Healing",
        "Divine Healing",
        "Ornate",
        "Animal Friendship",
        "Vampiric",
        "Clairvoyance",
        "Berserker",
        "Flying",
        "Gaseous Form",
        "Giant Strength",
        "Heroism",
        "Divine Heroism",
        "Mind Reading",
        "Water Breathing",
        "Levitation",
        "Magic Resistance",
        "Fire Resistance",
        "Wind Resistance",
        "Earth Resistance",
        "Water Resistance"
    ];

    string[] private realm = [
        "Concocted in",
        "Brewed in",
        "Discovered in",
        "Invented in",
        "Conjured in",
        "Created in",
        "Alchemized in",
        "Manifested in",
        "Stumbled Upon in",
        "Uncovered in",
        "Pillaged from",
        "Stolen from",
        "Found in",
        "Excavated in",
        "Accidentally Discovered in",
        "Accidentally Manifested in",
        "Accidentally Stolen from",
        "Accidentally Alchemized in",
        "Accidentally Conjured in",
        "Accidentally Concocted in",
        "Gifted from the King of",
        "Gifted from the Queen of",
        "Gifted from the Prince of",
        "Gifted from the Princess of",
        "Stolen from the King of",
        "Stolen from the Queen of",
        "Stolen from the Prince of",
        "Stolen from the Princess of",
        "Gifted from the Elders of",
        "Discovered by a Wizard from",
        "Summoned by a Warlock from",
        "Brewed by a Witch from",
        "Conjured by a Mage from",
        "Tuned by a Bard from"
    ];

    string[] private realmName = [
        "Okompolen",
        "Letulapemi",
        "Din Ren",
        "Immommememo",
        "Sangsang",
        "Arum-Unarev",
        "Upohumonuh",
        "Oqovuv",
        "Hompon",
        "Ponpon",
        "Wlukglig",
        "Mounluonnui",
        "Yaymagyay",
        "Mukkok",
        "Atalatat",
        "Pootzizdaas",
        "Popempo",
        "Tap Nah",
        "il Shar",
        "Chyekbak",
        "Kutshukshuk",
        "Mokuo",
        "Slonlunlun",
        "um Wun",
        "Rokylesghuk",
        "Enulinenon",
        "Meshmep",
        "Kekxix",
        "Nlel Nlip",
        "Lupzii"
    ];

// change rarity to not repeat the same thing

    function random(string memory input) internal pure returns (uint256) {
        return uint256(keccak256(abi.encodePacked(input)));
    }

    function getBase(uint256 tokenId) public view returns (string memory) {
        return pluck(tokenId, "BASE", base);
    }

    function getOrigin(uint256 tokenId) public view returns (string memory) {
        return pluck(tokenId, "ORIGIN", origin);
    }

    function getAppearance(uint256 tokenId) public view returns (string memory) {
        return pluck(tokenId, "APPEARANCE", appearance);
    }

    function getMagic(uint256 tokenId) public view returns (string memory) {
        return pluck(tokenId, "MAGIC", magic);
    }

    function getRealm(uint256 tokenId) public view returns (string memory) {
        return pluck(tokenId, "REALM", realm);
    }

    function getRealmName(uint256 tokenId) public view returns (string memory) {
        return pluck(tokenId, "REALMNAME", realmName);
    }

    function pluck(
        uint256 tokenId,
        string memory keyPrefix,
        string[] memory sourceArray
    ) internal view returns (string memory) {
        uint256 rand = random(string(abi.encodePacked(keyPrefix, toString(tokenId))));
        string memory output = sourceArray[rand % sourceArray.length];
        uint256 greatness = rand % 21;

        if (keccak256(abi.encodePacked(keyPrefix)) == keccak256("BASE")) {
            if (greatness > 14) {
                string[2] memory name;
                name[0] = namePrefixes[rand % namePrefixes.length];
                name[1] = nameSuffixes[rand % nameSuffixes.length];
                output = string(abi.encodePacked(name[0], " ", output, " ", name[1]));
            }
            if (greatness > 5 && greatness <= 14) {
                output = string(abi.encodePacked(output, " ", nameSuffixes[rand % nameSuffixes.length]));
            }
        }

        if (keccak256(abi.encodePacked(keyPrefix)) == keccak256("REALM")) {
            output = string(abi.encodePacked(output, " ", realmName[rand % realmName.length]));
        }


        return output;
    }

    function tokenURI(uint256 tokenId) public view override returns (string memory) {
        string[17] memory parts;
        parts[
            0
        ] = '<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin meet" viewBox="0 0 350 350"><style>.base { fill: white; font-family: serif; font-size: 14px; }</style><rect width="100%" height="100%" fill="black" /><text x="10" y="20" class="base">';

        parts[1] = getBase(tokenId);

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

        parts[3] = getOrigin(tokenId);

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

        parts[5] = getAppearance(tokenId);

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

        parts[7] = getMagic(tokenId);

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

        parts[9] = getRealm(tokenId);

        parts[10] = "</text></svg>";

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

        output = string(
            abi.encodePacked(
                output,
                parts[9],
                parts[10]
            )
        );

        string memory json = Base64.encode(
            bytes(
                string(
                    abi.encodePacked(
                        '{"name": "Potion #',
                        toString(tokenId),
                        '", "description": "Potions are randomized generated and stored on chain. Images and other functionality are intentionally omitted for others to interpret. Feel free to use potions in any way you want. Inspired and compatible with Loot (for Adventurers)", "image": "data:image/svg+xml;base64,',
                        Base64.encode(bytes(output)),
                        '"}'
                    )
                )
            )
        );
        output = string(abi.encodePacked("data:application/json;base64,", json));

        return output;
    }

    function mint(uint256 tokenId) public payable nonReentrant {
        require(tokenId > 8000 && tokenId <= 12000, "Token ID invalid");
        require(price <= msg.value, "Ether value sent is not correct");
        _safeMint(_msgSender(), tokenId);
    }

    function multiMint(uint256[] memory tokenIds) public payable nonReentrant {
        require((price * tokenIds.length) <= msg.value, "Ether value sent is not correct");
        for (uint256 i = 0; i < tokenIds.length; i++) {
            require(tokenIds[i] > 8000 && tokenIds[i] < 12000, "Token ID invalid");
            _safeMint(msg.sender, tokenIds[i]);
        }
    }

    function mintWithLoot(uint256 lootId) public payable nonReentrant {
        require(lootId > 0 && lootId <= 8000, "Token ID invalid");
        require(lootersPrice <= msg.value, "Ether value sent is not correct");
        require(lootContract.ownerOf(lootId) == msg.sender, "Not the owner of this loot");
        _safeMint(_msgSender(), lootId);
    }

    function multiMintWithLoot(uint256[] memory lootIds) public payable nonReentrant {
        require((lootersPrice * lootIds.length) <= msg.value, "Ether value sent is not correct");
        for (uint256 i = 0; i < lootIds.length; i++) {
            require(lootContract.ownerOf(lootIds[i]) == msg.sender, "Not the owner of this loot");
            _safeMint(_msgSender(), lootIds[i]);
        }
    }

    function withdraw() public onlyOwner {
        payable(0x6Ad85f72C82120aFb8f777095b60E8a9E84F29d0).transfer(address(this).balance);
    }

    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("Potions", "POTS") Ownable() {}
}
function random(string memory input) internal pure returns (uint256) {
    return uint256(keccak256(abi.encodePacked(input)));
}

// change rarity to not repeat the same thing