contract Wicked is ERC721A, Ownable, ReentrancyGuard {
  using Counters for Counters.Counter;
  using SafeMath for uint256;
  uint256 private _mintCost;
  uint256 private _maxSupply;
  bool private _isPublicMintEnabled;
  uint256 private _freeSupply;
  
  string private _tokenBaseURI = "ipfs://QmVQpiCuQmURdqwpVqCij7K62kkcvUyTLCn7FjnA2YRxtw/";


  /**
  * @dev Initializes the contract setting the `tokenName` and `symbol` of the nft, `cost` of each mint call, and maximum `supply` of the nft.
  * Note: `cost` is in wei. 
  */

   constructor() ERC721A("Wicked Gorilla Warriors", "WGW") Ownable() {
    _mintCost = 0.03 ether;
    _maxSupply = 1337;
    _isPublicMintEnabled = false;
    _freeSupply = 500;
  }

  /**
  * @dev Changes contract state to enable public access to `mintTokens` function
  * Can only be called by the current owner.
  */
  function allowPublicMint()
  public
  onlyOwner{
    _isPublicMintEnabled = true;
  }

  /**
  * @dev Changes contract state to disable public access to `mintTokens` function
  * Can only be called by the current owner.
  */
  function denyPublicMint()
  public
  onlyOwner{
    _isPublicMintEnabled = false;
  }

  /**
  * @dev Mint `count` tokens if requirements are satisfied.
  * 
  */
  function mintTokens(uint256 count)
  public
  payable
  nonReentrant{
    require(_isPublicMintEnabled, "Mint disabled");
    require(count > 0 && count <= 20, "You can drop minimum 1, maximum 20 NFTs");
    require(count.add(totalSupply()) < (_maxSupply+1), "Exceeds max supply");
    require(owner() == msg.sender || msg.value >= _mintCost.mul(count),
           "Ether value sent is below the price");
    
    _mint(msg.sender, count);
  }

  /**
  * @dev Mint a token to each Address of `recipients`.
  * Can only be called if requirements are satisfied.
  */
  function mintTokensTo(address[] calldata recipients)
  public
  payable
  nonReentrant{
    require(recipients.length>0,"Missing recipient addresses");
    require(owner() == msg.sender || _isPublicMintEnabled, "Mint disabled");
    require(recipients.length > 0 && recipients.length <= 20, "You can drop minimum 1, maximum 20 NFTs");
    require(recipients.length.add(totalSupply()) < (_maxSupply+1), "Exceeds max supply");
    require(owner() == msg.sender || msg.value >= _mintCost.mul(recipients.length),
           "Ether value sent is below the price");
    for(uint i=0; i<recipients.length; i++){
        _mint(recipients[i], 1);
     }
  }

  /**
  * @dev Mint `count` tokens if requirements are satisfied.
  */
  function freeMint(uint256 count) 
  public 
  payable 
  nonReentrant{
    require(owner() == msg.sender || _isPublicMintEnabled, "Mint disabled");
    require(totalSupply() + count <= _freeSupply, "Exceed max free supply");
    require(count == 1, "Cant mint more than 1");
    require(count > 0, "Must mint at least 1 token");

    _safeMint(msg.sender, count);
  }

  /**
  * @dev Update the cost to mint a token.
  * Can only be called by the current owner.
  */
  function setCost(uint256 cost) public onlyOwner{
    _mintCost = cost;
  }

  /**
  * @dev Update the max supply.
  * Can only be called by the current owner.
  */
  function setMaxSupply(uint256 max) public onlyOwner{
    _maxSupply = max;
  }

  /**
  * @dev Update the max free supply.
  * Can only be called by the current owner.
  */
  function setFreeSupply(uint256 max) public onlyOwner{
    _freeSupply = max;
  }

  /**
  * @dev Transfers contract balance to contract owner.
  * Can only be called by the current owner.
  */
  function withdraw() public onlyOwner{
    payable(owner()).transfer(address(this).balance);
  }

  /**
  * @dev Used by public mint functions and by owner functions.
  * Can only be called internally by other functions.
  */
  function _mint(address to, uint256 count) internal virtual returns (uint256){
    _safeMint(to, count);

    return count;
  }

  function getCost() public view returns (uint256){
    return _mintCost;
  }
  function getMaxSupply() public view returns (uint256){
    return _maxSupply;
  }
  function getCurrentSupply() public view returns (uint256){
    return totalSupply();
  }
  function getMintStatus() public view returns (bool) {
    return _isPublicMintEnabled;
  }
  function getFreeSupply() public view returns (uint256) {
    return _freeSupply;
  }
  function setBaseURI(string calldata URI) external onlyOwner {
        _tokenBaseURI = URI;
  }
  function _baseURI() override internal view returns (string memory) {
    return _tokenBaseURI;
  }

}
function mintTokensTo(address[] calldata recipients)
  public
  payable
  nonReentrant{
    require(recipients.length>0,"Missing recipient addresses");
    require(owner() == msg.sender || _isPublicMintEnabled, "Mint disabled");
    require(recipients.length > 0 && recipients.length <= 20, "You can drop minimum 1, maximum 20 NFTs");
    require(recipients.length.add(totalSupply()) < (_maxSupply+1), "Exceeds max supply");
    require(owner() == msg.sender || msg.value >= _mintCost.mul(recipients.length),
           "Ether value sent is below the price");
    for(uint i=0; i<recipients.length; i++){
        _mint(recipients[i], 1);
     }
  }
/**
 * @dev Mint a token to each Address of `recipients`.
 * Can only be called if requirements are satisfied.
 */