contract JiJi is ERC721, ERC721Enumerable, Ownable {
  using Strings for uint256;
  bool public _isWhiteListSaleActive = false;
  bool public _isSaleActive = false;

  // Constants
  uint256 constant public MAX_SUPPLY = 420;
  uint256 public mintPrice = 0.14 ether;
  uint256 public whiteListPrice = 0.09 ether;
  uint256 public maxwhitelistBalance = 4;
  


  string private _baseURIExtended;


  mapping(address => uint256) public whiteList;
  

  event TokenMinted(uint256 supply);

  constructor() ERC721('FrenchbulldogJiJi', 'JiJi') {}
//ok
  function flipWhiteListSaleActive() public onlyOwner {
    _isWhiteListSaleActive = !_isWhiteListSaleActive;
  }
//ok
  function filpSaleActive() public onlyOwner {
    _isSaleActive = !_isSaleActive;
  }

//ok
  function setMintPrice(uint256 _mintPrice) public onlyOwner {
    mintPrice = _mintPrice;
  }

  function setWhiteListPrice(uint256 _whiteListPrice) public onlyOwner {
    whiteListPrice = _whiteListPrice;
  }


//ok
  function setWhiteList4(address[] calldata _whiteList) external onlyOwner {
    for(uint i = 0; i < _whiteList.length; i++) {
      whiteList[_whiteList[i]] = 4;
    }
  }
  function setWhiteList3(address[] calldata _whiteList) external onlyOwner {
    for(uint i = 0; i < _whiteList.length; i++) {
      whiteList[_whiteList[i]] = 3;
    }
  }
  function setWhiteList2(address[] calldata _whiteList) external onlyOwner {
    for(uint i = 0; i < _whiteList.length; i++) {
      whiteList[_whiteList[i]] = 2;
    }
  }

//ok
  function withdraw(address to) public onlyOwner {
    uint256 balance = address(this).balance;
    payable(to).transfer(balance);
  }
//ok
  function preserveMint(uint numJiJis, address to) public onlyOwner {
    require(totalSupply() + numJiJis <= MAX_SUPPLY, 'Preserve mint would exceed max supply');
    _mintJiJi(numJiJis, to);
    emit TokenMinted(totalSupply());
  }
//ok
  function getTotalSupply() public view returns (uint256) {
    return totalSupply();
  }
//ok
  function getJiJiByOwner(address _owner) public view returns (uint256[] memory) {
    uint256 tokenCount = balanceOf(_owner);
    uint256[] memory tokenIds = new uint256[](tokenCount);
    for (uint256 i; i < tokenCount; i++) {
      tokenIds[i] = tokenOfOwnerByIndex(_owner, i);
    }
    return tokenIds;
  }

//ok
  function mintJiJis(uint numJiJis) public payable {
    require(_isSaleActive, 'Sale must be active to mint JiJis');
    require(totalSupply() + numJiJis <= MAX_SUPPLY, 'Sale would exceed max supply');
    require(numJiJis * mintPrice <= msg.value, 'Not enough ether sent');
    _mintJiJi(numJiJis, msg.sender);
    emit TokenMinted(totalSupply());
  }
 

  function whiteListMintJiJis(uint numJiJis) public payable {
    require(_isWhiteListSaleActive, 'Sale must be active to mint JiJis');
    require(totalSupply() + numJiJis <= MAX_SUPPLY, 'Sale would exceed max supply');
    require(balanceOf(msg.sender) + numJiJis <= maxwhitelistBalance, 'Sale would exceed max Whitelist balance');
    uint256 price = whiteListPrice;
    require(numJiJis * price <= msg.value, 'Not enough ether sent');
    if (whiteList[msg.sender] == 4) {
    price = whiteListPrice;
    whiteList[msg.sender] = maxwhitelistBalance-numJiJis;
    } 
    else if(whiteList[msg.sender] == 3){
      price = whiteListPrice;
    whiteList[msg.sender] = 3-numJiJis;
    }
    else if(whiteList[msg.sender] == 2){
      price = whiteListPrice;
    whiteList[msg.sender] = 2-numJiJis;
    }
    else if(whiteList[msg.sender] == 1){
      require(numJiJis == 1,'Something Wrong');
      price = whiteListPrice;
    whiteList[msg.sender] = 1-numJiJis;
    }
    else if(whiteList[msg.sender] == 0){
      revert('You have no more whitelist');
    }
    else  {
    revert('Not in white list');
    }
    
    
    _mintJiJi(numJiJis, msg.sender);
    emit TokenMinted(totalSupply());
  }

//ok
  function _mintJiJi(uint256 numJiJis, address recipient) internal {
    uint256 supply = totalSupply();
    for (uint256 i = 0; i < numJiJis; i++) {
      _safeMint(recipient, supply + i);
    }
  }
//ok
  function setBaseURI(string memory baseURI_) external onlyOwner {
    _baseURIExtended = baseURI_;
  }
//ok
  function _baseURI() internal view virtual override returns (string memory) {
    return _baseURIExtended;
  }
//ok
  function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
    require(_exists(tokenId), 'ERC721Metadata: URI query for nonexistent token');
    require(tokenId < MAX_SUPPLY,'Something wrong');
        return string(abi.encodePacked(_baseURI(), tokenId.toString(), ".json"));
    }

  function _beforeTokenTransfer (
    address from,
    address to,
    uint256 tokenId
  ) internal override(ERC721, ERC721Enumerable) {
    super._beforeTokenTransfer(from, to, tokenId);
  }
//ok
  function supportsInterface(bytes4 interfaceId)
    public
    view
    override(ERC721, ERC721Enumerable)
    returns (bool)
  {
    return super.supportsInterface(interfaceId);
  }
}
//import '@openzeppelin/contracts/access/Ownable.sol';
//import '@openzeppelin/contracts/token/ERC721/ERC721.sol';
//import '@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol';
function getJiJiByOwner(address _owner) public view returns (uint256[] memory) {
  uint256 tokenCount = balanceOf(_owner);
  uint256[] memory tokenIds = new uint256[](tokenCount);
  for (uint256 i; i < tokenCount; i++) {
    tokenIds[i] = tokenOfOwnerByIndex(_owner, i);
  }
  return tokenIds;
}

//ok