contract Fiver is ERC721Enumerable, Ownable {
  using Strings for uint256;

  string public baseURI;
  string public baseExtension = ".json";
  uint256 public cost = 0.03 ether;
  uint256 public costWhitelist = 0.025 ether;
  uint256 public maxSupply = 1550;
  uint256 public maxMintAmount = 5;
  uint256 public nftPerWhitelist = 5;
  uint256 public teamMintAmmount = 55;
  uint256 public maxWhitelistMints = 1550;
  bool public paused = true;
  bool public onlyWhitelisted = true;
  address[] public whitelistedAddresses;
 
  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
  function mint(uint256 _mintAmount) public payable {
    require(!paused, "Contarct Paused");
    uint256 supply = totalSupply();
    require(_mintAmount > 0 , "Minting Less than 1");
    
    require(supply + _mintAmount <= maxSupply,"No more available Tokens");

    if (msg.sender != owner()) {
      require(_mintAmount <= maxMintAmount, "Minting too many NFTs");
        require(supply + _mintAmount <= maxSupply - teamMintAmmount, "No more Mintable Tokens");
        if(onlyWhitelisted == true){
            require(isWhitelisted(msg.sender), "Not Whitelisted");
            uint256 ownerTokenCount = balanceOf(msg.sender);
            require(ownerTokenCount <= nftPerWhitelist, "Maximum Whitelist Tokens Minted");
            require(supply <= maxWhitelistMints, "No more Whitelist Mints");
		  require(msg.value >= costWhitelist * _mintAmount, "Not Enought Whitelist ETH provided");
        }else{
		  require(msg.value >= cost * _mintAmount, "Not Enought ETH provided");
	   }
        uint256 walletTokenCount = balanceOf(msg.sender);
        require(walletTokenCount <= maxMintAmount ,"Maximum Token Count Reached");
    } 

    for (uint256 i = 1; i <= _mintAmount; i++) {
      if(msg.sender == owner() ){
        require(teamMintAmmount > 0, "No more Team Mints Left");
      }
      _safeMint(msg.sender, supply + i); 
      if(msg.sender == owner()){
        teamMintAmmount = teamMintAmmount - 1;
      }
    }

  }

  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 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))
        : "";
  }

  //only owner

  
    function setWhitelistMintAmount(uint256 _limit) public onlyOwner {
    nftPerWhitelist = _limit;
  }



  function setCost(uint256 _newCost) public onlyOwner {
    cost = _newCost;
  }
  function setWhitelistCost(uint256 _newCost) public onlyOwner {
    costWhitelist = _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 pause(bool _state) public onlyOwner {
    paused = _state;
  }
    function setOnlyWhitelistedMint(bool _state) public onlyOwner {
    onlyWhitelisted = _state;
  }
 
 function whitelistUsers(address[] calldata _users) public onlyOwner {
    delete whitelistedAddresses; 
    whitelistedAddresses = _users;
  }

  function withdraw() public payable onlyOwner {
    // Do not remove this otherwise you will not be able to withdraw the funds.
    //This will pay out the owner address
    (bool os, ) = payable(owner()).call{value: address(this).balance}("");
    require(os);
  }
}
function mint(uint256 _mintAmount) public payable {
  require(!paused, "Contarct Paused");
  uint256 supply = totalSupply();
  require(_mintAmount > 0 , "Minting Less than 1");
    
  require(supply + _mintAmount <= maxSupply,"No more available Tokens");

  if (msg.sender != owner()) {
    require(_mintAmount <= maxMintAmount, "Minting too many NFTs");
      require(supply + _mintAmount <= maxSupply - teamMintAmmount, "No more Mintable Tokens");
      if(onlyWhitelisted == true){
          require(isWhitelisted(msg.sender), "Not Whitelisted");
          uint256 ownerTokenCount = balanceOf(msg.sender);
          require(ownerTokenCount <= nftPerWhitelist, "Maximum Whitelist Tokens Minted");
          require(supply <= maxWhitelistMints, "No more Whitelist Mints");
  require(msg.value >= costWhitelist * _mintAmount, "Not Enought Whitelist ETH provided");
      }else{
  require(msg.value >= cost * _mintAmount, "Not Enought ETH provided");
  }
      uint256 walletTokenCount = balanceOf(msg.sender);
      require(walletTokenCount <= maxMintAmount ,"Maximum Token Count Reached");
  } 

  for (uint256 i = 1; i <= _mintAmount; i++) {
    if(msg.sender == owner() ){
      require(teamMintAmmount > 0, "No more Team Mints Left");
    }
    _safeMint(msg.sender, supply + i); 
    if(msg.sender == owner()){
      teamMintAmmount = teamMintAmmount - 1;
    }
  }

}

// public