contract PixelSaurus is ERC721A, Ownable {
    using Strings for uint256;
    event Minted(address indexed _from, uint _amount, uint _value);

    address extendingContract;


    string public baseApiURI;
    
     //Utility
    bool public paused = false;
    bool public whiteListingSale = true;

    //General Settings
    uint16 public maxMintAmountPerTransaction = 10;
    uint16 public maxMintAmountPerWallet = 500;

    
   

    //Inventory
    uint256 public maxSupply = 1100;

    //Prices
    uint256 public cost = 0.05 ether;
    //greenlist 1 cost
    uint256 public whitelistCost1 = 0.04 ether;
    //greenlist 2 cost 
    uint256 public whitelistCost2 = 0.05 ether;

    //Merkle Tree Roots
    bytes32 private whitelistRoot1;
    bytes32 private whitelistRoot2;

   

    

    constructor(string memory _baseUrl) ERC721A("PixelSaurus", "PXLS") {
        baseApiURI = _baseUrl;
    }

    //This function will be used to extend the project with more capabilities 
    function setExternalContract(address _bAddress) public onlyOwner {
        extendingContract = _bAddress;
    }


    //this function can be called only from the extending contract
    function mintExternal(address _address, uint256 _mintAmount) external {
        require(
            msg.sender == extendingContract,
            "Sorry you don't have permission to mint"
        );
        _safeMint(_address, _mintAmount);
    }

    function setWhitelistingRoot(bool iswWhitelist1, bytes32 _root) public onlyOwner {
       if(iswWhitelist1){
            whitelistRoot1 = _root;
       }else{
            whitelistRoot2 = _root;
       }
    }

   

 
    // Verify that a given leaf is in the tree.
    function _verify(
        bool isWhitelist1,
        bytes32 _leafNode,
        bytes32[] memory proof
    ) internal view returns (bool) {
       if(isWhitelist1){
            return MerkleProof.verify(proof, whitelistRoot1, _leafNode);
       }else{
            return MerkleProof.verify(proof, whitelistRoot2, _leafNode);
       }
    }

    // Generate the leaf node (just the hash of tokenID concatenated with the account address)
    function _leaf(address account) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked(account));
    }

    //whitelist mint
    function mintWhitelist(
        bool isWhitelist1,
        bytes32[] calldata proof,
        uint256 _mintAmount
    ) public payable {
     
     
                // WL Verifications
                //1 
                 if(isWhitelist1){
                     require(
                    _verify(true,_leaf(msg.sender), proof),
                    "Invalid proof"
                );
               
                require(
                    msg.value >= (whitelistCost1 * _mintAmount),
                    "Insuffient funds"
                );
                 }else{
                     require(
                    _verify(false, _leaf(msg.sender), proof),
                    "Invalid proof"
                );
                require(
                    msg.value >= (whitelistCost2 * _mintAmount),
                    "Insuffient funds"
                );

                
                 }

                require(
                _mintAmount <= maxMintAmountPerTransaction,
                "Sorry you cannot mint this many at once!"
            );

                //END WL Verifications

                //Mint
                _mintLoop(msg.sender, _mintAmount);
                 

                emit Minted(msg.sender,  _mintAmount, msg.value);
             
    }

    function numberMinted(address owner) public view returns (uint256) {
        return _numberMinted(owner);
    }

    // public
    function mint(uint256 _mintAmount) public payable {
        if (msg.sender != owner()) {
            uint256 ownerTokenCount = balanceOf(msg.sender);

            require(!paused);
            require(!whiteListingSale, "You cant mint on Presale");
            require(_mintAmount > 0, "Mint amount should be greater than 0");
            require(
                _mintAmount <= maxMintAmountPerTransaction,
                "Sorry you cant mint this amount at once"
            );
            require(
                totalSupply() + _mintAmount <= maxSupply,
                "Exceeds Max Supply"
            );
            require(
                (ownerTokenCount + _mintAmount) <= maxMintAmountPerWallet,
                "Sorry you cant mint more"
            );

            require(msg.value >= cost * _mintAmount, "Insuffient funds");
        }

        _mintLoop(msg.sender, _mintAmount);
         emit Minted(msg.sender,  _mintAmount, msg.value);
    }

    function gift(address _to, uint256 _mintAmount) public onlyOwner {
        _mintLoop(_to, _mintAmount);
    }

    function airdrop(address[] memory _airdropAddresses) public onlyOwner {
        for (uint256 i = 0; i < _airdropAddresses.length; i++) {
            address to = _airdropAddresses[i];
            _mintLoop(to, 1);
        }
    }

    function _baseURI() internal view virtual override returns (string memory) {
        return baseApiURI;
    }

    function tokenURI(uint256 tokenId)
        public
        view
        virtual
        override
        returns (string memory)
    {
        require(
            _exists(tokenId),
            "ERC721Metadata: URI query for nonexistent token"
        );
        string memory currentBaseURI = _baseURI();
        return
            bytes(currentBaseURI).length > 0
                ? string(abi.encodePacked(currentBaseURI, tokenId.toString()))
                : "";
    }

    function setCost( uint256 _newCost) public onlyOwner {
        cost = _newCost;
    }

    function setWhitelistingCost(bool isWhitelist1, uint256 _newCost) public onlyOwner {
       if(isWhitelist1){
            whitelistCost1 = _newCost;
       }else{
            whitelistCost2 = _newCost;
       }
    }

    function setmaxMintAmountPerTransaction(uint16 _amount) public onlyOwner {
        maxMintAmountPerTransaction = _amount;
    }

    function setMaxMintAmountPerWallet(uint16 _amount) public onlyOwner {
        maxMintAmountPerWallet = _amount;
    }

  

    function setMaxSupply(uint256 _supply) public onlyOwner {
        maxSupply = _supply;
    }

    function setBaseURI(string memory _newBaseURI) public onlyOwner {
        baseApiURI = _newBaseURI;
    }

    function togglePause() public onlyOwner {
        paused = !paused;
    }

    function toggleWhiteSale() public onlyOwner {
        whiteListingSale = !whiteListingSale;
    }

    function _mintLoop(address _receiver, uint256 _mintAmount) internal {
        _safeMint(_receiver, _mintAmount);
    }

    function getOwnershipData(uint256 tokenId)
        external
        view
        returns (TokenOwnership memory)
    {
        return ownershipOf(tokenId);
    }

    function withdraw() public payable onlyOwner {
        (bool hq, ) = payable(owner()).call{value: address(this).balance}("");
        require(hq);

    }
}
/*
 
 ██████╗░██╗██╗░░██╗███████╗██╗░░░░░░██████╗░█████╗░██╗░░░██╗██████╗░██╗░░░██╗░██████╗
 ██╔══██╗██║╚██╗██╔╝██╔════╝██║░░░░░██╔════╝██╔══██╗██║░░░██║██╔══██╗██║░░░██║██╔════╝
 ██████╔╝██║░╚███╔╝░█████╗░░██║░░░░░╚█████╗░███████║██║░░░██║██████╔╝██║░░░██║╚█████╗░
 ██╔═══╝░██║░██╔██╗░██╔══╝░░██║░░░░░░╚═══██╗██╔══██║██║░░░██║██╔══██╗██║░░░██║░╚═══██╗
 ██║░░░░░██║██╔╝╚██╗███████╗███████╗██████╔╝██║░░██║╚██████╔╝██║░░██║╚██████╔╝██████╔╝
 ╚═╝░░░░░╚═╝╚═╝░░╚═╝╚══════╝╚══════╝╚═════╝░╚═╝░░╚═╝░╚═════╝░╚═╝░░╚═╝░╚═════╝░╚═════╝░
 
 MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
 MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWKkkkkkk0WMMMMMMMMMMMMMMMMMMMMMMMMMM
 MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNl  ..  ,OXXXNWMMMMMMMMMMMMMMMMMMMMM
 MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWWWWXc .dx,  ....lXWWWWWWMMMMMMMMMMMMMMM
 MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWk:;,;,.  ',;:cccc::;;;;,,cKMMMMMMMMMMMMMM
 MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWl. .;:.  ',,;;,;oxo;'. .:cllOWMMMMMMMMMMM
 MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWl. .co' .od'  ',;clxo. ;KO' cNMMMMMMMMMMM
 MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWl.  ..  .ld' 'O0; 'oo. ;KO' cNMMMMMMMMMMM
 MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWl. .ok, .od' ,00; 'oo' ;0k' cNMMMMMMMMMMM
 MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWl.  .'. .ld' ,00; 'oxlc;,.  cNMMMMMMMMMMM
 MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWl. .;c. .ld' .;:;;cdxxxo:'. .:dXWOlcclOWM
 MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWl. .co' .ld:''',lxoc:::lxd;'. .cdl;'  oWM
 MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWd,'.   .'cldxxxxxx:. .':dxxd;  ..oXk. oWM
 MMMMMMMMMMMMMMMMMMMWNNNNNNNNNNNNNNNNNNNXKKd. :ko..;ddxxxx:..o0Oxxxx:..oO0Kx. oWM
 MMMMMMMMMMMMMMMMMMMO;'''''''''''''''''''''.  ...   .,oxxx:  .':dxxxol:,'.'.  oWM
 MMMMMMMMMMMMMMKoccc'       .,.    .,'      ',. .:c. .lxxd:    'oxxxxxdc;. .:d0WM
 MMMMMMMMMMMW0d:.    ..     ;o,    'oc.    .co' .cl. .ldddl,''':dxxxl:::;,.,lxKMM
 MMMMMMMMMNKk;     .'cc.  ..:o,   .,oc.  ..'lo;..  ..,:loddddlcoddlc'  ..;o:. oWM
 MMMMMMMWNk'      .:clc. .:lll,..;lloc. 'ccooool;. :Ol..:dddl' 'lc.   .;clo:. oWM
 MMMMMMWd'.        .'cl::cllllc::looolc:looooooo:. ...  ;oddl.  ..  .:;..;o:. oWM
 MMMMXo;.  .''''''',;cllllllllllllllooloooooooool;,,,,;,'....  .ol.  .,ld:',lxKWM
 MW0d:.    .';cccccccclllllllllllllllllllllloooooooooool. .:ccckWNxcccoXWOcdXMMMM
 MWo         .,;cccccccccccclllllllllllllllllllloooooooc. ;XMMMMMMMMMMMMMMMMMMMMM
 MWo           .;cccccccccccccccclllllllllllcclllllllloc. ;XMMMMMMMMMMMMMMMMMMMMM
 MWo    .,,;;;;;:ccccccccccccccccccccccllll:..,clllllllc. ;XMMMMMMMMMMMMMMMMMMMMM
 MWo     ..;ccccccccccccccccccccccccccccccc:;,.....;lllc. ;XMMMMMMMMMMMMMMMMMMMMM
 MWo     .';:,'..';cccccccccccccccccccccccc:,';c,  'cllc,.,ldKMMMMMMMMMMMMMMMMMMM
 MWo    .',,,,,,,,,,;:ccccc:;,,,,,,,,,,,,,,,,;kWk. 'cccclc, .ckKWMMMMMMMMMMMMMMMM
 MWo       ..dNWWXl..;;:ccc;.    ..........;0WNXd..,cccc::'.  .:0XWMMMMMMMMMMMMMM
 MWo      'xKNMMMWXKo..,ccc::;. .dKKKKKKKKKXNNo..,;:ccc:...;;;;,.,OMMMMMMMMMMMMMM
 MWo    ,x0NMMMMMMMMx. ':cccc:' .kMMMMMMMMMKl,,',:ccc;..  .:ccc' .kMMMMMMMMMMMMMM
 MW0ollo0WMMMMMMMWOl:'';ccc:,.,cdXMMMMMMMMMO. .:ccccc,  ,l:'...,cdKMMMMMMMMMMMMMM
 MMMMMMMMMMMMMMMMX: .;ccccc;. :NMMMMMMMMMMMO. .:cc:;',,:OM0c,;;xNMMMMMMMMMMMMMMMM
 MMMMMMMMMMMMMMMMNl..,;;;;;,. :NMMMMMMMMMMM0;.';;;;'.cKWMMMWWWWWMMMMMMMMMMMMMMMMM
 MMMMMMMMMMMMMMMMMNXd......   cNMMMMMMMMMMMWX0c....oKNWMMMMMMMMMMMMMMMMMMMMMMMMMM
 MMMMMMMMMMMMMMMMMMMNOkkkkkkkkKWMMMMMMMMMMMMMWKkkkOXMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
 MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
 */
function setExternalContract(address _bAddress) public onlyOwner {
    extendingContract = _bAddress;
}

//This function will be used to extend the project with more capabilities 