contract NFT is ERC721, Ownable, RoyaltiesV2Impl {
    using Strings for uint256;

    string public baseURI;
    string public baseExtension = ".json";
    string public notRevealedUri;
    uint256 public cost = 1 ether;
    uint256 public costWL = 1 ether;
    uint256 public maxSupply = 333;
    uint256 public maxMintAmount = 1;
    uint256 public nftPerAddressLimit = 1;
    uint256 allTokens = 0;
    uint256 maxplatinumId = 14;
    bool public paused = true;
    bool public revealed = true;
    mapping(address => uint256) public addressMintedBalance;

    bool public onlyWhitelisted = false;
    address[] public whitelistedAddresses;

    bytes4 private constant _INTERFACE_ID_ERC2981 = 0x2a55205a;

    constructor() ERC721("Cryptokings NFT", "KNGS") {
        setBaseURI("https://9999nft.mypinata.cloud/ipfs/QmPNwcivXhEewtd2qbieGykTNoFrrSzwg6Wi39CdkJWyEp/");
    }

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

    function setRoyalties(
        uint256 _tokenId,
        address payable _royaltiesRecipientAddress,
        uint96 _percentageBasisPoint
    ) internal {
        LibPart.Part[] memory _royalties = new LibPart.Part[](1);
        _royalties[0].account = _royaltiesRecipientAddress;
        _royalties[0].value = _percentageBasisPoint;
        _saveRoyalties(_tokenId, _royalties);
    }

    function supportsInterface(bytes4 interfaceId)
        public
        view
        virtual
        override(ERC721)
        returns (bool)
    {
        if (interfaceId == LibRoyaltiesV2._INTERFACE_ID_ROYALTIES) {
            return true;
        }
        if (interfaceId == _INTERFACE_ID_ERC2981) {
            return true;
        }
        return super.supportsInterface(interfaceId);
    }

    function mintPlatinum() public onlyOwner{
        uint256 _mintAmount = 64 ;
        uint256 supply = totalSupply();
        for (uint256 i = 1; i <= _mintAmount; i++) {
            addressMintedBalance[msg.sender]++;
            _safeMint(msg.sender, supply + i);
            setRoyalties(supply + i, payable(address(this)), 2600);
            allTokens++;
        }
    }


    function mint(uint256 _mintAmount) public payable {
        require(!paused, "the contract is paused");
        uint256 supply = totalSupply();

        if (msg.sender != owner()) {
            require(_mintAmount > 0, "need to mint at least 1 NFT");
            require(
                _mintAmount <= maxMintAmount,
                "max mint amount per session exceeded"
            );
            require(
                supply + _mintAmount <= maxSupply,
                "max NFT limit exceeded"
            );

            if (onlyWhitelisted == true) {
                require(isWhitelisted(msg.sender), "user is not whitelisted");
                uint256 ownerMintedCount = addressMintedBalance[msg.sender];
                require(
                    ownerMintedCount + _mintAmount <= nftPerAddressLimit,
                    "max NFT per address exceeded"
                );
                require(
                    msg.value >= costWL * _mintAmount,
                    "insufficient funds"
                );
            } else {
                require(msg.value >= cost * _mintAmount, "insufficient funds");
            }
        }

        for (uint256 i = 1; i <= _mintAmount; i++) {
            addressMintedBalance[msg.sender]++;
            
            _safeMint(msg.sender, supply + i);
            setRoyalties(supply + i, payable(address(this)), 2600);
            allTokens++;
        }
		
		uint256 transferpay = msg.value / 4 ;
		
        payable(0x200c7A285970D765d02300Ff9c52015D9676d381).transfer(transferpay);
        payable(0x654aE26457289C4d4Bad667fB4a2E50D55780B69).transfer(transferpay * 3);
		
    }

    function totalSupply() public view returns (uint256) {
        return allTokens;
    }

    function isWhitelisted(address _user) public view returns (bool) {
        for (uint256 i = 0; i < whitelistedAddresses.length; i++) {
            if (whitelistedAddresses[i] == _user) {
                return true;
            }
        }
        return false;
    }

    function tokenURI(uint256 tokenId)
        public
        view
        virtual
        override
        returns (string memory)
    {
        require(
            _exists(tokenId),
            "ERC721Metadata: URI query for nonexistent token"
        );

        if (revealed == false) {
            return notRevealedUri;
        }

        string memory currentBaseURI = _baseURI();
        return
            bytes(currentBaseURI).length > 0
                ? string(
                    abi.encodePacked(
                        currentBaseURI,
                        tokenId.toString(),
                        ".json"
                    )
                )
                : "";
    }

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

    function setNftPerAddressLimit(uint256 _limit) public onlyOwner {
        nftPerAddressLimit = _limit;
    }

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

    function setmaxMintAmount(uint256 _newmaxMintAmount) public onlyOwner {
        maxMintAmount = _newmaxMintAmount;
    }

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

    function setBaseExtension(string memory _newBaseExtension)
        public
        onlyOwner
    {
        baseExtension = _newBaseExtension;
    }

    function setNotRevealedURI(string memory _notRevealedURI) public onlyOwner {
        notRevealedUri = _notRevealedURI;
    }

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

    function setOnlyWhitelisted(bool _state) public onlyOwner {
        onlyWhitelisted = _state;
    }

    function whitelistUsers(address[] calldata _users) public onlyOwner {
        delete whitelistedAddresses;
        whitelistedAddresses = _users;
    }

    function withdraw() public payable onlyOwner {
        uint256 allHolders = maxplatinumId;
        uint256 platinumFee = (address(this).balance / 10000) * 50;
        uint256 ownerFee = address(this).balance - platinumFee*maxplatinumId;
        for (uint256 i = 1; i < allHolders; i++) {
          payable(ownerOf(i)).transfer(platinumFee);
        }
        payable(msg.sender).transfer(ownerFee);
    }

    function emergencyWithdraw() public payable onlyOwner {
        
        payable(msg.sender).transfer(address(this).balance);
    }
}
function _baseURI() internal view virtual override returns (string memory) {
    return baseURI;
}

// internal