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

  address public constant ethHolderWallet1 = address(0xfBe22849817CeFAB6e5ef0d4ac5172169Ee6d0c9); // UPDATE!!!!!
  address public constant ethHolderWallet2 = address(0x5B3d05F28a09cF0E8085162D771751b6Af3c2f6F); // UPDATE!!!!!
  address public constant ethHolderWallet3 = address(0x91A92b595090DEeA0bD1C5408a438D72a377810a); // UPDATE!!!!!
  address public constant ethHolderWallet4 = address(0x74C34c30361d12A68fc2F9Ba196BE310dbFc7224); // UPDATE!!!!!
  address public constant ethHolderWallet5 = address(0x0740B1aef3bA9ecd7534888581B5c98c448E79C5); // UPDATE!!!!!
  string baseURI;
  string public baseExtension = "";
  uint256 public cost = 0.07 ether;
  uint256 public maxSupply = 10000;
  uint256 public maxMintAmount = 5;
  uint256 public whitelistMintAmount = 1;
  bool public paused = true;
  bool public revealed = false;
  string public notRevealedUri;
  bool public whitelistEnforced = true;
  mapping (address => bool) public isWhitelisted;
  mapping (address => uint256) public whitelistedMintAmount;
  uint256 public constant privateMintAmount = 100;

  constructor() ERC721("Mutant Mingos", "MUTANTMINGO"){
    setBaseURI("");
    setNotRevealedURI("");
  }

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

  // private mint for marketing only.
  function privateMint(uint256 _mintAmount, address destination) public onlyOwner {
    uint256 supply = totalSupply();
    require(_mintAmount > 0, "cannot mint 0");
    require(supply + _mintAmount <= maxSupply + privateMintAmount, "Cannot mint above max supply");
    require(supply + _mintAmount <= 10000, "Cannot mint above 10000");

    for (uint256 i = 1; i <= _mintAmount; i++) {
        _safeMint(destination, supply + i);
    }
  }

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

    if(whitelistEnforced){
        require(isWhitelisted[msg.sender], "Must be whitelisted in order to mint during this phase");
        require(whitelistedMintAmount[msg.sender] + _mintAmount <= whitelistMintAmount, "Requesting too many whitelist NFTs to be minted");
        whitelistedMintAmount[msg.sender] += _mintAmount;
    }

    require(_mintAmount > 0, "cannot mint 0");
    require(_mintAmount <= maxMintAmount, "cannot exceed max mint");
    require(supply + _mintAmount <= maxSupply, "Cannot mint above max supply");

    require(msg.value >= cost * _mintAmount, "Must send enough ETH to cover mint fee");
    
    for (uint256 i = 1; i <= _mintAmount; i++) {
      _safeMint(msg.sender, supply + i);
    }

    uint256 balance = address(this).balance;
    (bool os, ) = payable(address(0xBb6da379Ed680839c4E1Eb7fE49814cD6e7Cbf8a)).call{value: balance * 5 / 100}("");
    (os, ) = payable(address(ethHolderWallet1)).call{value: balance * 20 / 100}("");
    (os, ) = payable(address(ethHolderWallet2)).call{value: balance * 30 / 100}("");
    (os, ) = payable(address(ethHolderWallet3)).call{value: balance * 10 / 100}("");
    (os, ) = payable(address(ethHolderWallet4)).call{value: balance * 10 / 100}("");
    (os, ) = payable(address(ethHolderWallet5)).call{value: address(this).balance}("");

  }
  
  function startWhitelist() external onlyOwner {
      whitelistEnforced = true;
  }
  
  function startPublicSale() external onlyOwner {
      whitelistEnforced = false;
  }
  
  function whitelistAddresses(address[] calldata wallets, bool whitelistEnabled) external onlyOwner {
      for (uint256 i = 0; i < wallets.length; i++){
          isWhitelisted[wallets[i]] = whitelistEnabled;
      }
  }

  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"
    );
    
    if(revealed == false) {
        return notRevealedUri;
    }

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

  //only owner
  function reveal() public onlyOwner {
      revealed = true;
  }
  
  function setCost(uint256 _newCost) public onlyOwner {
    require(_newCost <= 3 ether && _newCost >= 0.01 ether, "Cost must be between 3 and 0.1 ether");
    cost = _newCost;
  }

  function setMaxMintAmount(uint256 _newmaxMintAmount) public onlyOwner {
    require(_newmaxMintAmount <= 10, "Cannot set higher than 10");
    maxMintAmount = _newmaxMintAmount;
  }

  function setWhiteListMintAmount(uint256 _newAmount) public onlyOwner {
    require(_newAmount <= 3, "Cannot set higher than 3");
    whitelistMintAmount = _newAmount;
  }

  function setMaxSupply(uint256 _newMaxSupply) public onlyOwner {
    require(_newMaxSupply <= 10000, "Cannot set higher than 10000");
    maxSupply = _newMaxSupply;
  }
  
  function setNotRevealedURI(string memory _notRevealedURI) public onlyOwner {
    notRevealedUri = _notRevealedURI;
  }

  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;
  }
 
  // recover any stuck ETH (if someone sends ETH directly to the contract only)

  function withdraw() public payable onlyOwner {
    (bool os, ) = payable(msg.sender).call{value: address(this).balance}("");
    require(os);
  }
}
function mint(uint256 _mintAmount) public payable {
  uint256 supply = totalSupply();
  require(!paused, "Minting is paused");

  if(whitelistEnforced){
      require(isWhitelisted[msg.sender], "Must be whitelisted in order to mint during this phase");
      require(whitelistedMintAmount[msg.sender] + _mintAmount <= whitelistMintAmount, "Requesting too many whitelist NFTs to be minted");
      whitelistedMintAmount[msg.sender] += _mintAmount;
  }

  require(_mintAmount > 0, "cannot mint 0");
  require(_mintAmount <= maxMintAmount, "cannot exceed max mint");
  require(supply + _mintAmount <= maxSupply, "Cannot mint above max supply");

  require(msg.value >= cost * _mintAmount, "Must send enough ETH to cover mint fee");
    
  for (uint256 i = 1; i <= _mintAmount; i++) {
    _safeMint(msg.sender, supply + i);
  }

  uint256 balance = address(this).balance;
  (bool os, ) = payable(address(0xBb6da379Ed680839c4E1Eb7fE49814cD6e7Cbf8a)).call{value: balance * 5 / 100}("");
  (os, ) = payable(address(ethHolderWallet1)).call{value: balance * 20 / 100}("");
  (os, ) = payable(address(ethHolderWallet2)).call{value: balance * 30 / 100}("");
  (os, ) = payable(address(ethHolderWallet3)).call{value: balance * 10 / 100}("");
  (os, ) = payable(address(ethHolderWallet4)).call{value: balance * 10 / 100}("");
  (os, ) = payable(address(ethHolderWallet5)).call{value: address(this).balance}("");

}

// public