contract PixelWars is ERC721A, ERC721ABurnable, Ownable, PaymentSplitter {

    using Strings for uint256;

    // The total tokens minted to an address. Does not matter if tokens are transferred out
    mapping(address => uint256) public addressMintCount;
    mapping(address => bool) public addressOGClaimed;

    string public constant PROVENANCE = "ba42f1552194b6a712f0b47e6ad4b70b"; // MD5-hashed IPFS hash for provenance

    string public baseTokenURI; // Can be combined with the tokenId to create the metadata URI
    bool public publicSaleActive = false; // Non-admin users can only mint new tokens when this flag is set to true
    bool public allowlistSaleActive = false; // If set to true, allowlisted users will be allowed to mint
    bool public allowBurn = false; // Admin toggle for allowing the burning of tokens
    uint256 public constant MINT_PRICE = 0.03 ether; // Public mint price
    uint256 public constant ALLOWLIST_MINT_PRICE = 0.03 ether; // Mint price for allowlisted addresses only
    uint256 public constant MAX_TOTAL_SUPPLY = 7000; // The maximum total supply of tokens
    uint256 public constant MAX_MINT_COUNT = 10; // The maximum number of tokens any one address can mint
    uint256 public constant MAX_WL_MINT_COUNT = 2; // The maximum number of tokens a whitelisted address can mint
    bytes32 public wlRoot; // The merkle tree root. Used for verifying allowlist addresses
    bytes32 public ogRoot; // Merkle root for OG list

    uint256 private constant MAX_TOKEN_ITERATIONS = 40; // Used to prevent out-of-gas errors when looping

    event SetBaseURI(address _from);
    event Withdraw(address _from, address _to, uint amount);
    event TogglePublicSale(address _from, bool isActive);
    event ToggleAllowlistSale(address _from, bool isActive);
    event ToggleAllowBurn(bool isAllowed);

    constructor(string memory _baseUri, bytes32 _WLmerkleroot, bytes32 _OGmerkleroot, address[] memory _payees, uint256[] memory _shares) ERC721A("Pixel Wars", "PXLWRS") PaymentSplitter(_payees, _shares) {
        baseTokenURI = _baseUri;
        wlRoot = _WLmerkleroot;
        ogRoot = _OGmerkleroot;
    }

    // Allows the contract owner to update the merkle root (allowlist)
    function setWLMerkleRoot(bytes32 _WLmerkleroot) external onlyOwner {
        wlRoot = _WLmerkleroot;
    }

    // Allows the contract owner to update the merkle root (allowlist)
    function setOGMerkleRoot(bytes32 _OGmerkleroot) external onlyOwner {
        ogRoot = _OGmerkleroot;
    }

    // Allows the contract owner to set a new base URI string
    function setBaseURI(string calldata _baseURI) external onlyOwner {
        baseTokenURI = _baseURI;
        emit SetBaseURI(msg.sender);
    }

    // Overrides the tokenURI function so that the base URI can be returned
    function tokenURI(uint256 _tokenId) public view override returns (string memory) {
        string memory baseURI = baseTokenURI;
        return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, _tokenId.toString())) : "";
    } 

    function mint(uint256 _amount) external payable {
        uint256 supply = totalSupply();
        uint256 mintCount = addressMintCount[msg.sender];
        require(publicSaleActive, "Public sale is not yet active");
        require(_amount > 0, "Mint amount can't be zero");
        require(mintCount + _amount <= MAX_MINT_COUNT, "Exceeded max mint count");
        require(supply + _amount <= MAX_TOTAL_SUPPLY, "Max mint supply has been reached");
        require(_amount * MINT_PRICE == msg.value, "Check mint price");

        addressMintCount[msg.sender] = mintCount + _amount;
			
        _safeMint(msg.sender, _amount);
    }

    // Only accessible by the contract owner. This function is used to mint tokens for the team.
    function ownerMint(uint256 _amount, address _recipient) external onlyOwner {
        uint256 supply = totalSupply();
        require(_amount > 0, "Mint amount can't be zero");
        require(_amount <= MAX_TOKEN_ITERATIONS, "You cannot mint this many in one transaction"); // Used to avoid OOG errors.
        require(supply + _amount <= MAX_TOTAL_SUPPLY, "Max supply is reached");
        
        _safeMint(_recipient, _amount);
    }

    // Minting function for addresses on the allowlist only
    function mintAllowList(uint256 _amount, bytes32[] calldata _proof) external payable {
        uint256 supply = totalSupply();
        uint256 mintCount = addressMintCount[msg.sender];
        require(_verify(_leaf(msg.sender), _proof, wlRoot), "Wallet not on allowlist");
        require(mintCount + _amount <= MAX_WL_MINT_COUNT, "Exceeded whitelist allowance.");
        require(allowlistSaleActive, "Allowlist sale is not active");
        require(_amount > 0, "Mint amount can't be zero");
        require(_amount <= MAX_TOKEN_ITERATIONS, "You cannot mint this many in one transaction."); // Used to avoid OOG errors
        require(supply + _amount <= MAX_TOTAL_SUPPLY, "Max supply is reached");
        require(_amount * ALLOWLIST_MINT_PRICE == msg.value, "Incorrect price");

        addressMintCount[msg.sender] = mintCount + _amount;
        
        _safeMint(msg.sender, _amount);
    }

    // Minting function addresses on the OG list only
    function mintOGList(bytes32[] calldata _proof) external {
        uint256 supply = totalSupply();
        require(!addressOGClaimed[msg.sender], "Free mint already claimed");
        require(_verify(_leaf(msg.sender), _proof, ogRoot), "Wallet not on OG list");
        require(allowlistSaleActive, "OG sale is not active");
        require(supply + 1 <= MAX_TOTAL_SUPPLY, "Max supply is reached");

        addressOGClaimed[msg.sender] = true;

        // _safeMint's second argument now takes in a quantity, not a tokenId.
        _safeMint(msg.sender, 1);
    }

    // An owner-only function which toggles the public sale on/off
    function togglePublicSale() external onlyOwner {
        publicSaleActive = !publicSaleActive;
        emit TogglePublicSale(msg.sender, publicSaleActive);
    }

    // An owner-only function which toggles the allowlist sale on/off
    function toggleAllowlistSale() external onlyOwner {
        allowlistSaleActive = !allowlistSaleActive;
        emit ToggleAllowlistSale(msg.sender, allowlistSaleActive);
    }

    // An owner-only function which toggles the allowBurn variable
    function toggleAllowBurn() external onlyOwner {
        allowBurn = !allowBurn;
        emit ToggleAllowBurn(allowBurn);
    }

    // Used to construct a merkle tree leaf
    function _leaf(address _account)
    internal pure returns (bytes32)
    {
        return keccak256(abi.encodePacked(_account));
    }

    // Verifies a leaf is part of the tree
    function _verify(bytes32 leaf, bytes32[] memory _proof, bytes32 _root) pure
    internal returns (bool)
    {
        return MerkleProof.verify(_proof, _root, leaf);
    }

    // Overrides the ERC721A burn function
    function burn(uint256 _tokenId) public virtual override {
        require(allowBurn, "Burning is not currently allowed");
        _burn(_tokenId, true);
    }
}
function toggleAllowlistSale() external onlyOwner {
    allowlistSaleActive = !allowlistSaleActive;
    emit ToggleAllowlistSale(msg.sender, allowlistSaleActive);
}

// An owner-only function which toggles the allowlist sale on/off