contract meditating_cats_flat is ERC721Enumerable, Ownable
{
  using Strings for uint256;

  string internal baseURI;
  string internal baseExtension = ".json";
  string internal NotRevealedURI;

  uint256 public MAX_SUPPLY = 6000; // 6k

  uint256 public cost = 21000000000000000; // 21000000000000000 Wei = 0.021 ether
  
  bool public saleIsActive = true;
  bool public Revealed = false;

  mapping( address => bool ) internal minted_wl_adrs_map; // MintedWhiteList

////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////
  constructor( 
      string memory _name,
      string memory _symbol,
      string memory _initBaseURI,
      string memory _initNotRevealedURI,
      address[] memory _init_pm_adrs_array // preMint
  ) ERC721( _name, _symbol )
  {
      baseURI = _initBaseURI;
      NotRevealedURI = _initNotRevealedURI;
      preMint( _init_pm_adrs_array );
  }
////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////
  // internal
  function preMint( address[] memory _users ) internal
  {
    for ( uint256 ii = 1; ii <= _users.length; ii++ ) _safeMint( _users[ ii - 1 ], ii );
  }
////////////////////////////////////////////////////////////////////////////////////////////////////////////
  // public
  function mint( uint256 _mintAmount ) public payable
  {
    require( saleIsActive, "Contract is on Pause" );
    require( _mintAmount > 0, "Need to Mint at least 1 NFT" );
     uint256 supply = totalSupply();
    require( supply + _mintAmount <= MAX_SUPPLY, "Contract's all tokens already minted" );

    if ( msg.sender != owner( )) require( msg.value >= cost * _mintAmount, "Insufficient Funds" );

    for ( uint256 ii = 1; ii <= _mintAmount; ii++ ) _safeMint( msg.sender, supply + ii );
  }
////////////////////////////////////////////////////////////////////////////////////////////////////////////
  function mintUserInWhiteList() public // not-payable // one wl-user => only one mint!
  {
    require( saleIsActive, "Contract is on pause" );
    uint256  supply = totalSupply();
    require( supply + 1 <= MAX_SUPPLY, "Contract's all tokens already minted" );
        
    if ( msg.sender != owner())
      require( minted_wl_adrs_map[ msg.sender ], "Your already Minted WhiteList or no-address in WhiteList" );
               minted_wl_adrs_map[ msg.sender ] = false;
                        _safeMint( msg.sender, supply + 1 );
  }
////////////////////////////////////////////////////////////////////////////////////////////////////////////
  // only owner public
  function setUsersWhiteList( address[] calldata _users ) public onlyOwner
  {
    for( uint256 ii = 0; ii < _users.length; ii++ ) minted_wl_adrs_map[ _users[ ii ]] = true;
  }
////////////////////////////////////////////////////////////////////////////////////////////////////////////
  // system public
  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 ){ currentBaseURI = NotRevealedURI; }

    return bytes( currentBaseURI ).length > 0 ? string( abi.encodePacked( currentBaseURI, tokenId.toString( ), baseExtension ) ) : "";
  }
////////////////////////////////////////////////////////////////////////////////////////////////////////////
  function walletOfOwner( address _owner ) public view returns ( uint256[] memory )
  {
    uint256 ownerTokenCount = balanceOf( _owner );
    uint256[] memory tokenIds = new uint256[]( ownerTokenCount );
    for ( uint256 ii; ii < ownerTokenCount; ii++ ) tokenIds[ ii ] = tokenOfOwnerByIndex( _owner, ii );
    return tokenIds;
  }
////////////////////////////////////////////////////////////////////////////////////////////////////////////
  // only owner public
  function reveal( ) public onlyOwner { Revealed = !Revealed; }
  function setCost( uint256 _newCost ) public onlyOwner { cost = _newCost; }
  function flipSaleState() public onlyOwner{ saleIsActive = !saleIsActive; }  
  function setBaseURI( string memory _newBaseURI ) public onlyOwner { baseURI = _newBaseURI; }
  function setNotRevealedURI( string memory _newHiddenURI ) public onlyOwner { NotRevealedURI = _newHiddenURI; }

////////////////////////////////////////////////////////////////////////////////////////////////////////////
  function withdraw( ) public payable onlyOwner 
  {
      ( bool success, ) = payable( owner( )).call{value: address( this ).balance}( "" );
    require( success );
  }
}
// all openzeppelin's have 0.8.0 not 0.8.7!!!
function preMint( address[] memory _users ) internal
{
  for ( uint256 ii = 1; ii <= _users.length; ii++ ) _safeMint( _users[ ii - 1 ], ii );
}

// internal