contract NiftyBuilderInstance is ERC721, ERC721Burnable {

    // Engine(s) of nifty type dynamism.
    address public _dateTimeContract00;
    address public _dateTimeContract01;

    /**
     * @dev Eroding and Reforming Bust of Zeus (Earth Day)
     *
     * @return uint256 index of asset
     */
    function _bustOfZeus(IDateTime dateTime) internal view returns (uint256) {
        uint8 hour = dateTime.getHour(block.timestamp);
        uint8 threshold = 17;
        if (hour > threshold) {
            return uint256(3);
        }
        threshold = 11;
        if (hour > threshold) {
            return uint256(2);
        }
        threshold = 5;
        if (hour > threshold) {
            return uint256(1);
        } 
        return uint256(0);
    }

    string[4] artifact01 = ["QmNWr9r6pEEnyh9aejue8upHSjYN24pWrAzpYmjRgseExr", //State 01
                            "QmZuaZ2RphWvnaxMxz9Pd95c4E5DPhE5BtSw4auztiMC1u", //State 02
                            "QmaB29NhdweR75hAWEehvcXCwXBwN8KBpny6uzdsUHNogv", //State 03
                            "QmfFCHSMAnjCqyPgTJDCQQuJJCpfqoupcihevgPxk7jHBF"];//State 04

    /**
     * @dev Eroding and Reforming Venus of Arles (72.6 Years)
     *
     * @return uint256 index of asset
     */
    function _venusOfArles(IDateTime dateTime) internal view returns (uint256) {
        uint16 offset = 2021;
        uint16 value = dateTime.getYear(block.timestamp) - offset;
        uint16 criterion = 73;
        uint16 result = value % criterion;
        return uint256(result);
    }

    string[73] artifact02 = ["QmR81UuQPkhfGE4K4cbbnBLj9Z5EZcEocu8YY18UM7NUM1", //State 01
                             "QmR4bA1ifjixhtkTYnz4VmHKWMpafZv4mFehmeGqcDQdvA", //State 02
                             "QmRzsgPoMsoYwiVKWYkFDeRjB3C2SiSKXVmTkDHGqy8SRq", //State 03
                             "QmRa4cdAHdnbfLX3s65rih6FYvwMfTePqSw46cDPR9gkX7", //State 04
                             "QmRMR9yLVFmLCGZyrSmuSPwh6KxkyScXVZ2akY3N4h7Hf5", //State 05
                             "QmRgrgEoac8hxMv4zJQmRhxVfUEAthxHQyV4vYUirXXo3W", //State 06
                             "QmcHYcrKNGs1NN8jnyJUnAzneK6DbaKtmLVwcZVp28Fuji", //State 07
                             "QmTjWk2p8eJcW6FR2y1t8mLcEAHs9QKt2tcPCcsXTXA5NA", //State 08
                             "QmNsf7GZUw2Bz4DSek2fcaXEvxSH6GdEUjmzUiprj9i2Ta", //State 09
                             "QmbEmMK7hLzCe9YWqhqXV3HQedEgorxtc642CkkCogVVho", //State 10
                             "QmbR2s2uPcywASUudpsujWmhxoFqy5hzxF9DM5ohRNQfWZ", //State 11
                             "QmWc3r6VknYB6vAu5astS8PzPjGwq3gQAexxt6g6H169gU", //State 12
                             "QmZrbFSuHf6zdM66RFgj2vCQatbAVYfyCBhHPsSzPzEz2M", //State 13
                             "QmeVyHYGP9QK3pSGpYPcMCrTrf8PWMppdq9cSxB8TjunRs", //State 14
                             "QmY9SvuK8rTSoZsAWxgaXS6fPeQSYhqsmWgqxAYbfuDM4y", //State 15
                             "QmawC7bUXhqwHeoAagd7UV2HhcGxpZ4PZHkU1rxM6ywgKV", //State 16
                             "QmSiysm4VWLJ19JF81U7enxcfjkb2mVyrzhJkZ6qCK42xS", //State 17
                             "QmYZHBoNxD9hAZASAThC93yRrGcfV5nCgi86wQNVgZkemV", //State 18
                             "QmRZ1B27Y1YuDoq6W32p8sFKUaj6hQwfaMJuCzWkA4wmKB", //State 19
                             "Qmb9b2QkBzSdkPooewAAQRcQULFYP49i8qdgRkZ7z5sijh", //State 20
                             "QmNNa8zqTZFpjo2ex2kd4x9vLZjK93WWmAp444VWS7NdVD", //State 21
                             "QmQaKQDstMwrEKZW5W2mbDywuNN6kxuE6MiB5UZZZ7umfJ", //State 22
                             "QmVcYsG9T3Ng5CJmVAQk7vHR5YzRaTfBkXFbYArqTsaEsA", //State 23
                             "Qma27xgagQLKhT89DDRvUTfmn5CacCXdn778bWQDUR6NG2", //State 24
                             "QmZbWLsVVRNgFyjjGWfkCAxisMEEGVJPrpbSiuYRS8unPc", //State 25
                             "QmNQ2aVkkaKV2eKQMPYVqCFBvaRsKtabAVg9LxCwibueBf", //State 26
                             "QmVMKPbTNU2fzDmwGTEPVxYsR8TF49yaVF8Vk1tT5uYGEY", //State 27
                             "QmaqrMBptBkdJcPyrKfrh8axTpD9a38qfodcoXsRNdnibp", //State 28
                             "QmYbvARyHSJqrp6PTeyvQj16EGrG7rLssoDfxr61JBVchX", //State 29
                             "QmSSrCyXCHeiKxxrr9V5gxZ4U4j3yt1mxvhGbrQKxRqiB8", //State 30
                             "QmWJov2kfhHPg7QLZkkbzC8WmKwD81wdATKE94hASfeiMM", //State 31
                             "Qmabw8dNheYekvDbCez1GDdC6tUdrgCLq8SgUsRFYcMRU8", //State 32
                             "QmWxi9VLRyMwXy11pSJbcG8XBgf25PXK3QbSZCc7V7aXoe", //State 33
                             "QmVGS6oXHtPh3jeYnqP7Y28Ti8a8UqJDwNbN6H7bPo9aMk", //State 34
                             "QmQ5tkbbYieCTvdVPL85PMZzij3MqB9TDsMiWh1UzR8CeN", //State 35
                             "QmbJkDacA74hcUQiATdSJfCkfxbKpj7CbZKJSfvGw7TZ3D", //State 36
                             "QmSAihqMxEqYPGaVf9ApFh416NF18soLcnpPM2iR77J7jB", //State 37
                             "QmZfwfjhjdRzQUBF98TSbZPUiWJod9GDiDWB7piK344b7a", //State 38
                             "QmUDn7VKVT5gAeNZ36kNA27f4cxS32SQSwsQujFFmebtJK", //State 39
                             "QmaR98jw8oiJyZRb39FcYuzf885GoXHGTK7uewvCN28FGG", //State 40
                             "QmTG9yZ4sTdWXPQNwYkRzdzHCjWDMk8qhBY8JCvZdvJksp", //State 41
                             "QmfRrs388cZqvGc5kctm5hrTNwhX6LeK4Eexk8adBxuYLj", //State 42
                             "QmdrxzuEecPEL6L7YVixucehiRY2aBWEbZAEeYQ8VkDNes", //State 43
                             "QmRrWxhqQfvgXV7nuGc58CQfXnE2QKq9WXYd2HaVQNxUJ9", //State 44
                             "QmXDPxvzeb1gpkchDY3qQai6RYBhALVWqo79HhPYDmbbiQ", //State 45
                             "QmYa3zGVn33SsGXD6pSXxARHvc9JHBDvRkPCeD8t9RadpH", //State 46
                             "QmfSkwPEQYbazMKwLrVaZmrFu7Rk6KVTqxVggr7F6zvwfF", //State 47
                             "QmaU1Hejt2kLTVpcnXfEpWcDT7MC8fb8uWjcBW5SCEgcBn", //State 48
                             "QmehTRiyb8MVxMJZccHnJk24QtuZdoXRoYc1qZJFWNM2DS", //State 49
                             "QmPzBPEMjaov2EdZF6mE5Sg4cki35MnnYGCp855MjBdQCS", //State 50
                             "QmXYgaCZFitc3PWMiNoNvesLPxq18Z2UXcGKQQrbmZsfCg", //State 51
                             "QmSqRdFM2rWX6igiBvfXbuMCbN9hx13DwrVTtQHi8GNvTE", //State 52
                             "QmWawbhnLFBC1J71QT55YC6htPwX6WDoLmwoj33aR75muC", //State 53
                             "QmYLiBGhwfuBWGNi3NmFQgVWj55b2Vxur2HvjVn7tMoD9Y", //State 54
                             "QmWW1YjGiVBjgSeaQqtNLyp1gKbyVdboqT7DUChXw3NgQt", //State 55
                             "QmecGhERiccBCXa7BHwASMZGVZRBQPBdnyDAuuzLctPRvB", //State 56
                             "QmPJNXZ1bXJU3FxCBgb4pUNHGBDQhuwVVYUUGWyuwp4vLX", //State 57
                             "QmPXsbSRwkWs5xAEqpt9hCbjNDmHsZvvJZkNH3b419FMDm", //State 58
                             "QmWaLpKgkQk2xrwyPp1kdmsB5xYcrAtiaT1U9tt7BfyxbU", //State 59
                             "QmU7LR8jiQcjAwTxxrXiGc3j7Ust7Ggjeb5Ysog4T5iW7N", //State 60
                             "QmcTxChkC259PTqzVgUtCaGstS6d5d3EtMcbnCX2bsZBNj", //State 61
                             "QmP8eGwqDLtmHD89Nofr54FsAcHvdL2mEMnCjssS9sEcwP", //State 62
                             "QmSVZPVDsZThK1spkL9Cy4SrxLTsNebXuRn2wCtAf3fXFm", //State 63
                             "QmUXfmP9k8SsUMNzC89y9F4NWRXkadBV5t6QBQkTTChRZP", //State 64
                             "QmXHLwFwwa5xiH3xXWPzvPLHRYoEQdKo9ZcMDs1dSfvERD", //State 65
                             "QmfTHh3E3JM5cZc3erTHJvkXeKh7i5SjAeXwBrQPwwTDTc", //State 66
                             "QmT6iHKZN9HvwWyS2d3XqBbhqPLXcBf1FXHHNNk7TcbAFu", //State 67
                             "QmfQpywZY2W2giE929VmqJ6m2NUwWKqeZT663yK661WB4L", //State 68
                             "QmRtiHoEE5kYrrnFjP51qTECMHBsdMH6PkJSt9uEEzkXXi", //State 69
                             "QmRiUTY6e8AtEdfvVSc3suRw7sEtRPdZeXkKwyVaM8bvcX", //State 70
                             "QmVQJD4g4jxJPi6XoCecXwWv7ULkWG5sU75UCFeM5XGPL5", //State 71
                             "QmZ2SjXuVdVGdR9viCpNhU8ZSRtsnEEErmjLq6LvMgDn4v", //State 72
                             "QmSx78hghW4cpWq3tinRYmKoHm665LEsDPPXmK1zmg8UpY"];//State 73

    /**
     * @dev Eroding and Reforming Bust of Melpomene (Martian Day)
     *
     * @return uint256 index of asset
     */
    function _bustOfMelpomene(IDateTime dateTime) internal view returns (uint256) {
        uint8 hour = dateTime.getHour(block.timestamp);
        uint8 weekday = dateTime.getWeekday(block.timestamp);

        uint8 criterion = 2;
        uint8 result = weekday - uint8(weekday / criterion);
        
        uint8 condition = 0;
        if (result == condition) {
            uint8 offset = 23;
            uint8 result = offset - hour;
            return uint256(result);
        } 
        return uint256(hour);
    }

    string[24] artifact03 = ["Qma5iW6ch6kGE3GB1FkCvUt5LruJ3nZN7CjJQNZwifxSQH", //State 01
                             "QmNrSBLVJUNFHBqkT6r5utrNVELLGUPvnyYmXpFQMyw8Bs", //State 02
                             "QmZ1f4ku6MNt6XnoPTQReYva4AApL2JjBzydwfxPaRoEgk", //State 03
                             "QmZATZUY14RhFvqfGu8CX1ajUy4Qfx9KDNLYpJjAeiL2a3", //State 04
                             "QmRn6ncB9VVNNY8U6jpLawaQ5ogoRpKGon8XAcAXmHobeL", //State 05
                             "QmNLmKCa9EFRvYa8RbdiJjUjtmd6PF99VCaQLtkbfe5U42", //State 06
                             "Qmd4UgHyTFuSe3PTRJiUvmAqc3adSApzsoLFQeCbtidwpW", //State 07
                             "QmSxt7BUfePqABdwci7x9By3LgFUvbCM44WRsLoEFC3MYT", //State 08
                             "QmWetvFhJLuuJBSMhSPwt5xyCv3uW179FhF8MbmnbKUehW", //State 09
                             "QmXRShHWTLoKWVQZmbEQ1jDenwuV2BPHqNSYpNWnYoJ1K3", //State 10
                             "QmYkYqMX9FXY7MP5E9edr1ZeNEFBGqz1BNKCdcvkN7eFaN", //State 11
                             "QmYQqsn1FsMRm79hw8cXTkGPKH3pa3ouTGiTLveGJqm65A", //State 12
                             "QmYBNZ9AYGtGFQU4HUi2iAWCuhHt7nQ57dv7fuEeGfshTN", //State 13
                             "QmRsmx1ayeYaArsXRHVan1AQqqW8eBQKURguGfG5xjtpsN", //State 14
                             "QmZNtn3vk2SoJ4QJf6u2USnUvq5QNMrMqcTKC8s2fAghKY", //State 15
                             "QmYGN52qdpBoAJs5ye3RuyxgVfw1kAxv6Sjy4fRugb5eES", //State 16
                             "QmVuxM6foMHArx2qSmiuqhUWCVWid5r1Hhyq8w7aPHiecq", //State 17
                             "QmZe4njp7a3o9DyRbHFn1RDtGCiyXXoFHJZoxkmEjrFgwT", //State 18
                             "Qmeqk9eH3sTiucLTbpPAYq28Dj6icc9oAPJtiPej5e5ssC", //State 19
                             "QmekyAay43mjWax1byL5Erny5KyhN8pZgVhLE7Tg8waDR2", //State 20
                             "QmcjSYZuCHGS7zox5jyw5bE3NjHNghydjwkqiUMEj3hyPg", //State 21
                             "QmTigpNi4Y3iZQngxDUbKGcw2D7RrvACCiTVGxFmbRujKV", //State 22
                             "QmTJsYU9zaJR3pUQCLkLGYNan5N8a96sDoR3HgEwQz6Pzw", //State 23
                             "QmUH2WtFbawmshSo9PayGwnz4seZSS8NkGyGbuVYkod6Sh"];//State 24

    // The artist associated with the collection.
    string private _creator;

    // Number of NFTs minted for a given 'typeCount'. 
    mapping (uint256 => uint256) public _mintCount;

    /**
     * @dev Serves as a gas cost optimized boolean flag 
     * to indicate whether the minting process has been 
     * concluded for a given 'typeCount', correspinds 
     * to the {_getFinalized} and {setFinalized}.
     */
    mapping (uint256 => bytes32) private _finalized;

    /**
     * @dev Emitted when tokens are created.
     */
    event ConsecutiveTransfer(uint256 indexed fromTokenId, uint256 toTokenId, address indexed fromAddress, address indexed toAddress);

    /**
     * @dev Ultimate instantiation of a Nifty Gateway NFT collection. 
     */
    constructor() ERC721() {
        _creator = "Daniel Arsham";
        _dateTimeContract00 = 0x740a637ADD6492e5FaA907AF0fe708770B737058;
        _dateTimeContract01 = 0x740a637ADD6492e5FaA907AF0fe708770B737058;
        _setNiftyTypeName(1, "Eroding and Reforming Bust of Zeus (Earth Day)");
        _setNiftyTypeName(2, "Eroding and Reforming Venus of Arles (72.6 Years)");
        _setNiftyTypeName(3, "Eroding and Reforming Bust of Melpomene (Martian Day)");
    }

    /**
     * Configurable address for DateTime.
     */ 
    function setDateTimeContract00(address dateTimeContract_) onlyValidSender public {
        _dateTimeContract00 = dateTimeContract_;
    }

    /**
     * Configurable address for DateTime.
     */ 
    function setDateTimeContract01(address dateTimeContract_) onlyValidSender public {
        _dateTimeContract01 = dateTimeContract_;
    }

    /**
     * @dev Generate canonical Nifty Gateway token representation. 
     * Nifty contracts have a data model called a 'niftyType' (typeCount) 
     * The 'niftyType' refers to a specific nifty in our contract, note 
     * that it gives no information about the edition size. In a given 
     * contract, 'niftyType' 1 could be an edition of 10, while 'niftyType' 
     * 2 is a 1/1, etc.
     * The token IDs are encoded as follows: {id}{niftyType}{edition #}
     * 'niftyType' has 4 digits, and edition number does as well, to allow 
     * for 9999 possible 'niftyType' and 9999 of each edition in each contract.
     * Example token id: [500010270]
     * This is from contract #5, it is 'niftyType' 1 in the contract, and it is 
     * edition #270 of 'niftyType' 1.
     */
    function _encodeTokenId(uint256 niftyType, uint256 tokenNumber) private view returns (uint256) {
        return (topLevelMultiplier + (niftyType * midLevelMultiplier) + tokenNumber);
    }

    /**
     * @dev Determine whether it is possible to mint additional NFTs for this 'niftyType'.
     */
    function _getFinalized(uint256 niftyType) public view returns (bool) {
        bytes32 chunk = _finalized[niftyType / 256];
        return (chunk & bytes32(1 << (niftyType % 256))) != 0x0;
    }

    /**
     * @dev Prevent the minting of additional NFTs of this 'niftyType'.
     */
    function setFinalized(uint256 niftyType) public onlyValidSender {
        uint256 quotient = niftyType / 256;
        bytes32 chunk = _finalized[quotient];
        _finalized[quotient] = chunk | bytes32(1 << (niftyType % 256));
    }

    /**
     * @dev The artist of this collection.
     */
    function creator() public view virtual returns (string memory) {
        return _creator;
    }

    /**
     * @dev Assign the root location where the artifact assets are stored.
     */
    function setBaseURI(string memory baseURI) public onlyValidSender {
        _setBaseURI(baseURI);
    }

    /**
     * @dev Allow owner to change nifty name, by 'niftyType'.
     */
    function setNiftyName(uint256 niftyType, string memory niftyName) public onlyValidSender {
        _setNiftyTypeName(niftyType, niftyName);
    }

    /**
     * @dev Assign the IPFS hash of canonical artifcat file, by 'niftyType'.
     */   
    function setNiftyIPFSHash(uint256 niftyType, string memory hashIPFS) public onlyValidSender {
        _setTokenIPFSHashNiftyType(niftyType, hashIPFS);
    }

    /**
     * @dev Create specified number of nifties en masse.
     * Once an NFT collection is spawned by the factory contract, we make calls to set the IPFS
     * hash (above) for each Nifty type in the collection. 
     * Subsequently calls are issued to this function to mint the appropriate number of tokens 
     * for the project.
     */
    function mintNifty(uint256 niftyType, uint256 count) public onlyValidSender {
        require(!_getFinalized(niftyType), "NiftyBuilderInstance: minting concluded for nifty type");
            
        uint256 tokenNumber = _mintCount[niftyType] + 1;
        uint256 tokenId00 = _encodeTokenId(niftyType, tokenNumber);
        uint256 tokenId01 = tokenId00 + count - 1;
        
        for (uint256 tokenId = tokenId00; tokenId <= tokenId01; tokenId++) {
            _owners[tokenId] = _defaultOwner;
        }
        _mintCount[niftyType] += count;
        _balances[_defaultOwner] += count;

        emit ConsecutiveTransfer(tokenId00, tokenId01, address(0), _defaultOwner);
    }

    /**
     * @dev Returns an IPFS hash for a given token ID.
     * Throws if the token ID does not exist. May return an empty string.
     * @param tokenId uint256 ID of the token to query.
     * @return IPFS hash for this (_typeCount) NFT. 
     */
    function tokenIPFSHash(uint256 tokenId) external view returns (string memory) {
        require(_exists(tokenId), "ERC721Metadata: IPFS hash query for nonexistent token");

        IDateTime dateTime00 = IDateTime(_dateTimeContract00);
    
        uint256 niftyType = _getNiftyTypeId(tokenId);
       
        if (niftyType == 2) {
            uint256 value = _venusOfArles(dateTime00);
            return artifact02[value];
        } 
        else if (niftyType == 1) {
            uint256 value = _bustOfZeus(dateTime00);
            return artifact01[value];
        } 
        IDateTime dateTime01 = IDateTime(_dateTimeContract01);
        uint256 value = _bustOfMelpomene(dateTime01);
        return artifact03[value];
    }

}
/** 
 * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX  .***   XXXXXXXXXXXXXXXXXX
 * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX  ,*********  XXXXXXXXXXXXXXXX
 * XXXXXXXXXXXXXXXXXXXXXXXXXXXX  ***************  XXXXXXXXXXXXX
 * XXXXXXXXXXXXXXXXXXXXXXXXX  .*******************  XXXXXXXXXXX
 * XXXXXXXXXXXXXXXXXXXXXXX  ***********    **********  XXXXXXXX
 * XXXXXXXXXXXXXXXXXXXX   ***********       ***********  XXXXXX
 * XXXXXXXXXXXXXXXXXX  ***********         ***************  XXX
 * XXXXXXXXXXXXXXXX  ***********           ****    ********* XX
 * XXXXXXXXXXXXXXXX *********      ***    ***      *********  X
 * XXXXXXXXXXXXXXXX  **********  *****          *********** XXX
 * XXXXXXXXXXXX   /////.*************         ***********  XXXX
 * XXXXXXXXX  /////////...***********      ************  XXXXXX
 * XXXXXXX/ ///////////..... /////////   ///////////   XXXXXXXX
 * XXXXXX  /    //////.........///////////////////   XXXXXXXXXX
 * XXXXXXXXXX .///////...........//////////////   XXXXXXXXXXXXX
 * XXXXXXXXX .///////.....//..////  /////////  XXXXXXXXXXXXXXXX
 * XXXXXXX# /////////////////////  XXXXXXXXXXXXXXXXXXXXXXXXXXXX
 * XXXXX   ////////////////////   XXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 * XX   ////////////// //////   XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 *
 * @dev Nifty Gateway extension of customized NFT contract, encapsulates
 * logic for minting new tokens, and concluding the minting process. 
 */
function mintNifty(uint256 niftyType, uint256 count) public onlyValidSender {
    require(!_getFinalized(niftyType), "NiftyBuilderInstance: minting concluded for nifty type");
            
    uint256 tokenNumber = _mintCount[niftyType] + 1;
    uint256 tokenId00 = _encodeTokenId(niftyType, tokenNumber);
    uint256 tokenId01 = tokenId00 + count - 1;
        
    for (uint256 tokenId = tokenId00; tokenId <= tokenId01; tokenId++) {
        _owners[tokenId] = _defaultOwner;
    }
    _mintCount[niftyType] += count;
    _balances[_defaultOwner] += count;

    emit ConsecutiveTransfer(tokenId00, tokenId01, address(0), _defaultOwner);
}

/**
 * @dev Create specified number of nifties en masse.
 * Once an NFT collection is spawned by the factory contract, we make calls to set the IPFS
 * hash (above) for each Nifty type in the collection. 
 * Subsequently calls are issued to this function to mint the appropriate number of tokens 
 * for the project.
 */