contract LootComponents {
    string[] internal weapons = [
        "Warhammer", // 0
        "Quarterstaff", // 1
        "Maul", // 2
        "Mace", // 3
        "Club", // 4
        "Katana", // 5
        "Falchion", // 6
        "Scimitar", // 7
        "Long Sword", // 8
        "Short Sword", // 9
        "Ghost Wand", // 10
        "Grave Wand", // 11
        "Bone Wand", // 12
        "Wand", // 13
        "Grimoire", // 14
        "Chronicle", // 15
        "Tome", // 16
        "Book" // 17
    ];
    uint256 constant weaponsLength = 18;

    string[] internal chestArmor = [
        "Divine Robe", // 0
        "Silk Robe", // 1
        "Linen Robe", // 2
        "Robe", // 3
        "Shirt", // 4
        "Demon Husk", // 5
        "Dragonskin Armor", // 6
        "Studded Leather Armor", // 7
        "Hard Leather Armor", // 8
        "Leather Armor", // 9
        "Holy Chestplate", // 10
        "Ornate Chestplate", // 11
        "Plate Mail", // 12
        "Chain Mail", // 13
        "Ring Mail" // 14
    ];
    uint256 constant chestLength = 15;

    string[] internal headArmor = [
        "Ancient Helm", // 0
        "Ornate Helm", // 1
        "Great Helm", // 2
        "Full Helm", // 3
        "Helm", // 4
        "Demon Crown", // 5
        "Dragon's Crown", // 6
        "War Cap", // 7
        "Leather Cap", // 8
        "Cap", // 9
        "Crown", // 10
        "Divine Hood", // 11
        "Silk Hood", // 12
        "Linen Hood", // 13
        "Hood" // 14
    ];
    uint256 constant headLength = 15;

    string[] internal waistArmor = [
        "Ornate Belt", // 0
        "War Belt", // 1
        "Plated Belt", // 2
        "Mesh Belt", // 3
        "Heavy Belt", // 4
        "Demonhide Belt", // 5
        "Dragonskin Belt", // 6
        "Studded Leather Belt", // 7
        "Hard Leather Belt", // 8
        "Leather Belt", // 9
        "Brightsilk Sash", // 10
        "Silk Sash", // 11
        "Wool Sash", // 12
        "Linen Sash", // 13
        "Sash" // 14
    ];
    uint256 constant waistLength = 15;

    string[] internal footArmor = [
        "Holy Greaves", // 0
        "Ornate Greaves", // 1
        "Greaves", // 2
        "Chain Boots", // 3
        "Heavy Boots", // 4
        "Demonhide Boots", // 5
        "Dragonskin Boots", // 6
        "Studded Leather Boots", // 7
        "Hard Leather Boots", // 8
        "Leather Boots", // 9
        "Divine Slippers", // 10
        "Silk Slippers", // 11
        "Wool Shoes", // 12
        "Linen Shoes", // 13
        "Shoes" // 14
    ];
    uint256 constant footLength = 15;

    string[] internal handArmor = [
        "Holy Gauntlets", // 0
        "Ornate Gauntlets", // 1
        "Gauntlets", // 2
        "Chain Gloves", // 3
        "Heavy Gloves", // 4
        "Demon's Hands", // 5
        "Dragonskin Gloves", // 6
        "Studded Leather Gloves", // 7
        "Hard Leather Gloves", // 8
        "Leather Gloves", // 9
        "Divine Gloves", // 10
        "Silk Gloves", // 11
        "Wool Gloves", // 12
        "Linen Gloves", // 13
        "Gloves" // 14
    ];
    uint256 constant handLength = 15;

    string[] internal necklaces = [
        "Necklace", // 0
        "Amulet", // 1
        "Pendant" // 2
    ];
    uint256 constant necklacesLength = 3;

    string[] internal rings = [
        "Gold Ring", // 0
        "Silver Ring", // 1
        "Bronze Ring", // 2
        "Platinum Ring", // 3
        "Titanium Ring" // 4
    ];
    uint256 constant ringsLength = 5;

    string[] internal suffixes = [
        // <no suffix>          // 0
        "of Power", // 1
        "of Giants", // 2
        "of Titans", // 3
        "of Skill", // 4
        "of Perfection", // 5
        "of Brilliance", // 6
        "of Enlightenment", // 7
        "of Protection", // 8
        "of Anger", // 9
        "of Rage", // 10
        "of Fury", // 11
        "of Vitriol", // 12
        "of the Fox", // 13
        "of Detection", // 14
        "of Reflection", // 15
        "of the Twins" // 16
    ];
    uint256 constant suffixesLength = 16;

    string[] internal namePrefixes = [
        // <no name>            // 0
        "Agony", // 1
        "Apocalypse", // 2
        "Armageddon", // 3
        "Beast", // 4
        "Behemoth", // 5
        "Blight", // 6
        "Blood", // 7
        "Bramble", // 8
        "Brimstone", // 9
        "Brood", // 10
        "Carrion", // 11
        "Cataclysm", // 12
        "Chimeric", // 13
        "Corpse", // 14
        "Corruption", // 15
        "Damnation", // 16
        "Death", // 17
        "Demon", // 18
        "Dire", // 19
        "Dragon", // 20
        "Dread", // 21
        "Doom", // 22
        "Dusk", // 23
        "Eagle", // 24
        "Empyrean", // 25
        "Fate", // 26
        "Foe", // 27
        "Gale", // 28
        "Ghoul", // 29
        "Gloom", // 30
        "Glyph", // 31
        "Golem", // 32
        "Grim", // 33
        "Hate", // 34
        "Havoc", // 35
        "Honour", // 36
        "Horror", // 37
        "Hypnotic", // 38
        "Kraken", // 39
        "Loath", // 40
        "Maelstrom", // 41
        "Mind", // 42
        "Miracle", // 43
        "Morbid", // 44
        "Oblivion", // 45
        "Onslaught", // 46
        "Pain", // 47
        "Pandemonium", // 48
        "Phoenix", // 49
        "Plague", // 50
        "Rage", // 51
        "Rapture", // 52
        "Rune", // 53
        "Skull", // 54
        "Sol", // 55
        "Soul", // 56
        "Sorrow", // 57
        "Spirit", // 58
        "Storm", // 59
        "Tempest", // 60
        "Torment", // 61
        "Vengeance", // 62
        "Victory", // 63
        "Viper", // 64
        "Vortex", // 65
        "Woe", // 66
        "Wrath", // 67
        "Light's", // 68
        "Shimmering" // 69
    ];
    uint256 constant namePrefixesLength = 69;

    string[] internal nameSuffixes = [
        // <no name>            // 0
        "Bane", // 1
        "Root", // 2
        "Bite", // 3
        "Song", // 4
        "Roar", // 5
        "Grasp", // 6
        "Instrument", // 7
        "Glow", // 8
        "Bender", // 9
        "Shadow", // 10
        "Whisper", // 11
        "Shout", // 12
        "Growl", // 13
        "Tear", // 14
        "Peak", // 15
        "Form", // 16
        "Sun", // 17
        "Moon" // 18
    ];
    uint256 constant nameSuffixesLength = 18;

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

    function weaponComponents(uint256 tokenId)
        internal
        pure
        returns (uint256[5] memory)
    {
        return pluck(tokenId, "WEAPON", weaponsLength);
    }

    function chestComponents(uint256 tokenId)
        internal
        pure
        returns (uint256[5] memory)
    {
        return pluck(tokenId, "CHEST", chestLength);
    }

    function headComponents(uint256 tokenId)
        internal
        pure
        returns (uint256[5] memory)
    {
        return pluck(tokenId, "HEAD", headLength);
    }

    function waistComponents(uint256 tokenId)
        internal
        pure
        returns (uint256[5] memory)
    {
        return pluck(tokenId, "WAIST", waistLength);
    }

    function footComponents(uint256 tokenId)
        internal
        pure
        returns (uint256[5] memory)
    {
        return pluck(tokenId, "FOOT", footLength);
    }

    function handComponents(uint256 tokenId)
        internal
        pure
        returns (uint256[5] memory)
    {
        return pluck(tokenId, "HAND", handLength);
    }

    function neckComponents(uint256 tokenId)
        internal
        pure
        returns (uint256[5] memory)
    {
        return pluck(tokenId, "NECK", necklacesLength);
    }

    function ringComponents(uint256 tokenId)
        internal
        pure
        returns (uint256[5] memory)
    {
        return pluck(tokenId, "RING", ringsLength);
    }

    function pluck(
        uint256 tokenId,
        string memory keyPrefix,
        uint256 sourceArrayLength
    ) internal pure returns (uint256[5] memory) {
        uint256[5] memory components;

        uint256 rand = random(
            string(abi.encodePacked(keyPrefix, toString(tokenId)))
        );

        components[0] = rand % sourceArrayLength;
        components[1] = 0;
        components[2] = 0;

        uint256 greatness = rand % 21;
        if (greatness > 14) {
            components[1] = (rand % suffixesLength) + 1;
        }
        if (greatness >= 19) {
            components[2] = (rand % namePrefixesLength) + 1;
            components[3] = (rand % nameSuffixesLength) + 1;
            if (greatness == 19) {
                // ...
            } else {
                components[4] = 1;
            }
        }

        return components;
    }

    // TODO: This costs 2.5k gas per invocation. We call it a lot when minting.
    // How can this be improved?
    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 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);
}

// TODO: This costs 2.5k gas per invocation. We call it a lot when minting.
// How can this be improved?