contract BlootRealEstate is ERC721Enumerable, ReentrancyGuard, Ownable {

    ERC721 bloot = ERC721(0x4F8730E0b32B04beaa5757e5aea3aeF970E5B613);
    
    // mirror a n-sided dice roll
    function randomN(uint256 tokenId, string memory keyPrefix, uint8 n) internal pure returns (uint256) {
        string memory input = string(abi.encodePacked(keyPrefix, toString(tokenId), "1"));
        uint256 roll = (uint256(keccak256(abi.encodePacked(input))) % n) + 1;
        return roll;
    }

    function random3(uint256 tokenId, string memory keyPrefix) internal pure returns (uint256) {
        string memory input1 = string(abi.encodePacked(keyPrefix, toString(tokenId), "1"));
        string memory input2 = string(abi.encodePacked(keyPrefix, toString(tokenId), "2"));
        string memory input3 = string(abi.encodePacked(keyPrefix, toString(tokenId), "3"));

        uint256 roll1 = (uint256(keccak256(abi.encodePacked(input1))) % 6) + 1;
        uint256 roll2 = (uint256(keccak256(abi.encodePacked(input2))) % 6) + 1;
        uint256 roll3 = (uint256(keccak256(abi.encodePacked(input3))) % 6) + 1;

        uint256 sum = roll1 + roll2 + roll3;
        return sum;
    }
    
    function getLocation(uint256 tokenId) public pure returns (string memory) {
        return pluckLocation(tokenId, "Location");
    }
    
    function getType(uint256 tokenId) public pure returns (string memory) {
        return pluckType(tokenId, "Type");
    }
    
    function getPartOfTown(uint256 tokenId) public pure returns (string memory) {
        return pluckPartOfTown(tokenId, "Part of Town");
    }
    
    function getBedrooms(uint256 tokenId) public pure returns (string memory) {
        return pluckRooms(tokenId, "Bedrooms");
    }

    function getBathrooms(uint256 tokenId) public pure returns (string memory) {
        return pluckRooms(tokenId, "Bathrooms");
    }
    
    function getAttribute1(uint256 tokenId) public pure returns (string memory) {
        return pluckAttribute(tokenId, "Attribute 1");
    }

    function getAttribute2(uint256 tokenId) public pure returns (string memory) {
        return pluckAttribute(tokenId, "Attribute 2");
    }

    function getAttribute3(uint256 tokenId) public pure returns (string memory) {
        return pluckAttribute(tokenId, "Attribute 3");
    }
    
    /*
        3: 1/216 = 0.5%, 1
        4: 3/216 = 1.4%, 4
        5: 6/216 = 2.8%, 10
        6: 10/216 = 4.6%, 20
        7: 15/216 = 7.0%, 35
        8: 21/216 = 9.7%, 56
        9: 25/216 = 11.6%, 81
        10: 27/216 = 12.5%, 108
        11: 27/216 = 12.5%, 135
        12: 25/216 = 11.6%, 160
        13: 21/216 = 9.7%, 181
        14: 15/216 = 7.0%, 196
        15: 10/216 = 4.6%, 206
        16: 6/216 = 2.8%, 212
        17: 3/216 = 1.4%, 215
        18: 1/216 = 0.5%, 216
    */
    function pluckLocation(uint256 tokenId, string memory keyPrefix) internal pure returns (string memory) {
        uint256 roll = randomN(tokenId, keyPrefix, 13);
        string memory location = "";
        if (roll == 1) {
            location = "Shithole, Nowhere";
        }
        else if (roll == 2) {
            location = "New York, NY";
        }
        else if (roll == 3) {
            location = "Paris, France";
        }
        else if (roll == 4) {
            location = "London, UK";
        }
        else if (roll == 5) {
            location = "Tokyo, Japan";
        }
        else if (roll == 6) {
            location = "Beijing, China";
        }
        else if (roll == 7) {
            location = "Vancouver, Canada";
        }
        else if (roll == 8) {
            location = "Tel Aviv, Israel";
        }
        else if (roll == 9) {
            location = "Amsterdam, Netherlands";
        }
        else if (roll == 10) {
            location = "San Francisco, California";
        }
        else if (roll == 11) {
            location = "Los Angeles, California";
        }
        else if (roll == 12) {
            location = "Miami, Florida";
        }
        else {
            location = "Gary, Indiana";
        }

        string memory output = string(abi.encodePacked(keyPrefix, ": ", location));
        return output;
    }

    function pluckType(uint256 tokenId, string memory keyPrefix) internal pure returns (string memory) {
        uint256 roll = random3(tokenId, keyPrefix);
        string memory homeType = "";
        if (roll < 6) {
            homeType = "Mansion";
        }
        else if (roll < 9) {
            homeType = "Single Family Home";
        }
        else if (roll < 11) {
            homeType = "Condo";
        }
        else if (roll == 13) {
            homeType = "Townhouse";
        }
        else if (roll == 14) {
            homeType = "Ranch";
        }
        else {
            homeType = "Shack";
        }

        string memory output = string(abi.encodePacked(keyPrefix, ": ", homeType));
        return output;
    }

    function pluckPartOfTown(uint256 tokenId, string memory keyPrefix) internal pure returns (string memory) {
        uint256 roll = randomN(tokenId, keyPrefix, 3);
        string memory part = "";
        if (roll == 1) {
            part = "Downtown";
        }
        else if (roll == 2) {
            part = "Suburbs";
        }
        else{
            part = 'Wilderness';
        }

        string memory output = string(abi.encodePacked(keyPrefix, ": ", part));
        return output;
    }

    function pluckRooms(uint256 tokenId, string memory keyPrefix) internal pure returns (string memory) {
        uint256 roll = random3(tokenId, keyPrefix);
        uint8 rooms = 1;

        if (roll == 10 || roll == 11) {
            rooms = 1;
        }
        else if (roll == 9 || roll == 12) {
            rooms = 2;
        }
        else if (roll == 8 || roll == 13) {
            rooms = 3;
        }
        else if (roll == 7 || roll == 14) {
            rooms = 4;
        }
        else if (roll == 6 || roll == 15) {
            rooms = 5;
        }
        else if (roll == 5 || roll == 16) {
            rooms = 6;
        }
        else if (roll == 4 || roll == 17) {
            rooms = 7;
        }
        else if (roll == 3 || roll == 18) {
            rooms = 8;
        }

        string memory output = string(abi.encodePacked(keyPrefix, ": ", toString(rooms)));
        return output;
    }

    function pluckAttribute(uint256 tokenId, string memory keyPrefix) internal pure returns (string memory) {
        uint256 roll = random3(tokenId, keyPrefix);
        string memory attribute = "";
        if (roll == 3) {
            attribute = "Beanie's Rainbow Cap";
        }
        else if (roll == 18) {
            attribute = "1,000,000 BGLD Wallet";
        }
        else if (roll == 4) {
            attribute = "Lamborghini Aventador";
        }
        else if (roll == 17) {
            attribute = "Stripper Pole";
        }
        else if (roll == 5) {
            attribute = "Cock Ring Holder";
        }
        else if (roll == 16) {
            attribute = "Infinity Pool";
        }
        else if (roll == 6) {
            attribute = "8Sleep Mattress";
        }
        else if (roll == 15) {
            attribute = "Private Gym";
        }
        else if (roll == 7) {
            attribute = "Broken Toilet";
        }
        else if (roll == 14) {
            attribute = "Inflatable Pool";
        }
        else if (roll == 8) {
            attribute = "Mercedes Benz C300";
        }
        else if (roll == 13) {
            attribute = "Toyota Corolla";
        }
        else if (roll == 9) {
            attribute = "Honda Sienna";
        }
        else if (roll == 12) { 
            attribute = "Expired Milk";
        }
        else if (roll == 11) {
            attribute = "Creaky Furniture";
        }
        else if (roll == 10) {
            attribute = "17 inch TV";
        }
        else {
            attribute = "None";
        }

        string memory output = string(abi.encodePacked(keyPrefix, ": ", attribute));
        return output;
    }


    function tokenURI(uint256 tokenId) override public pure 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: black; font-family: serif; font-size: 14px; }</style><rect width="100%" height="100%" fill="#01ff01" /><text x="10" y="20" class="base">';

        parts[1] = getLocation(tokenId);

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

        parts[3] = getType(tokenId);

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

        parts[5] = getPartOfTown(tokenId);

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

        parts[7] = getBedrooms(tokenId);

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

        parts[9] = getBathrooms(tokenId);

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

        parts[11] = getAttribute1(tokenId);

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

        parts[13] = getAttribute2(tokenId);

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

        parts[15] = getAttribute3(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": "Bloot Home #', toString(tokenId), '", "description": "Bloot Real Estate Market (not for Weaks) is a collection of non-fungible tokens stored on the Ethereum Blockchain, to be used in the Bloot metaverse.", "image": "data:image/svg+xml;base64,', Base64.encode(bytes(output)), '"}'))));
        output = string(abi.encodePacked('data:application/json;base64,', json));

        return output;
    }
    
    function claimForBlootOwner(uint256 tokenId) public nonReentrant {
        require(tokenId > 0 && tokenId < 8009, "Token ID invalid");
        require(bloot.ownerOf(tokenId) == msg.sender, "Not Bloot owner");
        _safeMint(_msgSender(), 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("BlootRealEstate", "BlootRE") Ownable() {}
}
function randomN(uint256 tokenId, string memory keyPrefix, uint8 n) internal pure returns (uint256) {
    string memory input = string(abi.encodePacked(keyPrefix, toString(tokenId), "1"));
    uint256 roll = (uint256(keccak256(abi.encodePacked(input))) % n) + 1;
    return roll;
}

// mirror a n-sided dice roll