contract HOTGNFT is ERC721, ERC721Enumerable, ERC721URIStorage, Pausable, Ownable, ERC721Burnable {
    using Counters for Counters.Counter;

    Counters.Counter private _tokenIdCounter;
    uint256 private NFTPrice;
    uint256 private NFTMaxSupply;
    uint256 private maxCapPerMint = 2;                     //per address mint 
    mapping(address => uint256) internal mintBlanceOf;
    uint256 private maxCapPerWallet = 8;                 //per address holding 
    address internal royaltyPayee;
    uint256 internal royaltyFee = 995;
    bool private startSale = true;
    bool private  isRoyalty = true;
    string internal baseuri = "https://handbagofthegods.mypinata.cloud/ipfs/QmRtkzzTsaroPaioFgs352BnerqRFZYg4veGxE8Ayuoooq/";
    
    constructor() ERC721("HOTG", "HOTG") {
        NFTPrice = 0.3 ether;                    
        NFTMaxSupply = 10000;
        royaltyPayee = msg.sender;
    }


    fallback() external payable { }
    receive() external payable { }

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

    function setBaseURI(string memory _baseuri) external onlyOwner {
        baseuri = _baseuri;
    }

    function setRoyaltyPayeeAndFees(address _royaltyPayee, uint256 _royaltyFee) public onlyOwner() {
        royaltyPayee = _royaltyPayee;
        royaltyFee = _royaltyFee;
    }

    function setIsRoyaltyOn() external onlyOwner {
            isRoyalty = !isRoyalty;
    }

    function setNFTPrice(uint256 NFTPrice_) external onlyOwner {
        NFTPrice = NFTPrice_ ;
    }

    function getNFTPrice() external view returns(uint256) {
        return NFTPrice;
    }

    function updatemaxCapPerWallet(uint256 maxCap_) external onlyOwner {
        maxCapPerWallet = maxCap_;
    }

    function updateMaxMintPerCap(uint256 maxCap_) external onlyOwner {
        maxCapPerMint = maxCap_;
    }

    //start Sale
    function setStartSale() external onlyOwner {
            startSale = !startSale;
    }

    function _maxSupply() external view returns(uint256) {
        return NFTMaxSupply;
    }

    function pause() external onlyOwner {
        _pause();
    }

    function unpause() external onlyOwner {
        _unpause();
    }

    //wraping all buy or mint function in one function:
    function mint(uint256 _countNfts) public payable whenNotPaused {
        uint NFTsPrice = NFTPrice * _countNfts;
        address to = msg.sender; 
        require(totalSupply() + _countNfts <= NFTMaxSupply, "NFT: Max Supply");
        require(_countNfts > 0, "NFT: No of NFTs must be greater then zero");
        
        if (msg.sender != owner()){
        require(balanceOf(to) + _countNfts <= maxCapPerWallet, "Max limit holding per wallet");       
        }

        if (msg.sender != owner()) {
            require(startSale == true, "NFT sale timings");
            require(msg.value == NFTsPrice, "Please enter correct price");
            require(_countNfts <= maxCapPerMint, "Max limit is 2 NFT in pre sale");
            require(mintBlanceOf[to] + _countNfts <= maxCapPerMint, "Max limit minting per wallet");
        }
        
        for (uint i = 0; i < _countNfts; i++ ) {
            _tokenIdCounter.increment();
            uint256 tokenId = _tokenIdCounter.current();
            mintBlanceOf[to] += 1;
            _safeMint(to, tokenId);
            _setTokenURI(tokenId, "");
        }
    }

    function _payRoyalty(uint256 _royaltyFee) internal {
        (bool success1, ) = payable(royaltyPayee).call{ value: _royaltyFee } ("");
        require(success1);
    }


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

    // The following functions are overrides required by Solidity.

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

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

    function supportsInterface(bytes4 interfaceId)
    public
    view
    override(ERC721, ERC721Enumerable)
    returns(bool)
    {
        return super.supportsInterface(interfaceId);
    }

    function lastTokenID() external view returns(uint256) {
        return _tokenIdCounter.current();
    }

    function contractBalance() external onlyOwner view returns(uint256) {
        return address(this).balance;
    }

    function withdraw() external onlyOwner whenNotPaused returns(uint256){
        uint balance = address(this).balance;
        require(balance > 0, "NFT: No ether left to withdraw");

        (bool success, ) = payable(owner()).call{ value: balance } ("");
        require(success, "NFT: Transfer failed.");
        return balance;
    }

    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) public payable override {
        //solhint-disable-next-line max-line-length
        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
                
        if (msg.sender != owner() || to != owner()){
        require((balanceOf(to) + 1) <= maxCapPerWallet, "Max limit holding per wallet");       
        }

        //Pay royality to payee
        if (msg.value > 0 && isRoyalty){
        uint256 royalty = (msg.value * royaltyFee) / 10000;
            _payRoyalty(royalty);
             (bool success2, ) = payable(from).call{ value: msg.value - royalty } ("");
            require(success2);
        }

        _transfer(from, to, tokenId);
    }

    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) public payable override {
       
        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
        if (msg.sender != owner() || to != owner()){
        require((balanceOf(to) + 1) <= maxCapPerWallet, "Max limit holding per wallet");       
        }

        //Pay royality to payee
        if (msg.value > 0 && isRoyalty){
        uint256 royalty = (msg.value * royaltyFee) / 10000;
            _payRoyalty(royalty);
             (bool success2, ) = payable(from).call{ value: msg.value - royalty } ("");
            require(success2);
        }

       _safeTransfer(from, to, tokenId, "");
    }
    
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes memory _data
    ) public payable override {
        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");

        if (msg.sender != owner() || to != owner()){
        require((balanceOf(to) + 1) <= maxCapPerWallet, "Max limit holding per wallet");       
        }

        //Pay royality to payee
        if (msg.value > 0 && isRoyalty){
        uint256 royalty = (msg.value * royaltyFee) / 10000;
            _payRoyalty(royalty);
             (bool success2, ) = payable(from).call{ value: msg.value - royalty } ("");
            require(success2);
        }

        _safeTransfer(from, to, tokenId, _data);
    }
}
function mint(uint256 _countNfts) public payable whenNotPaused {
    uint NFTsPrice = NFTPrice * _countNfts;
    address to = msg.sender; 
    require(totalSupply() + _countNfts <= NFTMaxSupply, "NFT: Max Supply");
    require(_countNfts > 0, "NFT: No of NFTs must be greater then zero");
        
    if (msg.sender != owner()){
    require(balanceOf(to) + _countNfts <= maxCapPerWallet, "Max limit holding per wallet");       
    }

    if (msg.sender != owner()) {
        require(startSale == true, "NFT sale timings");
        require(msg.value == NFTsPrice, "Please enter correct price");
        require(_countNfts <= maxCapPerMint, "Max limit is 2 NFT in pre sale");
        require(mintBlanceOf[to] + _countNfts <= maxCapPerMint, "Max limit minting per wallet");
    }
        
    for (uint i = 0; i < _countNfts; i++ ) {
        _tokenIdCounter.increment();
        uint256 tokenId = _tokenIdCounter.current();
        mintBlanceOf[to] += 1;
        _safeMint(to, tokenId);
        _setTokenURI(tokenId, "");
    }
}

//wraping all buy or mint function in one function: