// File: WatchecksMetadata2.sol



// SPDX-License-Identifier: MIT

import {Base64} from "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Base64.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol";
// import {Base64} from "OpenZeppelin/openzeppelin-contracts@4.8.0/contracts/utils/Base64.sol";
// import "OpenZeppelin/openzeppelin-contracts@4.8.0/contracts/access/Ownable.sol";

pragma solidity ^0.8.0;

interface IMetadataRenderer {
    function tokenURI(uint256) external view returns (string memory);

    function contractURI() external view returns (string memory);

    function initializeWithData(bytes memory initData) external;
}

interface IWatchecksEngine {
    function getTimeZone(uint256 id) external view returns (uint256);
}

contract WatchecksMetadata2 is IMetadataRenderer, Ownable {
    // address private targetContract = 0xC3120F76424c21A4019A10Fbc90AF0481b267123;
    string private _contractURI;
    IWatchecksEngine private IWatchecks;

    string private svgPart0 =
        '<svg width="400" height="400" viewBox="0 0 400 400" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="200" cy="200" r="140" fill="#';
    string private svgPart1 =
        '" id="dial"/><circle cx="200" cy="200" r="135" fill="none" stroke="black" stroke-width="4" stroke-dasharray="2 12.1371" stroke-dashoffset="1"/><circle cx="200" cy="200" r="136" fill="none" stroke="black" stroke-width="2" stroke-dasharray=".4 1.9736" stroke-dashoffset="0.2"/><rect width="120" height="25" fill="#fff" fill-opacity="0.5" stroke="#000" stroke-opacity="0.5" stroke-width="2" x="140" y="280"/><text x="200" y="300" fill="#000" font-weight="bold" font-family="Courier New" font-size="24" text-anchor="middle" id="dig"/><g id="houru" transform="rotate(';
    string private svgPart1b = ',200,200)" fill="#';
    string private svgPart2 =
        '"><rect x="196" y="110" width="8" height="120"/></g><g id="minu" transform="rotate(';
    string private svgPart2b = ',200,200)" fill="#';
    string svgPart3 =
        '"><rect x="197" y="90" width="6" height="140"/></g><g id="secondCenter" transform="rotate(';
    string private svgPart3b = ',200,200)" stroke="#';
    string private svgPart3c = '" fill="#';
    string private svgPart4 =
        '"><rect x="198" y="81" width="4" height="120"/><circle cx="200" cy="200" r="4"/><path d="M200 71C200.4 78 202 81 202 81H198C198 81 199.6 78 200 71Z" fill="none" stroke-width="1.5"/></g><script>var update,apiTime,mainTaskInterval,_offSet= ';
    string private svgPart5 =
        ' , timeDelta=0,paused=!1;async function getUTC(){try{await fetch("https://worldtimeapi.org/api/timezone/Etc/UTC",{}).then(e=>e.json()).then(e=>{apiTime=e.unixtime})}catch{apiTime=Math.floor((new Date).getTime()/1e3)}}document.getElementById("dial").addEventListener("click",async e=>{if(paused)return await run(),void(paused=!1);window.clearInterval(mainTaskInterval),paused=!0});const hourHand=document.getElementById("houru"),minHand=document.getElementById("minu"),secHand=document.getElementById("secondCenter"),digits=document.getElementById("dig");function baseDateObj(){timeDelta=Math.floor((new Date).getTime())-1e3*apiTime;var e=new Date(0);return e.setUTCSeconds(apiTime+_offSet),e}function generateCurrentTime(e){return new Date(e.getTime()+6e4*e.getTimezoneOffset()+timeDelta)}function generateCurrentAngles(e){var t=6*e.getSeconds()+6*Math.floor(e.getMilliseconds()/200)*200/1e3,n=6*e.getMinutes()+.1*e.getSeconds(),a=30*e.getHours()+.5*e.getMinutes();minHand.setAttribute("transform",`rotate(${n},200,200)`),hourHand.setAttribute("transform",`rotate(${a},200,200)`),secHand.setAttribute("transform",`rotate(${t},200,200)`),digits.innerHTML=`${e.getHours().toString().padStart(2,"0")}:${e.getMinutes().toString().padStart(2,"0")}:${e.getSeconds().toString().padStart(2,"0")}`}function actual(){generateCurrentAngles(generateCurrentTime(baseDateObj()))}async function run(){await getUTC(),mainTaskInterval=setInterval(update,200)}update=function(){actual()},run();</script></svg>';

    string[2] private handColor = ["000000", "ffffff"];

    string[23] private faceColor = ["F39C12","e5a0c8","b5252d","8E44AD","ea392a", "98d9f0", "f36e6e","e22e2d","E74C3C","FFC300","3498DB","aada4a","e6444c","2d6485","9B59B6","edaa3e","2b5452","fad450","685620", "d0f4e2","569e32","5fcd8c","2C3E50"];

    constructor(address IAddress) {
        IWatchecks = IWatchecksEngine(IAddress);
    }

    function setContractURI(string memory _uri) public onlyOwner {
        _contractURI = _uri;
    }

    function getTime(uint256 id) public view returns (uint256) {
        return IWatchecks.getTimeZone(id);
    }

    function renderTokenById(uint256 id) public view returns (string memory) {
        uint256 _time = getTime(id);
        if (_time == 0) {
            _time = 36605;
        } else {
            _time = _time - 1;
        }
        string memory _svg0 = string(
            abi.encodePacked(
                svgPart0,
                faceColor[id % 23],
                svgPart1,
                _toString((_time % 43200) / 120),
                svgPart1b,
                handColor[id % 2],
                svgPart2,
                _toString((_time % 3600) / 10),
                svgPart2b
            )
        );
        string memory _svg1 = string(
            abi.encodePacked(
                handColor[id % 2],
                svgPart3,
                _toString((_time % 60) * 6),
                svgPart3b,
                handColor[(id + 1) % 2],
                svgPart3c,
                handColor[(id + 1) % 2]
            )
        );
        string memory _svg2 = string(
            abi.encodePacked(svgPart4, _toString(_time), svgPart5)
        );
        return string(abi.encodePacked(_svg0, _svg1, _svg2));
    }

    function previewTokenById(
        uint256 id,
        uint256 time
    ) public view returns (string memory) {
        require(time > 0, "very bad");
        require(time < 86401, "bad bad");
        uint256 _time = time - 1;
        string memory _svg0 = string(
            abi.encodePacked(
                svgPart0,
                faceColor[id % 23],
                svgPart1,
                _toString((_time % 43200) / 120),
                svgPart1b,
                handColor[id % 2],
                svgPart2,
                _toString((_time % 3600) / 10),
                svgPart2b
            )
        );
        string memory _svg1 = string(
            abi.encodePacked(
                handColor[id % 2],
                svgPart3,
                _toString((_time % 60) * 6),
                svgPart3b,
                handColor[(id + 1) % 2],
                svgPart3c,
                handColor[(id + 1) % 2]
            )
        );
        string memory _svg2 = string(
            abi.encodePacked(svgPart4, _toString(_time), svgPart5)
        );
        return string(abi.encodePacked(_svg0, _svg1, _svg2));
    }

    function constructTokenURI(
        uint256 id
    ) internal view returns (string memory) {
        uint256 _time = getTime(id);
        string memory _cond;
        if (_time == 0) {
            _cond = "Unworn";
        } else {
            _cond = "Worn";
        }
        string memory _imageData = Base64.encode(bytes(renderTokenById(id)));
        string memory _header = '{"name": "Watchecks ';
        string memory _mid = '","description": "This Clockwork is Adjustable",';
        string memory _attr = string(
            abi.encodePacked(
                '"attributes": [{"trait_type": "Reference","value": "Ref#',
                _toString(id % 46),
                '"},{"display_type": "number","trait_type": "Number","value": ',
                _toString(id),
                '},{"trait_type": "Condition","value": "',
                _cond,
                '"}]}'
            )
        );
        return
            string(
                abi.encodePacked(
                    "data:application/json;base64,",
                    Base64.encode(
                        bytes(
                            abi.encodePacked(
                                _header,
                                _toString(id),
                                _mid,
                                '"image": "data:image/svg+xml;base64,',
                                _imageData,
                                '", "animation_url": "data:image/svg+xml;base64,',
                                _imageData,
                                '",',
                                _attr
                            )
                        )
                    )
                )
            );
    }

    function tokenURI(uint256 tokenId) public view returns (string memory) {
        //zora proxy check the token existence
        return constructTokenURI(tokenId);
    }

    function contractURI() public view returns (string memory) {
        return _contractURI;
    }

    /**
     * @dev Converts a uint256 to its ASCII string decimal representation.
     */
    function _toString(
        uint256 value
    ) internal pure virtual returns (string memory str) {
        assembly {
            // The maximum value of a uint256 contains 78 digits (1 byte per digit), but
            // we allocate 0xa0 bytes to keep the free memory pointer 32-byte word aligned.
            // We will need 1 word for the trailing zeros padding, 1 word for the length,
            // and 3 words for a maximum of 78 digits. Total: 5 * 0x20 = 0xa0.
            let m := add(mload(0x40), 0xa0)
            // Update the free memory pointer to allocate.
            mstore(0x40, m)
            // Assign the `str` to the end.
            str := sub(m, 0x20)
            // Zeroize the slot after the string.
            mstore(str, 0)

            // Cache the end of the memory to calculate the length later.
            let end := str

            // We write the string from rightmost digit to leftmost digit.
            // The following is essentially a do-while loop that also handles the zero case.
            // prettier-ignore
            for { let temp := value } 1 {} {
                str := sub(str, 1)
                // Write the character to the pointer.
                // The ASCII index of the '0' character is 48.
                mstore8(str, add(48, mod(temp, 10)))
                // Keep dividing `temp` until zero.
                temp := div(temp, 10)
                // prettier-ignore
                if iszero(temp) { break }
            }

            let length := sub(end, str)
            // Move the pointer 32 bytes leftwards to make room for the length.
            str := sub(str, 0x20)
            // Store the length.
            mstore(str, length)
        }
    }

    function initializeWithData(bytes memory initData) external {
        //pass
    }
}

// File: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

// File: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Base64.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Base64.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides a set of functions to operate with Base64 strings.
 *
 * _Available since v4.5._
 */
library Base64 {
    /**
     * @dev Base64 Encoding/Decoding Table
     */
    string internal constant _TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

    /**
     * @dev Converts a `bytes` to its Bytes64 `string` representation.
     */
    function encode(bytes memory data) internal pure returns (string memory) {
        /**
         * Inspired by Brecht Devos (Brechtpd) implementation - MIT licence
         * https://github.com/Brechtpd/base64/blob/e78d9fd951e7b0977ddca77d92dc85183770daf4/base64.sol
         */
        if (data.length == 0) return "";

        // Loads the table into memory
        string memory table = _TABLE;

        // Encoding takes 3 bytes chunks of binary data from `bytes` data parameter
        // and split into 4 numbers of 6 bits.
        // The final Base64 length should be `bytes` data length multiplied by 4/3 rounded up
        // - `data.length + 2`  -> Round up
        // - `/ 3`              -> Number of 3-bytes chunks
        // - `4 *`              -> 4 characters for each chunk
        string memory result = new string(4 * ((data.length + 2) / 3));

        /// @solidity memory-safe-assembly
        assembly {
            // Prepare the lookup table (skip the first "length" byte)
            let tablePtr := add(table, 1)

            // Prepare result pointer, jump over length
            let resultPtr := add(result, 32)

            // Run over the input, 3 bytes at a time
            for {
                let dataPtr := data
                let endPtr := add(data, mload(data))
            } lt(dataPtr, endPtr) {

            } {
                // Advance 3 bytes
                dataPtr := add(dataPtr, 3)
                let input := mload(dataPtr)

                // To write each character, shift the 3 bytes (18 bits) chunk
                // 4 times in blocks of 6 bits for each character (18, 12, 6, 0)
                // and apply logical AND with 0x3F which is the number of
                // the previous character in the ASCII table prior to the Base64 Table
                // The result is then added to the table to get the character to write,
                // and finally write it in the result pointer but with a left shift
                // of 256 (1 byte) - 8 (1 ASCII char) = 248 bits

                mstore8(resultPtr, mload(add(tablePtr, and(shr(18, input), 0x3F))))
                resultPtr := add(resultPtr, 1) // Advance

                mstore8(resultPtr, mload(add(tablePtr, and(shr(12, input), 0x3F))))
                resultPtr := add(resultPtr, 1) // Advance

                mstore8(resultPtr, mload(add(tablePtr, and(shr(6, input), 0x3F))))
                resultPtr := add(resultPtr, 1) // Advance

                mstore8(resultPtr, mload(add(tablePtr, and(input, 0x3F))))
                resultPtr := add(resultPtr, 1) // Advance
            }

            // When data `bytes` is not exactly 3 bytes long
            // it is padded with `=` characters at the end
            switch mod(mload(data), 3)
            case 1 {
                mstore8(sub(resultPtr, 1), 0x3d)
                mstore8(sub(resultPtr, 2), 0x3d)
            }
            case 2 {
                mstore8(sub(resultPtr, 1), 0x3d)
            }
        }

        return result;
    }
}

// File: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Context.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}