contract Playlists is ERC721Enumerable, ReentrancyGuard, Ownable {
    // Prive for non-loot owners
    uint256 public price = 40000000000000000; 

    ERC721 loot = ERC721(0xFF9C1b15B16263C61d017ee9F65C50e4AE0113D7);

    string[] private races = [
        "Humans",
        "Dwarfs",
        "Elfs",
        "Vampires",
        "Undead",
        "Goblins",
        "Demons",
        "Trolls",
        "Orks",
        "Catfolk",
        "Angels",
        "Fairies",
        "Halflings",
        "Giants",
        "Lizardfolk",
        "Nymphs"
    ];
    
    string[] private places = [
        "Abbey",
        "Apiary",
        "Bakery",
        "Barn",
        "Bathhouse",
        "Bridge",
        "Bazaar",
        "Black Market",
        "Beacon Chain",
        "Brewery",
        "Cabin",
        "Castle",
        "Catacombs",
        "Cathedral",
        "Cellar",
        "Citadel",
        "Convent",
        "Crypt",
        "Dock",
        "Dungeon",
        "Farm",
        "Fort",
        "Graveyard",
        "Guild Hall",
        "Doge Hall",
        "Inn",
        "Labyrinth",
        "Lighthouse",
        "Manor",
        "Market",
        "Obelisk",
        "Outpost",
        "Palace",
        "Portal",
        "Shrine",
        "Tavern",
        "Workshop"
    ];
    
    string [] song_types = [
        "Ballads",
        "Anthems",
        "Classics",
        "Crowd Pleasers",
        "Floor Fillers",
        "Hits",
        "Beats",
        "Tunes",
        "Guilty Pleasures"
    ];
    
    string [] decades = ["10s", "00s", "90s", "80s", "70s", "60s"];
    
    string [][] private songs = [
        [
            "Party Rock Anthem",
            "We Found Love",
            "Somebody That I Used To Know",
            "Rolling In The Deep",
            "Blurred Lines",
            "Call Me Maybe",
            "Happy",
            "Royals",
            "Dark Horse",
            "Moves Like Jagger",
            "Just The Way You Are",
            "Thrift Shop",
            "One More Night",
            "All Of Me",
            "We Are Young",
            "Counting Stars",
            "Radioactive",
            "Sexy And I Know It",
            "Someone Like You",
            "E.T."
        ],
        [
            "We Belong Together",
            "Yeah!",
            "Low",
            "I Gotta Feeling",
            "How You Remind Me",
            "No One",
            "Boom Boom Pow",
            "Let Me Love You",
            "Gold Digger",
            "Apologize",
            "Dilemma",
            "Family Affair",
            "Big Girls Don't Cry",
            "Maria Maria",
            "U Got It Bad",
            "Whatever You Like",
            "Bleeding Love",
            "Independent Women Part I",
            "Foolish",
            "Hey Ya!"
        ],
        [
            "How Do I Live",
            "Macarena",
            "Un-Break My Heart",
            "Foolish Games / You Were Meant For Me",
            "(Everything I Do) I Do It For You",
            "I'll Make Love To You",
            "Too Close",
            "One Sweet Day",
            "Truly Madly Deeply",
            "Candle In The Wind / Something About The Way You Look Tonight",
            "End Of The Road",
            "The Sign",
            "The Boy Is Mine",
            "Because I Love You",
            "Whoomp! (There It Is)" ,
            "Rush Rush",
            "You're Still The One",
            "I Will Always Love You",
            "Gangsta's Paradise",
            "Nothing Compares 2 U"
        ],
        [
            "Physical",
            "Bette Davis Eyes",
            "Endless Love",
            "Eye Of The Tiger",
            "Every Breath You Take",
            "Flashdance...What A Feeling",
            "Another One Bites The Dust",
            "Say Say Say",
            "Call Me",
            "Lady",
            "Centerfold",
            "(Just Like) Starting Over" ,
            "I Love Rock 'N Roll",
            "Ebony And Ivory",
            "That's What Friends Are For",
            "Upside Down",
            "Abracadabra",
            "Billie Jean",
            "Say You, Say Me",
            "All Night Long (All Night)"
        ],
        [
            "You Light Up My Life",
            "Tonight's The Night (Gonna Be Alright)",
            "Le Freak",
            "How Deep Is Your Love",
            "I Just Want To Be Your Everything",
            "Silly Love Songs",
            "Let's Get It On",
            "Night Fever",
            "Tie A Yellow Ribbon Round The Ole Oak Tree",
            "Shadow Dancing",
            "Stayin' Alive",
            "Hot Stuff",
            "You're So Vain",
            "Play That Funky Music",
            "My Sharona",
            "Killing Me Softly With His Song",
            "Best Of My Love",
            "The Way We Were",
            "A Fifth Of Beethoven",
            "I Will Survive"
        ],
        [
            "The Twist",
            "Hey Jude",
            "The Theme From 'A Summer Place'",
            "Tossin' And Turnin'",
            "I Want To Hold Your Hand",
            "I'm a Believer",
            "Aquarius / Let The Sunshine In",
            "Sugar, Sugar",
            "I Heard It Through The Grapevine",
            "Are You Lonesome Tonight?",
            "It's Now Or Never",
            "I Can't Stop Loving You",
            "I'm Sorry",
            "Love Is Blue",
            "Hello, Dolly!",
            "Big Girls Don't Cry",
            "Sugar Shack",
            "Honky Tonk Women",
            "(Sittin' On) The Dock Of The Bay",
            "Big Bad John"
        ]
    ];
        
    function random(string memory input) internal pure returns (uint256) {
        return uint256(keccak256(abi.encodePacked(input)));
    }
    
    function pluck(uint256 tokenId, string memory keyPrefix, string[] memory sourceArray) internal pure returns (string memory) {
        uint256 rand = random(string(abi.encodePacked(keyPrefix, toString(tokenId))));
        string memory output = sourceArray[rand % sourceArray.length];
        return output;
    }
    
    function getName(uint256 tokenId) public view returns (string memory) {
        return string(
            abi.encodePacked(
                pluck(tokenId, "PLACES", places), " ", 
                pluck(tokenId, "SONG_TYPES", song_types), " for ", 
                pluck(tokenId, "RACES", races), " (#",
                toString(tokenId),
                ")"));
    }
    
    function tokenURI(uint256 tokenId) override public view returns (string memory) {
        string[13] 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">';
        
        for (uint8 i = 0; i < 6; i++) {
            parts[(i*2)+1] = pluck(tokenId, decades[i], songs[i]);
            parts[(i*2)+2] = string(abi.encodePacked('</text><text x="10" y="', toString((20 * i) + 40),'" class="base">'));
        }

        parts[12] = '</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]));
        
        string memory json = Base64.encode(bytes(string(abi.encodePacked('{"name": "', getName(tokenId), '", "description": "Playlists are randomized and stored on chain. They are usable in metaverses. Loot frens can claim their playlist to match their loot.", "image": "data:image/svg+xml;base64,', Base64.encode(bytes(output)), '"}'))));
        output = string(abi.encodePacked('data:application/json;base64,', json));

        return output;
    }
    
    function setPrice(uint256 newPrice) public onlyOwner {
        price = newPrice;
    }

	// Because others need a chance too.
    function mint(uint256 tokenId) public payable nonReentrant {
        require(tokenId >= 8001 && tokenId < 9750, "Token ID invalid, public sales for 8001-9749.");
        require(price <= msg.value, "Ether value sent is not correct");
        _safeMint(_msgSender(), tokenId);
    }
	
	// Because the loot holders come first.
    function mintWithLoot(uint256 tokenId) public nonReentrant {
        require(tokenId > 0 && tokenId < 8001, "Token ID invalid, loot owners can claim 1-8000.");
        require(loot.ownerOf(tokenId) == msg.sender, "Not Loot owner");
        _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] >= 8001 && tokenIds[i] < 9750, "Token ID invalid, public sales for 8001-9749.");
          _safeMint(msg.sender, tokenIds[i]);
        }
      }
      
    function multiMintWithLoot(uint256[] memory lootIds) public nonReentrant {
        for (uint256 i = 0; i < lootIds.length; i++) {
          require(loot.ownerOf(lootIds[i]) == msg.sender, "Not the owner of this loot");
          _safeMint(_msgSender(), lootIds[i]);
        }
    }
    
	// Because gas is expensive...
    function ownerClaim(uint256 tokenId) public nonReentrant onlyOwner {
        require(tokenId > 9750 && tokenId < 10001, "Token ID invalid, owner can claim last 250.");
        _safeMint(owner(), tokenId);
    }
    
    function toString(uint256 value) internal pure returns (string memory) {
        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 withdraw() public onlyOwner {
        payable(owner()).send(address(this).balance);
    }
    
    constructor() ERC721("Playlists (for Adventurers)", "PLAYLIST") Ownable() {}
}
function ownerClaim(uint256 tokenId) public nonReentrant onlyOwner {
    require(tokenId > 9750 && tokenId < 10001, "Token ID invalid, owner can claim last 250.");
    _safeMint(owner(), tokenId);
}

// Because gas is expensive...