contract NCurve is ERC721Enumerable, Ownable, ReentrancyGuard {
  using Address for address;

  address public nAddress = 0x05a46f1E545526FB803FF974C790aCeA34D1f2D6;
  NInterface nContract = NInterface(nAddress);

  bool public presaleActive = false;
  bool public publicSaleActive = false;

  uint256 public presalePrice = 0.015 ether;
  uint256 public publicPrice = 0.05 ether;

  string public baseTokenURI;

  constructor(string memory _newBaseURI) ERC721('n Curve', 'NCRV') {
    setBaseURI(_newBaseURI);
  }

  // Override so the openzeppelin tokenURI() method will use this method to create the full tokenURI instead
  function _baseURI() internal view virtual override returns (string memory) {
    return baseTokenURI;
  }

  // See which address owns which tokens
  function tokensOfOwner(address _ownerAddress) public view returns (uint256[] memory) {
    uint256 tokenCount = balanceOf(_ownerAddress);
    uint256[] memory tokenIds = new uint256[](tokenCount);
    for (uint256 i; i < tokenCount; i++) {
      tokenIds[i] = tokenOfOwnerByIndex(_ownerAddress, i);
    }
    return tokenIds;
  }

  function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
    require(_exists(tokenId), 'ERC721Metadata: URI query for nonexistent token');

    return super.tokenURI(tokenId);
  }

  // Exclusive presale minting with N
  function mintWithN(uint256 _nId) public payable nonReentrant {
    require(presaleActive, 'Presale not started');
    require(msg.value == presalePrice, 'Wrong amount of ETH sent');
    require(nContract.ownerOf(_nId) == msg.sender, 'Not the owner of this N');

    _safeMint(msg.sender, _nId);
  }

  // Standard mint function
  function mint(uint256 _nId) public payable {
    require(publicSaleActive, 'Public sale not started');
    require(msg.value == publicPrice, 'Ether value sent is not correct');
    require(_nId > 0 && _nId <= 8888, 'Token ID invalid');

    _safeMint(msg.sender, _nId);
  }

  // Start and stop presale
  function setPresaleActive(bool _presaleActive) public onlyOwner {
    presaleActive = _presaleActive;
  }

  // Start and stop public sale
  function setPublicSaleActive(bool _publicSaleActive) public onlyOwner {
    publicSaleActive = _publicSaleActive;
  }

  // Set new baseURI
  function setBaseURI(string memory _newBaseURI) public onlyOwner {
    baseTokenURI = _newBaseURI;
  }

  function withdraw(uint256 _amount) public onlyOwner {
    (bool success, ) = msg.sender.call{value: _amount}('');
    require(success, 'Transfer failed.');
  }
}
function setPresaleActive(bool _presaleActive) public onlyOwner {
  presaleActive = _presaleActive;
}

// Start and stop presale