contract SKULLAPEGANG is ERC721Enumerable, Ownable {

  

  using Counters for Counters.Counter;

  using Strings for uint256;



  //Counters

  Counters.Counter internal _count;

  Counters.Counter internal _publicCount;



  string public baseURI;

  string public baseExtension = ".json";



  uint256 public cost = 0.04 ether;

  uint256 public whitelistCost = 0.00 ether;



  uint256 public maxSupply = 8488;

  uint256 public maxWhitelistSupply = 400;

  

  uint256 public maxMintAmount = 10;

  uint256 public nftPerAddressLimit = 1;

  

  bool public paused = false;

  bool public onlyWhitelisted = true;



  mapping(address => uint256) public addressMintedBalance;



  constructor(

    string memory _name,

    string memory _symbol,

    string memory _initBaseURI

  ) ERC721(_name, _symbol) {

    setBaseURI(_initBaseURI);

  }





  // internal

  function _baseURI() internal view virtual override returns (string memory) {

    return baseURI;

  }





  // public sale

  function mint(uint256 _mintAmount) public payable {

    // total nft minted count

     uint256 supply = totalSupply();

     //if owner is not 

      if (msg.sender != owner()) {

            //if pause is enable throw error

            require(!paused, "The contract is paused");

            //if whitelist is enable throw error

            require(!onlyWhitelisted, "You can't mint Pre-sale enable");

            //address mint count

            uint256 trackMintedCount = addressMintedBalance[msg.sender];

            //mint count should be greater than 0

            require(_mintAmount > 0, "Need to mint at least 1 NFT");

            //per transaction limit

            require(_mintAmount <= maxMintAmount, "Max mint amount per session exceeded");

            //max supply limit

            require(_publicCount.current() + _mintAmount <= maxSupply, "Exceeds Max Public Supply");

            //addresss quota limit

            require((trackMintedCount + _mintAmount) <= maxMintAmount,"Your quota exceeded for per address ");

            //mint cost

            require(msg.value >= cost * _mintAmount, "Insuffient funds");

   }

   

             for (uint256 i = 1; i <= _mintAmount; i++) {

                   addressMintedBalance[msg.sender]++;

                  _safeMint(msg.sender, supply + i);

                  _publicCount.increment();

              }

  

  }





    //pre-sale 

    function mintWhitelist( uint256 _mintAmount) public payable    {

        uint256 supply = totalSupply();



       if (msg.sender != owner()) {



        if(onlyWhitelisted == true) {



            require(!paused, "The contract is paused");



            require(_mintAmount > 0, "Need to mint at least 1 NFT");



            uint256 ownerMintedCount = addressMintedBalance[msg.sender];

            require(_count.current() + _mintAmount <= maxWhitelistSupply, "Exceeds Max Whitelist Supply");



            require(ownerMintedCount + _mintAmount <= nftPerAddressLimit, "Max NFT per address exceeded");

           

            require(msg.value >= whitelistCost * _mintAmount, "Insuffient funds");

           

            }  

        }

           for (uint256 i = 1; i <= _mintAmount; i++) {

                   addressMintedBalance[msg.sender]++;

                  _safeMint(msg.sender, supply + i);

                  _count.increment();

               }

    }

      

            

  





  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;

  }



  function tokenURI(uint256 tokenId)

    public

    view

    virtual

    override

    returns (string memory)

  {

    require(

      _exists(tokenId),

      "ERC721Metadata: URI query for nonexistent token"

    );



    string memory currentBaseURI = _baseURI();

    return bytes(currentBaseURI).length > 0

        ? string(abi.encodePacked(currentBaseURI, tokenId.toString(), baseExtension))

        : "";

  }



  function totalWhitelistSupply() public view returns (uint256) {

        return _count.current();

  }



   function totalPublicSupply() public view returns (uint256) {

        return _publicCount.current();

  }



  function remainingWhitelistSupply() public view returns (uint256) {

     return   maxWhitelistSupply - _count.current();

  }

    function remainingPublicSupply() public view returns (uint256) {

     return   maxSupply - _publicCount.current();

  }



  function setNftPerAddressLimit(uint256 _limit) public onlyOwner {

    nftPerAddressLimit = _limit;

  }

  

  function setCost(uint256 _newCost) public onlyOwner {

    cost = _newCost;

  }



  function setwhitelistCost(uint256 _newWhitelistCost) public onlyOwner {

    whitelistCost = _newWhitelistCost;

  }



  function setmaxMintAmount(uint256 _newmaxMintAmount) public onlyOwner {

    maxMintAmount = _newmaxMintAmount;

  }



  function setmaxSupply(uint256 _newmaxSupply) public onlyOwner {

    maxSupply = _newmaxSupply;

  }



  function setmaxWhitelistSupply(uint256 _newmaxWhitelistSupply) public onlyOwner {

    maxWhitelistSupply = _newmaxWhitelistSupply;

  }



  function setBaseURI(string memory _newBaseURI) public onlyOwner {

    baseURI = _newBaseURI;

  }



  function setBaseExtension(string memory _newBaseExtension) public onlyOwner {

    baseExtension = _newBaseExtension;

  }





  function pause(bool _state) public onlyOwner {

    paused = _state;

  }

  

  function setOnlyWhitelisted(bool _state) public onlyOwner {

    onlyWhitelisted = _state;

  }

  

 

  function withdraw() public payable onlyOwner {

    (bool os, ) = payable(owner()).call{value: address(this).balance}("");

    require(os);

    

  }

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

  return baseURI;

}

// internal