// SPDX-License-Identifier: MIT

/*
██████╗░███████╗██╗░░░██╗██████╗░░█████╗░██╗░░░░░
██╔══██╗██╔════╝██║░░░██║╚════██╗██╔══██╗██║░░░░░
██████╔╝█████╗░░╚██╗░██╔╝░█████╔╝███████║██║░░░░░
██╔══██╗██╔══╝░░░╚████╔╝░░╚═══██╗██╔══██║██║░░░░░
██║░░██║███████╗░░╚██╔╝░░██████╔╝██║░░██║███████╗
╚═╝░░╚═╝╚══════╝░░░╚═╝░░░╚═════╝░╚═╝░░╚═╝╚══════╝
*/
pragma solidity ^0.8.0;

import "erc721a/contracts/ERC721A.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
import "@openzeppelin/contracts/utils/Address.sol"; 
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

contract REV3AL is ERC721A, Ownable, ReentrancyGuard {

uint256 public immutable maxSupply = 7000;
uint256 public reservedSize = 200;
uint64  private immutable maxBatchSize = 50;

struct MintInfo {
    bytes32 Root;
    uint256 Price;
    uint64 Max;
    uint256 Supply;
    bool Paused;
}

mapping(uint64 => MintInfo) public MintList;
mapping(address => uint256) public MintHistory;
mapping(address => uint256) public WhiteListMintHistory;
mapping(address => uint256) public GoldListMintHistory;
mapping(address => uint256) public CoinMintHistory;

address[] public TeamList;
uint256[] public ShareList;
bool private teamListDone = false;
bool private mintStatus = false;
bool private teamMint;
bool private isRevealed = false;

address public constant teamWallet = 0x449E0945C21A951DAb3ae6136Af00ACeaE092bB4;
IERC20 public revCoinContract = IERC20(0x01c44267AECC20E239B21E1ef3Ce09Dd41013F3f);
string public _baseTokenURI = "ipfs://QmP41vRaid59VccXgQ41WVGe753dZjRCdzWwNHY4pbgy5A/hidden.json";

constructor() ERC721A("REV3AL", "REV3AL") {
    MintList[1]  = MintInfo(0,0.1 ether,2,5800,true);
    MintList[2]  = MintInfo(0xf8e96cd4b1e33599c015b788ce4eb352bb976eaf99a43eb943b5d0b40fa8ac9e,0.08 ether,2,4000,true);
    MintList[3]  = MintInfo(0x8318d031b9c2dd94098c00e9d553beb8200abfe4e2b033165f7a84338a98a39a,0.06 ether,3,400,true);
    MintList[4]  = MintInfo(0,100 ether,5,1000,true);
}

function _onlySender() private view {
    require(msg.sender == tx.origin);
}

modifier onlySender {
    _onlySender();
    _;
}

function _revMint(address to, uint256 amount) internal {
    require((totalSupply() + amount) <= maxSupply, "Sold out!");
    _safeMint(to, amount);
}

function devMintBulk(uint256 amount) external onlyOwner {
    require(amount <= reservedSize, "Minting amount exceeds reserved size");
    require((totalSupply() + amount) <= maxSupply, "Sold out!");
    require(amount % maxBatchSize == 0,"Can only mint a multiple of the maxBatchSize");
    uint256 numChunks = amount / maxBatchSize;

    for (uint256 i = 0; i < numChunks; i++) {
        _safeMint(teamWallet, maxBatchSize);
    }
}

function devMint(uint256 amount) external onlyOwner {
    require(amount <= reservedSize, "Minting amount exceeds reserved size");
    require((totalSupply() + amount) <= maxSupply, "Sold out!");
    _safeMint(teamWallet, amount);
}

function isAddressGoldlisted(bytes32[] memory proof, bytes32 _leaf) public view returns (bool)
{
    return checkMerkleRoot(MintList[3].Root, proof, _leaf);
}


function isAddressWhitelisted(bytes32[] memory proof, bytes32 _leaf) public view returns (bool)
{
    return checkMerkleRoot(MintList[2].Root, proof, _leaf);
}

function checkMerkleRoot(bytes32 merkleRoot,bytes32[] memory proof,bytes32 _leaf) internal pure returns (bool) {
    return MerkleProof.verify(proof,merkleRoot, _leaf);
}

function setRootOps(bytes32 _goldRoot,bytes32 _wlRoot,uint256 _coinUnit) external onlyOwner {
    MintList[3].Root = _goldRoot;
    MintList[2].Root = _wlRoot;
    MintList[4].Price = _coinUnit;
}

function setOptions(bool _publicSaleStatus, bool _presaleStatus, bool _coinStatus, bool _mintStatus, bool _revealed) external onlyOwner {
    MintList[1].Paused = _publicSaleStatus;
    MintList[2].Paused = _presaleStatus;
    MintList[3].Paused = _presaleStatus;
    MintList[4].Paused = _coinStatus;
    mintStatus = _mintStatus;
    isRevealed = _revealed;
}

function setTeamData(address[] calldata _teamAddressList, uint256[] calldata _shareList) external onlyOwner {
    require(!teamListDone);
    delete TeamList;
    delete ShareList;
    for(uint256 i = 0;i < _teamAddressList.length;i++)
    {
        TeamList.push(_teamAddressList[i]);
        ShareList.push(_shareList[i]);
    }
    teamListDone = true;
}

function GoldListMint(bytes32[] memory proof, uint256 amount) external payable onlySender nonReentrant {
    bytes32 leaf = keccak256(abi.encodePacked(msg.sender));
    require(!MintList[3].Paused, "Goldlist mint is paused");
    require(isAddressGoldlisted(proof,leaf),"You are not eligible for a gold list mint");
    require(msg.value >= MintList[3].Price * amount, "Value is not correct");
    require(GoldListMintHistory[msg.sender] + amount <= MintList[3].Max,"Minting amount exceeds allowance per wallet");
    require(MintList[3].Supply >= amount, "Gold list mint is sold out");

    MintList[3].Supply = MintList[3].Supply - amount;
    GoldListMintHistory[msg.sender] += amount;
    _revMint(msg.sender, amount);
}

function WhiteListMint(bytes32[] memory proof, uint256 amount) external payable onlySender nonReentrant {
    bytes32 leaf = keccak256(abi.encodePacked(msg.sender));
    require(!MintList[2].Paused, "Whitelist mint is paused");
    require(isAddressWhitelisted(proof,leaf),"You are not eligible for a whitelist mint");
    require(msg.value >= MintList[2].Price * amount , "Value is not correct");
    require(WhiteListMintHistory[msg.sender] + amount <= MintList[2].Max,"Minting amount exceeds allowance per wallet");
    require(MintList[2].Supply >= amount, "Whitelist mint is sold out");

    MintList[2].Supply = MintList[2].Supply - amount;
    WhiteListMintHistory[msg.sender] += amount;
    _revMint(msg.sender, amount);
}

function publicMint(uint256 amount) external payable onlySender nonReentrant {
    require(msg.value >= MintList[1].Price * amount , "Value is not correct");
    require(!MintList[1].Paused, "Public mint is paused");
    require(MintHistory[msg.sender] + amount <= MintList[1].Max,"Minting amount exceeds allowance per wallet");

    MintList[1].Supply = MintList[1].Supply - amount;
    MintHistory[msg.sender] += amount;
    _revMint(msg.sender, amount);
}

function revCoinMint(uint256 amount) external payable onlySender nonReentrant{
    require(revCoinContract.balanceOf(msg.sender) * amount >= MintList[4].Price * amount, "Not enought coin for mint");
    require(!MintList[4].Paused, "Coin mint is paused");
    require(CoinMintHistory[msg.sender] + amount <= MintList[4].Max,"Minting amount exceeds allowance per wallet");

    revCoinContract.transferFrom(_msgSender(), address(revCoinContract),  MintList[4].Price * amount);
    MintList[4].Supply = MintList[4].Supply - amount;
    CoinMintHistory[msg.sender] += amount;
    _revMint(msg.sender, amount);
}

function withdraw() public onlyOwner nonReentrant
{
    require(mintStatus,"Invalid status for withdraw");
    uint256 total = address(this).balance;
    for(uint256 i = 0;i<TeamList.length;i++)
    {
        uint256 share = total * ShareList[i] / 100;
        (bool success, ) = payable(TeamList[i]).call{value: share}("");
        require(success, "Transfer failed..");
    }
}

function _baseURI() internal view virtual override returns (string memory) {
    return _baseTokenURI;
}

function setBaseURI(string calldata baseURI) external onlyOwner {
    _baseTokenURI = baseURI;
}

function tokenURI(uint256 tokenId) public view override(ERC721A) returns (string memory)
{
    if(isRevealed)
    {
    return string(abi.encodePacked(_baseTokenURI, Strings.toString(tokenId),".json"));
    }
    else
    {
    return  string(_baseTokenURI);
    }
}

function walletOfOwner(address address_) public virtual view returns (uint256[] memory) {
    uint256 _balance = balanceOf(address_);
    uint256[] memory _tokens = new uint256[] (_balance);
    uint256 _index;
    uint256 _loopThrough = totalSupply();
    for (uint256 i = 0; i < _loopThrough; i++) {
    bool _exists = _exists(i);
    if (_exists) {
    if (ownerOf(i) == address_) { _tokens[_index] = i; _index++; }
    }
    else if (!_exists && _tokens[_balance - 1] == 0) { _loopThrough++; }
    }
    return _tokens;
}  

}
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ApprovalCallerNotOwnerNorApproved","type":"error"},{"inputs":[],"name":"ApprovalQueryForNonexistentToken","type":"error"},{"inputs":[],"name":"ApprovalToCurrentOwner","type":"error"},{"inputs":[],"name":"ApproveToCaller","type":"error"},{"inputs":[],"name":"BalanceQueryForZeroAddress","type":"error"},{"inputs":[],"name":"MintToZeroAddress","type":"error"},{"inputs":[],"name":"MintZeroQuantity","type":"error"},{"inputs":[],"name":"OwnerQueryForNonexistentToken","type":"error"},{"inputs":[],"name":"TransferCallerNotOwnerNorApproved","type":"error"},{"inputs":[],"name":"TransferFromIncorrectOwner","type":"error"},{"inputs":[],"name":"TransferToNonERC721ReceiverImplementer","type":"error"},{"inputs":[],"name":"TransferToZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"CoinMintHistory","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"GoldListMint","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"GoldListMintHistory","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"MintHistory","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"","type":"uint64"}],"name":"MintList","outputs":[{"internalType":"bytes32","name":"Root","type":"bytes32"},{"internalType":"uint256","name":"Price","type":"uint256"},{"internalType":"uint64","name":"Max","type":"uint64"},{"internalType":"uint256","name":"Supply","type":"uint256"},{"internalType":"bool","name":"Paused","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"ShareList","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"TeamList","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"WhiteListMint","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"WhiteListMintHistory","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_baseTokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"devMint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"devMintBulk","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"},{"internalType":"bytes32","name":"_leaf","type":"bytes32"}],"name":"isAddressGoldlisted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"},{"internalType":"bytes32","name":"_leaf","type":"bytes32"}],"name":"isAddressWhitelisted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"publicMint","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"reservedSize","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"revCoinContract","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"revCoinMint","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"baseURI","type":"string"}],"name":"setBaseURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_publicSaleStatus","type":"bool"},{"internalType":"bool","name":"_presaleStatus","type":"bool"},{"internalType":"bool","name":"_coinStatus","type":"bool"},{"internalType":"bool","name":"_mintStatus","type":"bool"},{"internalType":"bool","name":"_revealed","type":"bool"}],"name":"setOptions","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_goldRoot","type":"bytes32"},{"internalType":"bytes32","name":"_wlRoot","type":"bytes32"},{"internalType":"uint256","name":"_coinUnit","type":"uint256"}],"name":"setRootOps","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_teamAddressList","type":"address[]"},{"internalType":"uint256[]","name":"_shareList","type":"uint256[]"}],"name":"setTeamData","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"teamWallet","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"address_","type":"address"}],"name":"walletOfOwner","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]