// author: hetaodao.eth
pragma solidity ^0.8.4;






contract HETAOMEDAL_1 is ERC721, ERC721URIStorage, Pausable, Ownable, ERC721Burnable {
    using Strings for uint256;
    //Legendary, 30
    //Epic, 150
    //Rare, 750
    //Common, 3750
    enum CardType {Legendary,Epic,Rare,Common,Free}

    uint256 public price = 0;

    string public baseURI;
    string public baseExtension = ".json";

    uint256 private minted_legendary = 0;
    uint256 private minted_epic = 0;
    uint256 private minted_rare = 0;
    uint256 private minted_common = 0;

    uint256 private start_legendary = 0;
    uint256 private start_epic = 30;
    uint256 private start_rare = 180;
    uint256 private start_common = 930;

    uint256 public maxMintAmount = 1;
    uint256 public maxSupply = 4680;

    mapping(address => bool) public addressMintedBalance;

    mapping(address => bool) private whitelisted_legendary;
    mapping(address => bool) private whitelisted_epic;
    mapping(address => bool) private whitelisted_rare;

    constructor() ERC721("HETAODAO1", "HTD1") {
        pause();
    }

    function setBaseURI(string memory _newBaseURI) public onlyOwner {
        baseURI = _newBaseURI;
    }
    
      // internal
    function _baseURI() internal view virtual override returns (string memory) {
        return baseURI;
    }

    function totalSupply() public view returns(uint256) {
        return minted_legendary + minted_epic + minted_rare + minted_common;
    }    


    function mint(uint _mintAmount) public {
        address _user = msg.sender;
        require(_mintAmount > 0);
        require(_mintAmount <= 1);
        require(addressMintedBalance[_user]==false, "max NFT per address exceeded.");
        uint tokenId = 0;

        if (whitelisted_legendary[_user]) {

            tokenId = minted_legendary + start_legendary;
            require(tokenId < start_epic, "max legendary NFT limit exceeded");

        } else if (whitelisted_epic[_user]) {

            tokenId = minted_epic + start_epic;
            require(tokenId < start_rare, "max NFT limit exceeded");

        } else if (whitelisted_rare[_user]) {

            tokenId = minted_rare + start_rare;
            require(tokenId < start_common, "max NFT limit exceeded");

        } else {
            tokenId = minted_common + start_common;
            require(tokenId < maxSupply, "max NFT limit exceeded");

        }
        safeMint(_user, tokenId);
    }

    function adminMint(address to, uint256 tokenId) public onlyOwner {
        safeMint(to, tokenId);
    }

    function safeMint(address to, uint256 tokenId) private whenNotPaused{
        require((tokenId>=0) && (tokenId <4680), "invalid token id");
        _safeMint(to, tokenId);
        addressMintedBalance[to] = true;

        if (tokenId < start_epic) {
            minted_legendary++;
        }
        else if (tokenId < start_rare) {
            minted_epic++;
        }     
        else if (tokenId < start_common) {
            minted_rare++;
        }
        else {
            minted_common++;
        }

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

    function mint_type(address to) public view returns(string memory) {
        if (whitelisted_legendary[to]) {
            return "legendary";
        }
        if (whitelisted_epic[to]) {
            return "epic";
        }
        if (whitelisted_rare[to]) {
            return "rare";
        }
        return "normal";
    }


    function minteCount(CardType cardType) public view returns(uint256) {
        require(cardType < CardType.Free, "wrong card type.");
        if (cardType == CardType.Legendary)
            return minted_legendary;
        else if (cardType == CardType.Epic)
            return minted_epic;
        else if (cardType == CardType.Rare)
            return minted_rare;
        else if (cardType == CardType.Common)
            return minted_common;
        return 0;
    }

    function mintTotal() public view returns(uint256) {
        return minted_legendary + minted_epic + minted_rare + minted_common;
    }


    function removeWhitelistUser(CardType cardType, address _user) public onlyOwner {
        require(cardType<CardType.Common, "wrong card type.");
        if(cardType == CardType.Legendary) {
            whitelisted_legendary[_user] = false;
        }
        else if(cardType == CardType.Epic) {
            whitelisted_epic[_user] = false;
        }
        else if(cardType == CardType.Rare) {
            whitelisted_rare[_user] = false;
        }
    }

    function whitelistAddress (CardType cardType, address[] memory users) external onlyOwner {
        require(cardType<CardType.Common, "wrong card type.");
        require(users.length>0, "at least have 1 address.");

        if(cardType == CardType.Legendary) {
            for (uint i = 0; i < users.length; i++) {
                whitelisted_legendary[users[i]] = true;
            }
        }
        else if(cardType == CardType.Epic) {
            for (uint i = 0; i < users.length; i++) {
                whitelisted_epic[users[i]] = true;
            }
        }
        else if(cardType == CardType.Rare) {
            for (uint i = 0; i < users.length; i++) {
                whitelisted_rare[users[i]] = true;
            }
        }
    }

    function isWithlistAdress(CardType cardType, address _user) public view returns(bool) {
        require(cardType<CardType.Common, "wrong card type.");
        if(cardType == CardType.Legendary) {
            return whitelisted_legendary[_user];
        }
        else if(cardType == CardType.Epic) {
            return whitelisted_epic[_user];
        }
        else if(cardType == CardType.Rare) {
            return whitelisted_rare[_user];
        }
        return false;
    }

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

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

    function _beforeTokenTransfer(address from, address to, uint256 tokenId)
        internal
        whenNotPaused
        override
    {
        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);

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

    function withdraw() public payable onlyOwner {
        (bool success, ) = payable(msg.sender).call{value: address(this).balance}("");
        require(success);
    }
}
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"addressMintedBalance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"adminMint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseExtension","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum HETAOMEDAL_1.CardType","name":"cardType","type":"uint8"},{"internalType":"address","name":"_user","type":"address"}],"name":"isWithlistAdress","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxMintAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_mintAmount","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"mintTotal","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"mint_type","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum HETAOMEDAL_1.CardType","name":"cardType","type":"uint8"}],"name":"minteCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"price","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum HETAOMEDAL_1.CardType","name":"cardType","type":"uint8"},{"internalType":"address","name":"_user","type":"address"}],"name":"removeWhitelistUser","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_newBaseURI","type":"string"}],"name":"setBaseURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum HETAOMEDAL_1.CardType","name":"cardType","type":"uint8"},{"internalType":"address[]","name":"users","type":"address[]"}],"name":"whitelistAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"payable","type":"function"}]