contract She is ERC721Enum, Ownable, PaymentSplitter {
    using Strings for uint256;

    uint256 public constant MAX_SUPPLY = 1164;
    uint256 public constant MAX_PRESALE_PER_TX = 10;
    uint256 public constant MAX_PRESALE_PER_WALLET = 50;
    uint256 public constant MAX_PUBLIC_SALE_PER_TX = 10;
    uint256 public constant MAX_PUBLIC_PER_WALLET = 50;
    uint256 public constant COST = 0.05 ether;
    bytes32 private merkleRoot;
    bool public presaleOnlyActive = true;
    bool public paused = true;
    bool public revealed = false;
    string baseURI;
    string hiddenURI;
    
    mapping(address => uint256) public whitelistAddressMintedBalance;
    mapping(address => uint256) public publicAddressMintedBalance;

    address[] private _splitterAddressList = [
        0x948CD796Db88b64a55f7070cBF3db77017314873, //ian
        0x596DC4Bb87c9D7dc4e227906b2fd6651AE2E6FD9, //fidel
        0xF012B5afD31d8aa22B3B97740e41731E3e30f7d8  //adam
    ];

    uint256[] private _shareList = [12, 44, 44];

    constructor(string memory _hiddenURI, bytes32 _merkleRoot) ERC721P("She", "SHE") PaymentSplitter(_splitterAddressList, _shareList) {
        setHiddenURI(_hiddenURI);
        merkleRoot = _merkleRoot;
    }

    modifier mintCheck(uint256 _mintAmount) {
        uint256 supply = totalSupply();
        require(_mintAmount > 0, "Mint amt must be greater than 0");
        require(supply + _mintAmount <= MAX_SUPPLY, "Mint amt exceeds max supply");
        require(msg.value >= COST * _mintAmount, "Insufficient funds");
        _;
    }

    //---------------- Internal ----------------

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

    function _leaf(address _account) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked(_account));
    }

    function _verifyLeaf(bytes32 _leafNode, bytes32[] memory _proof) internal view returns (bool) {
        return MerkleProof.verify(_proof, merkleRoot, _leafNode);
    }

    function _doMint(address _receiver, uint256 _mintAmount) internal {
        uint256 supply = totalSupply();
        for (uint256 i = 0; i < _mintAmount; i++) {
            _safeMint(_receiver, supply + i);
        }
    }

    //---------------- Public/External ----------------

    function whitelistMint(uint256 _mintAmount, bytes32[] calldata proof) external payable mintCheck(_mintAmount) {
        require(presaleOnlyActive && !paused, "Presale is currently not active");
        require(_verifyLeaf(_leaf(msg.sender), proof), "Invalid proof");
        require(_mintAmount <= MAX_PRESALE_PER_TX, "Mint amt greater than max per tx");

        uint256 senderMintedCount = whitelistAddressMintedBalance[msg.sender];
        require(senderMintedCount + _mintAmount <= MAX_PRESALE_PER_WALLET, "Total mints after tx exceeds max mints for this address");
        whitelistAddressMintedBalance[msg.sender] += _mintAmount;
        
        _doMint(msg.sender, _mintAmount);
    }

    function publicMint(uint256 _mintAmount) external payable mintCheck(_mintAmount) {
        require(!presaleOnlyActive && !paused, "Public sale is not active");
        require(!Address.isContract(msg.sender), "Contract to contract minting not allowed");
        require(msg.sender == tx.origin, "Sender must be origin wallet");
        require(_mintAmount <= MAX_PUBLIC_SALE_PER_TX, "Mint amt greater than max per tx");
        
        uint256 senderMintedCount = publicAddressMintedBalance[msg.sender]; 
        require(senderMintedCount + _mintAmount <= MAX_PUBLIC_PER_WALLET, "Total mints after tx exceeds max mints for this address.");
        publicAddressMintedBalance[msg.sender] += _mintAmount;

        _doMint(msg.sender, _mintAmount);
    }

    function walletOfOwner(address _owner) public view returns (uint256[] memory) {
        uint256 ownerTokenCount = balanceOf(_owner);
        uint256[] memory ownedTokenIds = new uint256[](ownerTokenCount);
        uint256 currentTokenId = 1;
        uint256 ownedTokenIndex = 0;

        while (ownedTokenIndex < ownerTokenCount && currentTokenId <= MAX_SUPPLY) {
            address currentTokenOwner = ownerOf(currentTokenId);
            if (currentTokenOwner == _owner) {
                ownedTokenIds[ownedTokenIndex] = currentTokenId;
                ownedTokenIndex++;
            }
            currentTokenId++;
        }
        return ownedTokenIds;
    }

    function tokenURI(uint256 _tokenId) public view virtual override returns (string memory) {
        require(_exists(_tokenId), "ERC721Metadata: TokenID does not exist.");
        if (revealed == false) {
            return hiddenURI;
        }
        string memory currentBaseURI = _baseURI();
        return bytes(currentBaseURI).length > 0 ? string(abi.encodePacked(currentBaseURI, _tokenId.toString(), ".json")) : "";
    }

    //---------------- Only Owner ----------------

    function teamReserveMint(uint256 _mintAmount, address _receiver) public onlyOwner {
        uint256 supply = totalSupply();
        require(supply + _mintAmount <= MAX_SUPPLY, "Mint amt exceeds max supply");
        _doMint(_receiver, _mintAmount);
    }

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

    function setHiddenURI(string memory _hiddenURI) public onlyOwner {
        hiddenURI = _hiddenURI;
    }

    function pause(bool _state) public onlyOwner {
        paused = _state;
    }

    function reveal() public onlyOwner {
        revealed = true;
    }

    function whitelistMintOnly(bool _state) public onlyOwner {
        presaleOnlyActive = _state;
    }

    function setMerkleRoot(bytes32 _root) public onlyOwner {
        merkleRoot = _root;
    }
}
function whitelistMint(uint256 _mintAmount, bytes32[] calldata proof) external payable mintCheck(_mintAmount) {
    require(presaleOnlyActive && !paused, "Presale is currently not active");
    require(_verifyLeaf(_leaf(msg.sender), proof), "Invalid proof");
    require(_mintAmount <= MAX_PRESALE_PER_TX, "Mint amt greater than max per tx");

    uint256 senderMintedCount = whitelistAddressMintedBalance[msg.sender];
    require(senderMintedCount + _mintAmount <= MAX_PRESALE_PER_WALLET, "Total mints after tx exceeds max mints for this address");
    whitelistAddressMintedBalance[msg.sender] += _mintAmount;
        
    _doMint(msg.sender, _mintAmount);
}

//---------------- Public/External ----------------