contract Pupverse is ERC721Enumerable, Ownable {

  using Strings for uint256;
  string public baseURI;
  string public baseExtension = ".json";
  string public notRevealedURI;
  address[] public WhitelistedWallets;
  bool public isWhitelistSales = true;
  bool public RevealedActive = false;
  bool public isPauseMode = true;
  uint256 public PriceNFT = 0.08 ether;
  uint256 public WhitelistPrice = 0.03 ether;
  uint256 public MaxToken = 10000;
  uint256 public nftPerWallet = 10;
  mapping(address => uint256) public WalletMintedBalance;

  constructor(string memory _initBaseURI,string memory _initNotRevealedURI)
    ERC721("Pupverse", "PUPVERSE") 
    {
    Uri_Base(_initBaseURI);
    Uri_NotRevealed(_initNotRevealedURI);
  }

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

  //MINT
  function mint(uint256 _mintAmount) public payable {
    require(!isPauseMode, "Pause Mode ON");
    uint256 supply = totalSupply();
    require(_mintAmount > 0, "Select 1 NFT");  
    require(supply + _mintAmount <= MaxToken, "Collecton Sold");   
   if (msg.sender != owner()) {
        if (isWhitelistSales == true) {
            require(isAddressWhitelisted(msg.sender), "Wallet not Whitelisted");
            uint256 ownerMintedCount = WalletMintedBalance[msg.sender];
            require(ownerMintedCount + _mintAmount <= nftPerWallet, "Whitelist Sales: Max NFT per Wallet Reached");
            require(msg.value >= WhitelistPrice * _mintAmount, "Whitelist Sales: Balance Insufficient");
        }
        else {
            require(msg.value >= PriceNFT * _mintAmount, "Main Sale: Balance Insufficient"); 
        }
    }

    for (uint256 i = 1; i <= _mintAmount; i++) {
      WalletMintedBalance[msg.sender]++;
      _safeMint(msg.sender, supply + i);
    }
  }
  
   function isAddressWhitelisted(address _user) public view returns (bool) {
    for (uint i = 0; i < WhitelistedWallets.length; i++) {
      if (WhitelistedWallets[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(RevealedActive == false) {
        return notRevealedURI;
    }
    string memory currentBaseURI = _baseURI();
      return bytes(currentBaseURI).length > 0
        ? string(abi.encodePacked(currentBaseURI, tokenId.toString(), baseExtension))
        : "";
  }

  function walletOfOwner(address _owner) public view returns (uint256[] memory)
  {
    uint256 ownerTokenCount = balanceOf(_owner);

    uint256[] memory tokenIds = new uint256[](ownerTokenCount);
    for (uint256 i; i < ownerTokenCount; i++) {
      tokenIds[i] = tokenOfOwnerByIndex(_owner, i);
    }
    return tokenIds;
  }

  //OWNER
  function RevealMode() public onlyOwner {
    RevealedActive = true;
  }

  function addWhitelistedWallets(address[] calldata _users) public onlyOwner {
    delete WhitelistedWallets;
    WhitelistedWallets = _users;
  }

  function isPauseModebtn(bool _state) public onlyOwner {
    isPauseMode = _state;
  }

  function isWhitelistSalesbtn(bool _state) public onlyOwner {
    isWhitelistSales = _state;
  }

  function MainPrice(uint256 _newPriceNFT) public onlyOwner {
    PriceNFT = _newPriceNFT;
  }

  function WhitelistPricebtn(uint256 _newWhitelistSalesPrice) public onlyOwner {
    WhitelistPrice = _newWhitelistSalesPrice;
  }

  function Uri_Base(string memory _newBaseURI) public onlyOwner {
    baseURI = _newBaseURI;
  }
  
  function Uri_NotRevealed(string memory _notRevealedURI) public onlyOwner {
    notRevealedURI = _notRevealedURI;
  }
 
  function withdraw() public payable onlyOwner {

    (bool pv, ) = payable(owner()).call{value: address(this).balance}("");
    require(pv);
  }
}
function _baseURI() internal view virtual override returns (string memory) {
  return baseURI;
}

//INT