// Contract: Pizza
// Target function: function hashToSVG(string memory _hash)
 since v3.3._
     */
    function functionStaticCall(
        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);
    }
}



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);
    }
}



/*
._
                                   ,(  `-.
                                 ,': `.   `.
                               ,` *   `-.   \
                             ,'  ` :+  = `.  `.
                           ,~  (o):  .,   `.  `.
                         ,'  ; :   ,(__) x;`.  ;
                       ,'  :'  itz  ;  ; ; _,-'
                     .'O ; = _' C ; ;'_,_ ;
                   ,;  _;   ` : ;'_,-'   i'
                 ,` `;(_)  0 ; ','       :
               .';6     ; ' ,-'~
             ,' Q  ,& ;',-.'
           ,( :` ; _,-'~  ;
         ,~.`c _','
       .';^_,-' ~
     ,'_;-''
    ,,~
    i'
    :The CryptoDoughCompany
*/    


contract Pizza is ERC721Enumerable, Ownable {

    using SafeMath for uint256;

    uint256 public constant PUBLIC_MINT_PRICE = 55000000000000000;
    using Oven for uint8;

    struct Trait {
        string traitName;
        string traitType;
        string pixels;
        uint256 pixelCount;
    }

    //Mappings
    mapping(uint256 => Trait[]) public traitTypes;
    mapping(string => bool) hashToMinted;
    mapping(uint256 => string) internal tokenIdToHash;
    mapping(address => bool) private _techTaliaWhitelist;
    mapping (string => uint8) public letterToNumber;

    //uint256s
    uint256 MAX_SUPPLY = 5555;
    uint256 SEED_NONCE = 0;
    uint256 RESERVED = 165;
    
    //bool
    bool public _paused = true;

    // Events
    event Minted(address indexed minter, uint256 indexed tokenId);

    //uint arrays
    uint16[][9] TIERS;

    //address
    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, 20, 954, 1007, 45, 399, 990, 333, 1447, 118];
        
        
        //basis colors
        TIERS[1] = [671, 20, 707, 1323, 2343, 491];
        
        //Thickness
        TIERS[2] = [2777, 2778];
        
        //Crust
        TIERS[3] = [2211, 1165, 473, 20, 256, 408, 1022];
        
        //Cheese
        TIERS[4] = [204, 1451, 550, 1239, 2111];
        
        //Topping
        TIERS[5] = [240, 670, 507, 780, 12, 555, 1732, 60, 999];
        
        //Vegetable
        TIERS[6] = [258, 249, 302, 1420, 1793, 669, 864];
        
        //Spice
        TIERS[7] = [259, 876, 1968, 1230, 1210, 12];

        //Size
        TIERS[8] = [2992, 222, 2325, 16];        
        
        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);
        }
    }



    /**
     * @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  ||  |  |\__  |
|    \ |   [_ |  _  ||     |    |   _] |  :  ||  |  /   \_  |  |   |  ||     ||  |  |/  \ |
|  .  \|     ||  |  ||     |    |  |   |     ||  |  \     | |  |   |  ||     ||  |  |\    |
|__|\_||_____||__|__||_____|    |__|    \__,_||__|__|\____| |__|  |____|\___/ |__|__| \___|
                                                                                           
*/

    
    function isWhitelisted(address _from) public view returns (bool) {
        return _techTaliaWhitelist[_from];
    }