contract CoolFrens is Ownable, ERC721A, ReentrancyGuard {

  constructor(
    uint256 maxBatchSize_,
    uint256 collectionSize_,
    uint256 amountForAuctionAndDev_,
    uint256 amountForDevs_
  ) ERC721A("CoolFrens", "CoolFrens", maxBatchSize_, collectionSize_) {
    require(amountForAuctionAndDev_ <= collectionSize_, "larger collection size needed" );
  }
    
    uint256 public pricePerWL = 0.04 ether;
    uint256 public pricePerPublic = 0.06 ether;
    bool public presaleLive = true;
    bool public publicLive = false;
    address private ownerAddy = 0x74701CA166857B83bB2F5Eb9420aF938E39088E8;
    mapping(address => uint256) public WLminted;

    /* 
    * @dev Requires msg.sender to have valid access message.
    * @param _v ECDSA signature parameter v.
    * @param _r ECDSA signature parameters r.
    * @param _s ECDSA signature parameters s.
    */

    modifier noInvalidAccess(uint8 _v, bytes32 _r, bytes32 _s) {
        require(!isInvalidAccessMessage(_v,_r,_s) );
        _;
    }
 
    /* 
    * @dev Verifies if message was signed by owner to give access to _add for this contract.
    *      Assumes Geth signature prefix.
    * @param _add Address of agent with access
    * @param _v ECDSA signature parameter v.
    * @param _r ECDSA signature parameters r.
    * @param _s ECDSA signature parameters s.
    * @return Validity of access message for a given address.
    */
    function isInvalidAccessMessage(
        uint8 _v, 
        bytes32 _r, 
        bytes32 _s) 
        view public returns (bool)
    {
        bytes32 hash = keccak256(abi.encodePacked(this));
        address signAddress = ecrecover(
            keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)),
            _v,
            _r,
            _s
        );
        if (ownerAddy == signAddress){
          return true;
        }
        else{
          return false;
        }
    }

  function setPreSale(bool status) public onlyOwner{
    presaleLive = status;
  }  
  
  function setPublicSalePrice(bool status) public onlyOwner{
    publicLive = status;
  }

  function setPreSale(uint256 price) public onlyOwner{
    pricePerWL = price;
  }  
  
  function setPublicSalePrice(uint256 price) public onlyOwner{
    pricePerPublic = price;
  }


  /* 
    * @dev Public Sale Mint
    * @param quantity Number of NFT's to be minted at 0.06 with a max of 10 per transaction. This is a gas efficient function.
    */
  function mint(uint256 quantity) external payable {
    require(quantity <= 10, "Cant mint more than 10");
    require(totalSupply() + quantity <= 3183, "Sold out");
    require(msg.value >= pricePerPublic * quantity, "Not enough Eth");
    require(publicLive, "sale not live");
    _safeMint(msg.sender, quantity);
  }

  function mintMarketing(address to, uint256 quantity) external payable onlyOwner {
    require(quantity <= 10, "Cant mint more than 10");
    require(totalSupply() + quantity <= 3333, "Sold out");
    require(publicLive, "sale not live");
    _safeMint(to, quantity);
  }

 /* 
    * @dev Verifies if message was signed by owner to give access to this contract.
    *      Assumes Geth signature prefix.
    * @param quantity Number of NFT's to be minted at 0.04 with a max of 3. This is a gas efficient function.
    * @param _v ECDSA signature parameter v.
    * @param _r ECDSA signature parameters r.
    * @param _s ECDSA signature parameters s.
    */
  function mintWL(uint256 quantity, uint8 _v, bytes32 _r, bytes32 _s) external payable noInvalidAccess(_v, _r, _s){
    require(quantity <= 3, "Cant mint more than 3");
    require(totalSupply() + quantity <= 3183, "Sold out");
    require(msg.value >= pricePerWL * quantity, "Not enough Eth");
    require(presaleLive, "sale not live");
    require(WLminted[msg.sender] + quantity <= 3, "Cant mint more than 3");
    WLminted[msg.sender] += quantity;
    _safeMint(msg.sender, quantity);
  }

  // // metadata URI
  string private _baseTokenURI;

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

  function setBaseURI(string calldata baseURI) external onlyOwner {
    _baseTokenURI = baseURI;
  }

  function withdrawMoney() external onlyOwner nonReentrant {
    (bool success, ) = msg.sender.call{value: address(this).balance}("");
    require(success, "Transfer failed.");
  }

  function setOwnersExplicit(uint256 quantity) external onlyOwner nonReentrant {
    _setOwnersExplicit(quantity);
  }

  function numberMinted(address owner) public view returns (uint256) {
    return _numberMinted(owner);
  }

  function getOwnershipData(uint256 tokenId)
    external
    view
    returns (TokenOwnership memory)
  {
    return ownershipOf(tokenId);
  }
}
function isInvalidAccessMessage(
    uint8 _v, 
    bytes32 _r, 
    bytes32 _s) 
    view public returns (bool)
{
    bytes32 hash = keccak256(abi.encodePacked(this));
    address signAddress = ecrecover(
        keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)),
        _v,
        _r,
        _s
    );
    if (ownerAddy == signAddress){
      return true;
    }
    else{
      return false;
    }
}

/* 
 * @dev Verifies if message was signed by owner to give access to _add for this contract.
 *      Assumes Geth signature prefix.
 * @param _add Address of agent with access
 * @param _v ECDSA signature parameter v.
 * @param _r ECDSA signature parameters r.
 * @param _s ECDSA signature parameters s.
 * @return Validity of access message for a given address.
 */