contract MineNations is ERC721URIStorage, Ownable {
    using SafeMath for uint256;
    using Counters for Counters.Counter;
    Counters.Counter private _tokenIds;

    string private baseURI;
    string public notRevealedURI;
    string public baseExtension = ".json";
    bytes32 public merkleRoot;
    address public community = 0x26F6fbe9b2888De3A36870Ddf241e28Fa134A3D6;
    bool public revealed = false;
    uint256 public presaleEnded;
    uint256 public publicPrice = 0.06 ether;
    uint256 public whitelistPrice = 0.05 ether;

    mapping (address => uint) public ownWallet;

    constructor(string memory _name, string memory _symbol, string memory _initBaseURI, string memory _initNotRevealedURI) ERC721(_name, _symbol) {
      baseURI = _initBaseURI;
      notRevealedURI = _initNotRevealedURI;
      mintItem(community, 35);
    }

    /* ****************** */
    /* INTERNAL FUNCTIONS */
    /* ****************** */
    function _baseURI() internal view virtual override returns (string memory) {
      if(revealed == false){
        return notRevealedURI;
      }
      return baseURI;
    }

    /* **************** */
    /* PUBLIC FUNCTIONS */
    /* **************** */
    function totalSupply() public view returns (uint256) {
        return _tokenIds.current();
    }

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

      string memory currentBaseURI = _baseURI();

      if(revealed == false){
        return bytes(currentBaseURI).length > 0
            ? string(
                abi.encodePacked(
                    currentBaseURI,
                    Strings.toString(1),
                    baseExtension
                )
            )
            : "";
      }
      
      return bytes(currentBaseURI).length > 0
            ? string(
                abi.encodePacked(
                    currentBaseURI,
                    Strings.toString(tokenId),
                    baseExtension
                )
            )
            : "";
    }

    function whitelistMint(address _to, uint _count, bytes32[] memory _merkleProof) public payable {
      bytes32 leaf = keccak256(abi.encodePacked(msg.sender));
      require(MerkleProof.verify(_merkleProof, merkleRoot, leaf), "Invalid proof");
      require(totalSupply() + _count <= 10000, "Can't mint anymore");
      require(presaleEnded > 0, "Can't mint the nft");
      if(block.timestamp < presaleEnded) {
        require(ownWallet[msg.sender] + _count <= 2, "Maxium is 2 during pre-sale step");
        require(msg.value >= _count * whitelistPrice, "Not match balance");
      } else {
        require(ownWallet[msg.sender] + _count <= 5, "Maxium is 5");
        require(msg.value >= _count * publicPrice, "Not match balance");
      }

      for(uint i = 0; i < _count; i++) {
        _tokenIds.increment();
        ownWallet[_to]++;
        _safeMint(_to, _tokenIds.current());
      }
    }

    function mintItem(address _to, uint _count) public payable {
      require(totalSupply() + _count <= 10000, "Can't mint anymore");
      if(msg.sender != owner() && msg.sender !=community) {
        require(presaleEnded > 0, "Can't mint the nft");
        require(block.timestamp >= presaleEnded, "Only whitelists can be mint during the pre-sale step");
        require(msg.value >= _count * publicPrice, "Not match balance");
        require(ownWallet[msg.sender] + _count <= 3, "Maxium is 3");
      }

      for(uint i = 0; i < _count; i++) {
        _tokenIds.increment();
        ownWallet[_to]++;
        _safeMint(_to, _tokenIds.current());
      }
    }

    function status() public view returns (bool) {
      return block.timestamp < presaleEnded;
    }

    /* *************** */
    /* OWNER FUNCTIONS */
    /* *************** */
    function setBaseURI(string memory _newBaseURI) public onlyOwner {
        baseURI = _newBaseURI;
    }

    function setNotRevealedURI(string memory _newNotRevealedURI) public onlyOwner {
        notRevealedURI = _newNotRevealedURI;
    }

    function setBaseExtension(string memory _newBaseExtension) public onlyOwner {
        baseExtension = _newBaseExtension;
    }

    function setMerkleRoot(bytes32 _merkleRoot) external onlyOwner{
        merkleRoot = _merkleRoot;
    }

    function startReveal() public onlyOwner {
        revealed = true;
    }

    function startPresale() public onlyOwner {
        presaleEnded = block.timestamp + 1 days;
    }

    function setPublicPrice(uint256 _newPublicPrice) public onlyOwner {
        publicPrice = _newPublicPrice;
    }

    function setWhitelistPrice(uint256 _newWhitelistPrice) public onlyOwner {
        whitelistPrice = _newWhitelistPrice;
    }

    function isPresale() public view returns (bool) {
        return block.timestamp < presaleEnded;
    }

    function isWhitelist(bytes32[] memory _merkleProof) public view returns (bool) {
       bytes32 leaf = keccak256(abi.encodePacked(msg.sender));
       return MerkleProof.verify(_merkleProof, merkleRoot, leaf);
    }

    function withdraw() external onlyOwner {
      address _owner = owner();
      payable(_owner).transfer( address(this).balance );
    }
}
function _baseURI() internal view virtual override returns (string memory) {
  if(revealed == false){
    return notRevealedURI;
  }
  return baseURI;
}

/* ****************** */