// Contract: Pizza
// Target function: function bakePizza(uint256 _quantity) public payable
 address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(isContract(target), "Address: delegate call to non-contract");

        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly

                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}





/**
 * @dev Wrappers over Solidity's arithmetic operations.
 *
 * NOTE: `SafeMath` is no longer needed starting with Solidity 0.8. The compiler
 * now has built in overflow checking.
 */
library SafeMath {
  function add(uint256 a, uint256 b) internal pure returns (uint256) {
    uint256 c = a + b;
    assert(c >= a);
    return c;
  }
}


/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        // Inspired by OraclizeAPI's implementation - MIT licence
        // 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);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        if (value == 0) {
            return "0x00";
        }
        uint256 temp = value;
        uint256 length = 0;
        while (temp != 0) {
            length++;
            temp >>= 8;
        }
        return toHexString(value, length);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _HEX_SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }
}




/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
// abstract contract ReentrancyGuard {
//     // Booleans are more expensive than uint256 or any type that takes up a full
//     // word because each write operation emits an extra SLOAD to first read the
//     // slot's contents, replace the bits taken up by the boolean, and then write
//     // back. This is the compiler's defense against contract upgrades and
//     // pointer aliasing, and it cannot be disabled.

//     // The values being non-zero value makes deployment a bit more expensive,
//     // but in exchange the refund on every call to nonReentrant will be lower in
//     // amount. Since refunds are capped to a percentage of the total
//     // transaction's gas, it is best to keep them low in cases like this one, to
//     // increase the likelihood of the full refund coming into effect.
//     uint256 private constant _NOT_ENTERED = 1;
//     uint256 private constant _ENTERED = 2;

//     uint256 private _status;

//     constructor() {
//         _status = _NOT_ENTERED;
//     }

//     /**
//      * @dev Prevents a contract from calling itself, directly or indirectly.
//      * Calling a `nonReentrant` function from another `nonReentrant`
//      * function is not supported. It is possible to prevent this from happening
//      * by making the `nonReentrant` function external, and make it call a
//      * `private` function that does the actual work.
//      */
//     modifier nonReentrant() {
//         // On the first call to nonReentrant, _notEntered will be true
//         require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

//         // Any calls to nonReentrant after this point will fail
//         _status = _ENTERED;

//         _;

//         // By storing the original value once again, a refund is triggered (see
//         // https://eips.ethereum.org/EIPS/eip-2200)
//         _status = _NOT_ENTERED;
//     }
// }


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() {
        _setOwner(_msgSender());
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
        _;
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _setOwner(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");
        _setOwner(newOwner);
    }

    function _setOwner(address newOwner) private {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}







contract Pizza is ERC721Enumerable, Ownable {

    using SafeMath for uint256;

    uint256 public constant PUBLIC_MINT_PRICE = 30000000000000000; //0.03 ETH
    using Oven for uint8;

    struct Trait {
        string traitName;
        string traitType;
        string pixels;
        uint256 pixelCount;
    }

    //string arrays
    string[] LETTERS = [
    "a",
    "b",
    "c",
    "d",
    "e",
    "f",
    "g",
    "h",
    "i",
    "j",
    "k",
    "l",
    "m",
    "n",
    "o",
    "p",
    "q",
    "r",
    "s",
    "t",
    "u",
    "v",
    "w",
    "x",
    "y",
    "z"
    ];

    //Mappings
    mapping(uint256 => Trait[]) public traitTypes;
    mapping(string => bool) hashToMinted;
    mapping(uint256 => string) internal tokenIdToHash;
    mapping(address => bool) private _privateSaleWhitelist;

    //uint256s
    uint256 MAX_SUPPLY = 5555;
    uint256 SEED_NONCE = 0;
    uint256 RESERVED = 0;
    
    //bool
    bool public _paused = true;

    // Events
    event Minted(address indexed minter, uint256 indexed tokenId);

    //uint arrays
    uint16[][9] TIERS;

    //address
    address cheethAddress;
    address _owner;

    constructor() ERC721("PizzaOnChain", "PIZZA") {
        _owner = msg.sender;

	
        //bg colors
        traitTypes[0].push(Trait("Aero Blue","Background","C1FFD7",0));
        traitTypes[0].push(Trait("Gold","Background","FECD51",0));
        traitTypes[0].push(Trait("Green Crayola","Background","C6D57E",0));
        traitTypes[0].push(Trait("Light Coral","Background","F16F6F",0));
        traitTypes[0].push(Trait("Maximum Purple","Background","CAB8FF",0));
        traitTypes[0].push(Trait("New York Pink","Background","D57E7E",0));
        traitTypes[0].push(Trait("Opal","Background","A2CDCD",0));
        traitTypes[0].push(Trait("Peach","Background","FFE1AF",0));
        traitTypes[0].push(Trait("Snow","Background","F9F3F3",0));
        traitTypes[0].push(Trait("Uranian Blue","Background","B5DEFF",0));
        
        //Sauce
        traitTypes[1].push(Trait("BBQ","Sauce","714c4b",0));
        traitTypes[1].push(Trait("Gold","Sauce","FECD51",0));
        traitTypes[1].push(Trait("Hot","Sauce","cd7542",0));
        traitTypes[1].push(Trait("Pesto","Sauce","7aa579",0));
        traitTypes[1].push(Trait("Tomato","Sauce","c94f49",0));
        traitTypes[1].push(Trait("White","Sauce","acacac",0));
        
        //Thickness
        traitTypes[2].push(Trait("Thin","Thickness","",0));
        traitTypes[2].push(Trait("Thick","Thickness","",0));
        
        //bg colors
        TIERS[0] = [242, 10, 954, 1007, 94, 775, 629, 333, 1393, 118];
        
        //basis colors
        TIERS[1] = [671, 10, 707, 1321, 2355, 491];
        
        //Thickness
        TIERS[2] = [2777, 2778];
        
        //Crust
        TIERS[3] = [2223, 1165, 473, 10, 256, 406, 1022];
        
        //Cheese
        TIERS[4] = [204, 1451, 550, 1239 ,2111];
        
        //Topping
        TIERS[5] = [240, 670, 507, 780, 1, 555, 1773, 30, 999];
        
        //Vegetable
        TIERS[6] = [258, 249, 302, 1420, 1793, 669, 864];
        
        //Spice
        TIERS[7] = [259, 876, 1977, 1231, 1211, 1];

        //Size
        TIERS[8] = [2663, 222, 2663, 7];        
        
        traitTypes[8].push(Trait("Large","Size","",0));
        traitTypes[8].push(Trait("Massive","Size","",0));
        traitTypes[8].push(Trait("Medium","Size","",0));
        traitTypes[8].push(Trait("Nano","Size","",0));
        
       
    }

  
    /**
     * @dev Converts a digit from 0 - 5555 into its corresponding rarity based on the given rarity tier.
     * @param _randinput The input from 0 - 5555 to use for rarity gen.
     * @param _rarityTier The tier to use.
     */
    function rarityGen(uint256 _randinput, uint8 _rarityTier)
        internal
        view
        returns (string memory)
    {
        uint16 currentLowerBound = 0;
        for (uint8 i = 0; i < TIERS[_rarityTier].length; i++) {
            uint16 thisPercentage = TIERS[_rarityTier][i];
            if (
                _randinput >= currentLowerBound &&
                _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);
        }
    }