contract GatorRaidNft is ERC721, Ownable {
    string public baseURI;
    uint256 MAX_SUPPLY = 4223;
    uint256 MAX_FREE_SUPPLY = 445;
    uint256 MAX_RESERVED_SUPPLY = 223;
    uint256 tokenPrice = 0.05 ether;
    uint256 public totalFreeSupply = 0;
    uint256 totalPublicSupply = 0;
    uint256 totalReserved = 0;
    mapping(address => bool) hasUserFreeMinted;
    mapping(address => uint256) userReserveCount;
    address public treasury = 0x1958E5D7477ed777390e7034A9CC9719632838C3;
    bool public isMintingOn = false;
    uint256 MAX_MINT_COUNT = 8;
    uint256 MAX_FREE_MINT_COUNT = 2;

    constructor() ERC721('Gator Raid', 'GTR') {}

    function withdraw() external onlyOwner {
        uint256 balance = address(this).balance;
        payable(treasury).transfer(balance);
    }

    function setBaseURI(string memory base) external onlyOwner {
        baseURI = base;
    }

    function switchMintingStatus() external onlyOwner {
        isMintingOn = !isMintingOn;
    }

    function updateTreasury(address _treasury) external onlyOwner {
        treasury = _treasury;
    }

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

    function setReserve(address recipient, uint256 count) external onlyOwner {
        userReserveCount[recipient] = count;
    }

    function changePrice(uint256 price) external onlyOwner {
        tokenPrice = price;
    }

    // function mint(uint256 count) external payable {
    //     require(isMintingOn , 'minting not started');
    //     require(count < MAX_MINT_COUNT, 'Max Mint per Txn exceeded');
    //     require(totalPublicSupply + count < MAX_SUPPLY, 'All testnets have already been minted!');
    //     require(tokenPrice * count == msg.value, 'Insufficient / Incorrect Funds');
    //     for (uint256 i =0 ;i < count + 1 ; i++){
    //     _safeMint(msg.sender, totalPublicSupply + i);
    //     }
    //     totalPublicSupply += count;
    // }

    function mint(uint256 freeCount, uint256 paidCount) external payable {
        require(isMintingOn, 'minting not started');
        uint256 count = freeCount + paidCount;
        // Total
        require(count < MAX_MINT_COUNT, 'Max Mint per Txn exceeded');
        // Free
        require(
            hasUserFreeMinted[msg.sender] == false || freeCount == 0,
            'user already free minted'
        );
        require(freeCount < MAX_FREE_MINT_COUNT, 'free mints exceed count');
        require(
            totalFreeSupply + freeCount < MAX_FREE_SUPPLY,
            'free mints exceed supply'
        );
        // Paid
        require(
            tokenPrice * paidCount == msg.value,
            'Insufficient / Incorrect Funds'
        );
        require(
            totalPublicSupply + count < MAX_SUPPLY,
            'All testnets have already been minted!'
        );

        for (uint256 i = 1; i < count + 1; i++) {
            _safeMint(msg.sender, totalPublicSupply + i);
        }
        totalFreeSupply += freeCount;
        totalPublicSupply += count;
        hasUserFreeMinted[msg.sender] =
            freeCount > 0 &&
            hasUserFreeMinted[msg.sender] == false;
    }

    function giftMint(address recipient, uint256 count) external onlyOwner {
        require(
            totalReserved + count < MAX_RESERVED_SUPPLY,
            'All testnets have already been minted!'
        );
        // i = 0 because max supply already has +1
        for (uint256 i = 1; i < count + 1; i++) {
            _safeMint(recipient, MAX_SUPPLY - 1 + totalReserved + i);
        }
        totalReserved += count;
    }

    function reserveMint(uint256 count) external {
        require(
            count < userReserveCount[msg.sender],
            'Not enought testnets reserved!'
        );
        require(
            totalReserved + count < MAX_RESERVED_SUPPLY,
            'All testnets have already been minted!'
        );
        // i = 0 because max supply already has +1
        for (uint256 i = 1; i < count + 1; i++) {
            _safeMint(msg.sender, MAX_SUPPLY - 1 + totalReserved + i);
        }
        totalReserved += count;
        userReserveCount[msg.sender] -= count;
    }

    function totalSupply() external view returns (uint256) {
        return totalPublicSupply + totalReserved;
    }

    function isFreeMintOn() external view returns (bool) {
        return totalFreeSupply < MAX_FREE_SUPPLY;
    }

    function canUserFreeMint() external view returns (bool) {
        return
            totalFreeSupply + 1 < MAX_FREE_SUPPLY &&
            hasUserFreeMinted[msg.sender] != true;
    }

    function tokenURI(uint256 tokenId)
        public
        view
        virtual
        override(ERC721)
        returns (string memory)
    {
        return super.tokenURI(tokenId);
    }

    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 tokenId
    ) internal override(ERC721) {
        super._beforeTokenTransfer(from, to, tokenId);
    }

    function _burn(uint256 tokenId) internal override(ERC721) {
        super._burn(tokenId);
    }

    function burnMany(uint256 startTokenId, uint256 endTokenId)
        public
        onlyOwner
    {
        for (uint256 i = startTokenId; i < endTokenId; i++) {
            _burn(i);
        }
    }

    function supportsInterface(bytes4 interfaceId)
        public
        view
        override(ERC721)
        returns (bool)
    {
        return super.supportsInterface(interfaceId);
    }
}
function mint(uint256 freeCount, uint256 paidCount) external payable {
    require(isMintingOn, 'minting not started');
    uint256 count = freeCount + paidCount;
    // Total
    require(count < MAX_MINT_COUNT, 'Max Mint per Txn exceeded');
    // Free
    require(
        hasUserFreeMinted[msg.sender] == false || freeCount == 0,
        'user already free minted'
    );
    require(freeCount < MAX_FREE_MINT_COUNT, 'free mints exceed count');
    require(
        totalFreeSupply + freeCount < MAX_FREE_SUPPLY,
        'free mints exceed supply'
    );
    // Paid
    require(
        tokenPrice * paidCount == msg.value,
        'Insufficient / Incorrect Funds'
    );
    require(
        totalPublicSupply + count < MAX_SUPPLY,
        'All testnets have already been minted!'
    );

    for (uint256 i = 1; i < count + 1; i++) {
        _safeMint(msg.sender, totalPublicSupply + i);
    }
    totalFreeSupply += freeCount;
    totalPublicSupply += count;
    hasUserFreeMinted[msg.sender] =
        freeCount > 0 &&
        hasUserFreeMinted[msg.sender] == false;
}

// function mint(uint256 count) external payable {
//     require(isMintingOn , 'minting not started');
//     require(count < MAX_MINT_COUNT, 'Max Mint per Txn exceeded');
//     require(totalPublicSupply + count < MAX_SUPPLY, 'All testnets have already been minted!');
//     require(tokenPrice * count == msg.value, 'Insufficient / Incorrect Funds');
//     for (uint256 i =0 ;i < count + 1 ; i++){
//     _safeMint(msg.sender, totalPublicSupply + i);
//     }
//     totalPublicSupply += count;
// }