contract AnimeAztecs is ERC721, Ownable {
  using Strings for uint256;
 
  string public baseURI;
  mapping(address => uint256) public freeMintWhitelist;
  uint256 public cost = 0.033 ether;
  uint256 public maxSupply = 3780;
  uint256 public partialSupply = 300;
  uint256 public maxMintAmount = 20;
  uint256 public totalSupply = 0;
  bool public paused = false;
 
  constructor(
    string memory _initBaseURI
  ) ERC721("AnimeAztecs", "AZTECS") {
    setBaseURI(_initBaseURI);
    for (uint256 i = 1; i <= 10; i++) {
      _safeMint(msg.sender, totalSupply + i);
    }
    totalSupply += 10;
  }
 
  // internal
  function _baseURI() internal view virtual override returns (string memory) {
    return baseURI;
  }
 
  // modifiers
  modifier onlyFreeMint() {
    require(freeMintWhitelist[msg.sender] > 0);
    _;
  }
 
  // public
  function mint(address _to, uint256 _mintAmount) payable public {
    require(!paused);
    require(_mintAmount > 0);
    require(_mintAmount <= maxMintAmount);
    require(totalSupply + _mintAmount <= maxSupply);
    require(totalSupply + _mintAmount <= partialSupply);   
 
    if (msg.sender != owner()) {
        require(msg.value >= cost * _mintAmount);
    }
    
    for (uint256 i = 1; i <= _mintAmount; i++) {
      _safeMint(_to, totalSupply + i);
    }
    totalSupply += _mintAmount;
  }
 
  function freeMint(address _to, uint256 _mintAmount) public onlyFreeMint() {
    require(!paused);
    require(_mintAmount > 0);
    require(totalSupply + _mintAmount <= maxSupply);
    require(totalSupply + _mintAmount <= partialSupply); 
    require(_mintAmount <= freeMintWhitelist[_to]);
 
    for (uint256 i = 1; i <= _mintAmount; i++) {
      _safeMint(_to, totalSupply + i);
      freeMintWhitelist[_to] = freeMintWhitelist[_to] - 1;
    }
    totalSupply += _mintAmount;
  }
 
  function walletOfOwner(address _owner) public view returns (uint256[] memory) {
    uint256 ownerTokenCount = balanceOf(_owner);
    uint256[] memory ownedTokenIds = new uint256[](ownerTokenCount);
    uint256 currentTokenId = 1;
    uint256 ownedTokenIndex = 0;

    while (ownedTokenIndex < ownerTokenCount && currentTokenId <= maxSupply) {
      address currentTokenOwner = ownerOf(currentTokenId);
      if (currentTokenOwner == _owner) {
        ownedTokenIds[ownedTokenIndex] = currentTokenId;
        ownedTokenIndex++;
      }
      currentTokenId++;
    }
    return ownedTokenIds;
  }
 
  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()))
        : "";
  }
 
  //only owner
  function addFreeMint(address addr, uint256 amount) onlyOwner public returns(bool success) {
    freeMintWhitelist[addr] = amount;
    success = true;
  }
 
  function addFreeMints(address[] memory addrs, uint256 amount) onlyOwner public returns(bool success) {
    for (uint256 i = 0; i < addrs.length; i++) {
      if (addFreeMint(addrs[i], amount)) {
        success = true;
      }
    }    
  }
 
  function setCost(uint256 _newCost) public onlyOwner() {
    cost = _newCost;
  }
 
  function setPartialSupply(uint256 _newPartialSupply) public onlyOwner() {
    partialSupply = _newPartialSupply;
  }
 
  function setmaxMintAmount(uint256 _newmaxMintAmount) public onlyOwner() {
    maxMintAmount = _newmaxMintAmount;
  }
 
  function setBaseURI(string memory _newBaseURI) public onlyOwner {
    baseURI = _newBaseURI;
  }
 
  function pause(bool _state) public onlyOwner {
    paused = _state;
  }
 
  function withdraw() public onlyOwner {
    (bool success, ) = payable(msg.sender).call{value: address(this).balance}("");
    require(success);
  }
}
function addFreeMint(address addr, uint256 amount) onlyOwner public returns(bool success) {
  freeMintWhitelist[addr] = amount;
  success = true;
}

//only owner