// Contract: Pizza
// Target function: function addTraitType(uint256 _traitTypeIndex, Trait[] memory traits)
                _randinput < currentLowerBound + thisPercentage
            ) return i.toString();
            currentLowerBound = currentLowerBound + thisPercentage;
        }

        revert();
    }

    /**
     * @dev Generates a 9 digit hash from a tokenId, address, and random number.
     * @param _t The token id to be used within the hash.
     * @param _a The address to be used within the hash.
     * @param _c The custom nonce to be used within the hash.
     */
    function hash(
        uint256 _t,
        address _a,
        uint256 _c
    ) internal returns (string memory) {
        require(_c < 10);

        // This will generate a 9 character string.
        string memory currentHash = "";

        for (uint8 i = 0; i < 9; i++) {
            SEED_NONCE++;
            uint16 _randinput = uint16(
                uint256(
                    keccak256(
                        abi.encodePacked(
                            block.timestamp,
                            block.difficulty,
                            _t,
                            _a,
                            _c,
                            SEED_NONCE
                        )
                    )
                ) % 5555
            );

            currentHash = string(
                abi.encodePacked(currentHash, rarityGen(_randinput, i))
            );
        }

        if (hashToMinted[currentHash]) return hash(_t, _a, _c + 1);

        return currentHash;
    }

   

    /**
     * @dev Mint internal, this is to avoid code duplication.
     */
    function mintInternal(address to, uint256 _quantity) internal {
        uint256 _totalSupply = totalSupply();
        require(totalSupply().add(_quantity) <= MAX_SUPPLY, "Public Sale Exceeded");
        require(_quantity > 0,"Quantity must be greater then zero");
        require(!Oven.isContract(to));

        
        for (uint256 i = 0; i < _quantity; i++) {
             uint256 thisTokenId = _totalSupply + i;
             tokenIdToHash[thisTokenId] = hash(thisTokenId, to, 0);
             hashToMinted[tokenIdToHash[thisTokenId]] = true;
             _mint(to, thisTokenId);
              emit Minted(to, thisTokenId);
        }
    }



    /**
     * @dev Mints new tokens.
     */
    function bakePizza(uint256 _quantity) public payable {
        require( msg.value >= PUBLIC_MINT_PRICE * _quantity, "Payment too low");
        require(totalSupply().add(_quantity) <= MAX_SUPPLY - RESERVED, "Public Sale Exceeded");
        require(balanceOf(_msgSender()) <= 2,"Max mints per wallet = 3");
        require( !_paused, "Sale paused");
            return mintInternal(_msgSender(), _quantity);
    }
    
    /**
     * @dev Private Mint.
     */
    function privateBake() public {
        require( 1 <= RESERVED, "Exceeds reserved supply" );
        require( totalSupply().add(1) <= MAX_SUPPLY, "Supply Exceeded");
        require(isWhitelisted(_msgSender()) == true, "Not Whitelisted");
        _techTaliaWhitelist[_msgSender()] = false;
        RESERVED -= 1;
        return mintInternal(_msgSender(), 1);
    }
    


    /*
 ____     ___   ____  ___        _____  __ __  ____     __ ______  ____  ___   ____   _____
|    \   /  _] /    ||   \      |     ||  |  ||    \   /  ]      ||    |/   \ |    \ / ___/
|  D  ) /  [_ |  o  ||    \     |   __||  |  ||  _  | /  /|      | |  ||     ||  _  (   \_ 
|    / |    _]|     ||  D  |    |  |_  |  |  ||  |  |/  / |_|  |_| |  ||  O  ||  |  |\__  |
|    \ |   [_ |  _  ||     |    |   _] |  :  ||  |  /   \_  |  |   |  ||     ||  |  |/  \ |
|  .  \|     ||  |  ||     |    |  |   |     ||  |  \     | |  |   |  ||     ||  |  |\    |
|__|\_||_____||__|__||_____|    |__|    \__,_||__|__|\____| |__|  |____|\___/ |__|__| \___|
                                                                                           
*/

   
   /**
     * @dev Helper function to reduce pixel size within contract
     */
    function letterToNumber(string memory _inputLetter)
        internal
        view
        returns (uint8)
    {
        for (uint8 i = 0; i < LETTERS.length; i++) {
            if (Oven.compareStrings((LETTERS[i]),_inputLetter))
               return (i);
        }
        revert();
    }
    
    
    function isWhitelisted(address _from) public view returns (bool) {
        return _techTaliaWhitelist[_from];
    }
    
    /**
     * @dev Hash to SVG function
     */
    function hashToSVG(string memory _hash)
        public
        view
        returns (string memory)
    {
        string memory svgString;
        string memory bgRect;
        string memory basisRect;
        string memory crustRect;
        string memory crustColor;
        
        
        bgRect = string(
                    abi.encodePacked(
                        '<rect class="bg" fill="#',
                        traitTypes[0][Oven.parseInt(Oven.substring(_hash, 0, 1))].pixels,
                        '"/>'
                    )
                );
                
        basisRect = string(
                    abi.encodePacked(
                        '<circle fill="#',
                        traitTypes[1][Oven.parseInt(Oven.substring(_hash, 1, 2))].pixels,
                        '" cx="12" cy="12" r="9.9" />'
                    )
                );
                
        
             
        if(Oven.compareStrings(traitTypes[2][Oven.parseInt(Oven.substring(_hash, 2, 3))].traitName, "Thick")){  
             crustColor = string(
                    abi.encodePacked('c',
                    Oven.substring(traitTypes[3][Oven.parseInt(Oven.substring(_hash, 3, 4))].pixels, 14, 16)
                    )
                );
                
            crustRect = string(
                    abi.encodePacked(
                            '<rect x="0" y="9" class="a p"/><rect x="1" y="7" class="b o"/><rect x="2" y="5" class="c n"/><rect x="3" y="4" class="d m"/><rect x="4" y="3" class="e l"/><rect x="5" y="2" class="f k"/><rect x="7" y="1" class="g j"/><rect x="9" y="0" class="h i"/>'
                    )
                );
            }
        
        for (uint8 i = 2; i < 8; i++) {
            uint8 thisTraitIndex = Oven.parseInt(
                Oven.substring(_hash, i, i + 1)
            );

            for (
                uint16 j = 0;
                j < traitTypes[i][thisTraitIndex].pixelCount;
                j++
            ) {
                
                string memory thisPixel;
                
                if(j <= traitTypes[i][thisTraitIndex].pixelCount - 1){
                 thisPixel = Oven.substring(
                    traitTypes[i][thisTraitIndex].pixels,
                    j * 4,
                    j * 4 + 4
                );
                

                uint8 x = letterToNumber(
                    Oven.substring(thisPixel, 0, 1)
                );
                uint8 y = letterToNumber(
                    Oven.substring(thisPixel, 1, 2)
                );

                
                svgString = string(
                    abi.encodePacked(
                        svgString,
                        "<rect class='c",
                        Oven.substring(thisPixel, 2, 4),
                        "' x='",
                        x.toString(),
                        "' y='",
                        y.toString(),
                        "'/>"
                    )
                );

                }
            }
        
        }

        svgString = string(
            abi.encodePacked(
                '<svg id="pizza-svg" class="',
                crustColor,
                '" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin meet" viewBox="0 0 48 48">',
                bgRect,
                '<g transform="matrix(1 0 0 1 12 12)">',
                crustRect,
                basisRect,
                svgString,
                '</g><style>rect {width: 1px;height: 1px;}#pizza-svg {shape-rendering: crispedges;}.bg {width: 100%;height: 100%}.z{fill:#590a01}..c00{fill:#c2d7cf}.c01{fill:#aabeb6}.c02{fill:#ed8126}.c03{fill:#ecf69e}.c04{fill:#708848}.c05{fill:#efffd4}.c06{fill:#b45230}.c07{fill:#d36d4a}.c08{fill:#63363a}.c09{fill:#6a160a}.c10{fill:#903e20}.c11{fill:#b6742f}.c12{fill:#d59049}.c13{fill:#590a01}.c14{fill:#6b2e09}.c15{fill:#915a1f}.c16{fill:#2e2e2e}.c17{fill:#393939}.c18{fill:#171717}.c19{fill:#232323}.c20{fill:#acbfd3}.c21{fill:#b8cce0}.c22{fill:#d1d9da}.c23{fill:#98a9b4}.c24{fill:#a3b2c4}.c25{fill:#c5a51f}.c26{fill:#e5c339}.c27{fill:#856900}.c28{fill:#744e00}.c29{fill:#9e8412}.c30{fill:#dfc6a7}.c31{fill:#ecd2b2}.c32{fill:#d0c4c2}.c33{fill:#bfa897}.c34{fill:#cfbba0}.c35{fill:#373737}.c36{fill:#837554}.c37{fill:#9f906e}.c38{fill:#3a241b}.c39{fill:#483925}.c40{fill:#665a3e}.c41{fill:#466930}.c42{fill:#779a61}.c43{fill:#fffae1}.c44{fill:#311500}.c45{fill:#2d2a26}.c46{fill:#821003}.c47{fill:#4e1e03}.c48{fill:#864420}.c49{fill:#862520}.c50{fill:#ff7244}.c51{fill:#9a4025}.c52{fill:#db8e2e}.c53{fill:#efca66}.c54{fill:#7c1f1f}.c55{fill:#9a4126}.c56{fill:#b85805}.c57{fill:#ce402a}.c58{fill:#b75704}.c59{fill:#7e1d13}.c60{fill:#620f06}.c61{fill:#e4bc0e}.c62{fill:#e4d20e}.c63{fill:#91112c}.c64{fill:#ba193a}.c65{fill:#d7f66a}.c66{fill:#f6ea6a}.c67{fill:#2d7717}.c68{fill:#366028}.c69{fill:#e6d7d7}.c70{fill:#c7bfac}.c71{fill:#131913}.c72{fill:#c74833}.c73{fill:#e99a8c}',
                '.a{width:24px}.b{width:22px}.c{width:20px}.d{width:18px}.e{width:16px}.f{width:14px}.g{width:10px}.h{width:6px}.i{height:24px}.j{height:22px}.k{height:20px}.l{height:18px}.m{height:16px}.n{height:14px}.o{height:10px}.p{height:6px}',
                '</style></svg>'
         )
        );
        

        return svgString;
    }



    /**
     * @dev Hash to metadata function
     */
    function hashToMetadata(string memory _hash)
        public
        view
        returns (string memory)
    {
        string memory metadataString;

        for (uint8 i = 0; i < 9; i++) {
            uint8 thisTraitIndex = Oven.parseInt(
                Oven.substring(_hash, i, i + 1)
            );

            metadataString = string(
                abi.encodePacked(
                    metadataString,
                    '{"trait_type":"',
                    traitTypes[i][thisTraitIndex].traitType,
                    '","value":"',
                    traitTypes[i][thisTraitIndex].traitName,
                    '"}'
                )
            );

            if (i != 8)
                metadataString = string(abi.encodePacked(metadataString, ","));
        }

        return string(abi.encodePacked("[", metadataString, "]"));
    }

    /**
     * @dev Returns the SVG and metadata for a token Id
     * @param _tokenId The tokenId to return the SVG and metadata for.
     */
    function tokenURI(uint256 _tokenId)
        public
        view
        override
        returns (string memory)
    {
        require(_exists(_tokenId));

        string memory tokenHash = _tokenIdToHash(_tokenId);

        return
            string(
                abi.encodePacked(
                    "data:application/json;base64,",
                    Oven.encode(
                        bytes(
                            string(
                                abi.encodePacked(
                                    '{"name": "Pizza #',
                                    Oven.toString(_tokenId),
                                    '", "description": "Delicious pizzas baked with love and kept warm on chain. All the metadata and images are generated and stored 100% on-chain. No IPFS, no API. Just the Ethereum blockchain.", "image": "data:image/svg+xml;base64,',
                                    Oven.encode(
                                        bytes(hashToSVG(tokenHash))
                                    ),
                                    '","attributes":',
                                    hashToMetadata(tokenHash),
                                    "}"
                                )
                            )
                        )
                    )
                )
            );
        
        
    }

    /**
     * @dev Returns a hash for a given tokenId
     * @param _tokenId The tokenId to return the hash for.
     */
    function _tokenIdToHash(uint256 _tokenId)
        public
        view
        returns (string memory)
    {
        string memory tokenHash = tokenIdToHash[_tokenId];
        return tokenHash;
    }

    /**
     * @dev Returns the wallet of a given wallet. Mainly for ease for frontend devs.
     * @param _wallet The wallet to get the tokens of.
     */
    function walletOfOwner(address _wallet)
        public
        view
        returns (uint256[] memory)
    {
        uint256 tokenCount = balanceOf(_wallet);

        uint256[] memory tokensId = new uint256[](tokenCount);
        for (uint256 i; i < tokenCount; i++) {
            tokensId[i] = tokenOfOwnerByIndex(_wallet, i);
        }
        return tokensId;
    }

    /*

  ___   __    __  ____     ___  ____       _____  __ __  ____     __ ______  ____  ___   ____   _____
 /   \ |  |__|  ||    \   /  _]|    \     |     ||  |  ||    \   /  ]      ||    |/   \ |    \ / ___/
|     ||  |  |  ||  _  | /  [_ |  D  )    |   __||  |  ||  _  | /  /|      | |  ||     ||  _  (   \_ 
|  O  ||  |  |  ||  |  ||    _]|    /     |  |_  |  |  ||  |  |/  / |_|  |_| |  ||  O  ||  |  |\__  |
|     ||  `  '  ||  |  ||   [_ |    \     |   _] |  :  ||  |  /   \_  |  |   |  ||     ||  |  |/  \ |
|     | \      / |  |  ||     ||  .  \    |  |   |     ||  |  \     | |  |   |  ||     ||  |  |\    |
 \___/   \_/\_/  |__|__||_____||__|\_|    |__|    \__,_||__|__|\____| |__|  |____|\___/ |__|__| \___|
                                                                                                     


    */

    /**
     * @dev Clears the traits.
     */
    function clearTraits() public onlyOwner {
        for (uint256 i = 3; i < 8; i++) {
            delete traitTypes[i];
        }
    }