contract CryptoNobles is ERC721, ERC721Enumerable, Ownable {
  using Strings for uint256;
  using SafeMath for uint256;
  string public baseURI;
  string public baseExtension = ".json";
  uint256 public cost = 0.06 ether;
  uint256 public whitelistCost = 0.04 ether;
  uint256 public maxSupply = 2500;
  uint256 public maxMintAmount = 3;
  uint256 public whiteListLength = 0;
  uint256 public maxWhitelistSpots = 500;
  mapping(address => bool) public whitelisted;
  bool public paused = true;
  bool public pauseWhitelistDrop = true; 
  bool public pauseWhitelist = false; 

  constructor(
    string memory _name,
    string memory _symbol,
    string memory _initBaseURI
  ) ERC721(_name, _symbol) {
    setBaseURI(_initBaseURI);
  }

  // internal
  function _baseURI() internal view virtual override returns (string memory) {
    return baseURI;
  } 
  
  function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal override(ERC721, ERC721Enumerable) {
      super._beforeTokenTransfer(from, to, tokenId);
  }

  function supportsInterface(bytes4 interfaceId) public view virtual override(ERC721, ERC721Enumerable) returns (bool) {
      return super.supportsInterface(interfaceId);
  }

  // public
  function mint(address _to, uint256 _mintAmount) public payable {
    uint256 supply = totalSupply();
    require(!paused);
    require(_mintAmount > 0);
    require(_mintAmount <= maxMintAmount);
    require((cost.mul(_mintAmount)) <= msg.value);
    require(supply + _mintAmount <= maxSupply);
    require(walletOfOwner(_to).length < 3);
    for (uint256 i = 1; i <= _mintAmount; i++) {
      _safeMint(_to, supply + i);
    }
  }

  function mintFromWhiteList(address _to, uint256 _mintAmount) public payable {
    uint256 supply = totalSupply();
    require(!pauseWhitelistDrop);
    require(whitelisted[_to] != false);
    require(_mintAmount > 0);
    require(_mintAmount <= maxMintAmount);
    require((whitelistCost.mul(_mintAmount)) <= msg.value);
    require(supply + _mintAmount <= maxSupply);
    require(walletOfOwner(_to).length < 3);
    for (uint256 i = 1; i <= _mintAmount; i++) {
      _safeMint(_to, supply + i);
    }
  }

  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"
    );

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

  function setCost(uint256 _newCost) public onlyOwner {
    cost = _newCost;
  }

  function setWhitelistCost(uint256 _newCost) public onlyOwner {
    whitelistCost = _newCost;
  }

  function setmaxMintAmount(uint256 _newmaxMintAmount) public onlyOwner {
    maxMintAmount = _newmaxMintAmount;
  }
   
   function setmaxWhitelistSpots(uint256 _newmaxWhitelistSpots) public onlyOwner {
    maxWhitelistSpots = _newmaxWhitelistSpots;
  }

  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;
  }

  function setPauseWhitelistDrop(bool _state) public onlyOwner {
    pauseWhitelistDrop = _state;
  }

  function setPauseWhitelist(bool _state) public onlyOwner {
    pauseWhitelist = _state;
  }

  function whitelistUser() public payable {
    require(whiteListLength < maxWhitelistSpots);
    require(whitelisted[msg.sender] != true);
    require(!pauseWhitelist);
    whitelisted[msg.sender] = true;
    whiteListLength ++;
  }
 
  function removeWhitelistUser(address _user) public onlyOwner {
    whitelisted[_user] = false;
    whiteListLength --;
  }

  function withdrawAll() public payable onlyOwner {
      require(payable(msg.sender).send(address(this).balance));
  }
}
// _________                        __          _______        ___.   .__                 
// \_   ___ \_______ ___.__._______/  |_  ____  \      \   ____\_ |__ |  |   ____   ______
// /    \  \/\_  __ <   |  |\____ \   __\/  _ \ /   |   \ /  _ \| __ \|  | _/ __ \ /  ___/
// \     \____|  | \/\___  ||  |_> >  | (  <_> )    |    (  <_> ) \_\ \  |_\  ___/ \___ \ 
//  \______  /|__|   / ____||   __/|__|  \____/\____|__  /\____/|___  /____/\___  >____  >
//         \/        \/     |__|                       \/           \/          \/     \/ 
function _baseURI() internal view virtual override returns (string memory) {
  return baseURI;
} 

// internal