contract Gear is ERC721Enumerable, ReentrancyGuard, Ownable {
    string[] private weapons = [
        "Heavy Machine Gun",
        "Light Machine Gun",
        "Railgun",
        "Assault Rifle",
        "Precision Rifle",
        "Submachine Gun",
        "Double-Barrel Shotgun",
        "Shotgun",
        "Revolver",
        "Pistol",
        "Katana",
        "Shuriken",
        "Switchblade",
        "Knife",
        "Screwdriver",
        "Baseball Bat",
        "Hammer"
    ];

    string[] private chestWare = [
        "Chrome Jacket",
        "Tactical Jacket",
        "Leather Jacket",
        "Denim Jacket",
        "Suit Jacket",
        "Jacket",
        "Fur Coat",
        "Formal Shirt",
        "Tech Shirt",
        "Shirt",
        "Designer Hoodie",
        "Hoodie",
        "T-Shirt",
        "Tactical Vest",
        "Vest",
        "Sweater",
        "Blouse"
    ];

    string[] private headWare = [
        "Baseball Cap",
        "Tactical Helmet",
        "Combat Helmet",
        "Tactical Mask",
        "Gas Mask",
        "Shrike Mask",
        "Bucket Hat",
        "Fedora",
        "Cowboy Hat",
        "Bandana",
        "Beanie",
        "Hardhat",
        "Balaclava",
        "Motorcycle Cap",
        "Cyberware",
        "Hood"
    ];

    string[] private waistWare = [
        "Combat Belt",
        "Tactical Belt",
        "Armored Belt",
        "Studded Belt",
        "Augmented Belt",
        "Leather Belt",
        "Chrome Belt",
        "Chipped Belt",
        "Holographic Belt",
        "Membrane Belt",
        "Hybrid Belt",
        "Polarized Belt",
        "Crossbody Bag",
        "Platinum Belt",
        "Designer Belt"
    ];

    string[] private footWare = [
        "Combat Boots",
        "Ankle Boots",
        "Anti-Gravity Boots",
        "Platform Boots",
        "Tactical Boots",
        "Augmented Boots",
        "Chrome Boots",
        "Holographic Boots",
        "Polarized Boots",
        "Boots",
        "Designer Sneakers",
        "High Top Sneakers",
        "Low Top Sneakers",
        "Sneakers"
    ];

    string[] private handWare = [
        "Armored Gloves",
        "Tactical Gloves",
        "Long Gloves",
        "Chrome Gloves",
        "Holographic Gloves",
        "Polarized Gloves",
        "Holo Hands",
        "Augmented Hands",
        "Chrome Bracelet",
        "Leather Bracelet",
        "Smartwatch",
        "Leather Gloves",
        "Gloves"
    ];

    string[] private eyes = ["Lenses", "Glasses", "Sunglasses"];

    string[] private implant = [
        "Neural Inhibitor",
        "Neural Enhancer",
        "Neural Optimizer",
        "Neural Agitator",
        "Neural Intoxicator"
    ];

    string[] private prefixes = [
        "Waterproof",
        "Fireproof",
        "Bulletproof",
        "Reinforced",
        "Enhanced",
        "Faded",
        "Worn",
        "Bloody",
        "Corrupted",
        "Synthetic",
        "Hidden",
        "Obscured",
        "Concealed",
        "Invisible",
        "Secret",
        "Experimental"
    ];

    string[] private namePrefixes = [
        "Anthracite",
        "Armor",
        "Ballistic",
        "Blood",
        "Bronze",
        "Carbon",
        "Carbyne",
        "Combat",
        "Composite",
        "Corrupt",
        "Cyber",
        "Data",
        "Dark",
        "Diamond",
        "Durable",
        "Duolayer",
        "Elastic",
        "Elemental",
        "Emerald",
        "False",
        "Final",
        "Flint",
        "Frost",
        "Gold",
        "Graphic",
        "Heavy",
        "Hex",
        "Icon",
        "Impulse",
        "Knot",
        "Kevlar",
        "Laminate",
        "Limited",
        "Liquid",
        "Maelstrom",
        "Malware",
        "Machine",
        "Master",
        "Membrane",
        "Military",
        "Nanoweave",
        "Nanite",
        "Net",
        "Pixel",
        "Platinum",
        "Point",
        "Polycarb",
        "Risk",
        "Scope",
        "Serpent",
        "Sight",
        "Tank",
        "Tear",
        "Thermal",
        "Titanium",
        "Valk",
        "Viper",
        "Voxel"
    ];

    string[] private nameSuffixes = [
        "Cypher",
        "Zone",
        "Ware",
        "Tech",
        "Stitch",
        "Fold",
        "Weave",
        "Drip",
        "Fiber",
        "Dusk",
        "Dawn",
        "Synth",
        "Net",
        "Max",
        "Ice",
        "Tap",
        "Night"
    ];

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

    function getWeapon(uint256 tokenId) public view returns (string memory) {
        return pluck(tokenId, "WEAPON", weapons);
    }

    function getChest(uint256 tokenId) public view returns (string memory) {
        return pluck(tokenId, "CHEST", chestWare);
    }

    function getHead(uint256 tokenId) public view returns (string memory) {
        return pluck(tokenId, "HEAD", headWare);
    }

    function getWaist(uint256 tokenId) public view returns (string memory) {
        return pluck(tokenId, "WAIST", waistWare);
    }

    function getFoot(uint256 tokenId) public view returns (string memory) {
        return pluck(tokenId, "FOOT", footWare);
    }

    function getHand(uint256 tokenId) public view returns (string memory) {
        return pluck(tokenId, "HAND", handWare);
    }

    function getEyes(uint256 tokenId) public view returns (string memory) {
        return pluck(tokenId, "EYES", eyes);
    }

    function getImplant(uint256 tokenId) public view returns (string memory) {
        return pluck(tokenId, "IMPLANT", implant);
    }

    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 (greatness > 14) {
            output = string(
                abi.encodePacked(prefixes[rand % prefixes.length], " ", output)
            );
        }
        if (greatness >= 19) {
            string[2] memory name;
            name[0] = namePrefixes[rand % namePrefixes.length];
            name[1] = nameSuffixes[rand % nameSuffixes.length];
            if (greatness == 19) {
                output = string(
                    abi.encodePacked('"', name[0], " ", name[1], '" ', output)
                );
            } else {
                output = string(
                    abi.encodePacked(
                        '"',
                        name[0],
                        " ",
                        name[1],
                        '" ',
                        output,
                        " +1"
                    )
                );
            }
        }
        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 420 420"><style>.base { fill: white; font-family: monospace; font-size: 14px; }</style><rect width="100%" height="100%" fill="#222222" /><text x="10" y="20" class="base">';

        parts[1] = getWeapon(tokenId);

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

        parts[3] = getChest(tokenId);

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

        parts[5] = getHead(tokenId);

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

        parts[7] = getWaist(tokenId);

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

        parts[9] = getFoot(tokenId);

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

        parts[11] = getHand(tokenId);

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

        parts[13] = getEyes(tokenId);

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

        parts[15] = getImplant(tokenId);

        parts[16] = "</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],
                parts[11],
                parts[12],
                parts[13],
                parts[14],
                parts[15],
                parts[16]
            )
        );

        string memory json = Base64.encode(
            bytes(
                string(
                    abi.encodePacked(
                        '{"name": "Stash #',
                        toString(tokenId),
                        '", "description": "Gear is randomized cyber gear generated and stored on chain. Stats, images, and other functionality are intentionally omitted for others to interpret. Feel free to use Gear 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 < 7778, "Token ID invalid");
        _safeMint(_msgSender(), tokenId);
    }

    // function ownerClaim(uint256 tokenId) public nonReentrant onlyOwner {
    //     require(tokenId > 7777 && tokenId < 8001, "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("Gear", "GEAR") Ownable() {}
}
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);
}

// function ownerClaim(uint256 tokenId) public nonReentrant onlyOwner {
//     require(tokenId > 7777 && tokenId < 8001, "Token ID invalid");
//     _safeMint(owner(), tokenId);
// }