// SPDX-License-Identifier: MIT

pragma solidity ^0.8.4;

import "erc721a/contracts/ERC721A.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract ParaNFT is ERC721A, Ownable {
    using SafeMath for uint256;
    using Counters for Counters.Counter;

    string public baseTokenURI = "https://ipfs.io/ipfs/QmShRgk2qjhwKkAreVxcwxfB9tTxyvVoXjkBCAkav4WW7t";
    bytes32 public whitelistMerkleRoot;
    uint16 public constant TOTAL_MINT_AMOUNT = 3000;
    string public mintType = "presale";
    uint16 public mintAmountPerMintType = 100;
    uint256 public pricePerNft = 0.08 ether;
    mapping(address => uint256) public mintedNfts;
    mapping(address => uint256) public mintedTimestamp;

    constructor() ERC721A("ParaNFT", "PARA") {}

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

    //  Set the base uri for token
    function setBaseURI(string memory _baseTokenURI) public onlyOwner {
        baseTokenURI = _baseTokenURI;
    }

    //  Set the merkle tree for whitelist
    function setWhitelistMerkleRoot(bytes32 _whitelistMerkleRoot)
        public
        onlyOwner
    {
        whitelistMerkleRoot = _whitelistMerkleRoot;
    }

    //  Set the price per NFT
    function setPricePerNft(uint256 _pricePerNft) public onlyOwner {
        pricePerNft = _pricePerNft;
    }

    //  Set mint amount according to the mint type
    function setMintAmountPerMintType(uint16 _mintAmountPerMintType)
        public
        onlyOwner
    {
        mintAmountPerMintType = _mintAmountPerMintType;
    }

    //  Set the mint type
    function setMintType(string memory _mintType) external onlyOwner {
        if (
            keccak256(abi.encodePacked((_mintType))) ==
            keccak256(abi.encodePacked(("presale")))
        ) {
            mintType = _mintType;
            setMintAmountPerMintType(100);
            setPricePerNft(0.08 ether);
        } else if (
            keccak256(abi.encodePacked((_mintType))) ==
            keccak256(abi.encodePacked(("public")))
        ) {
            mintType = _mintType;
            setMintAmountPerMintType(100);
            setPricePerNft(0.08 ether);
        } else {
            revert("No valid mint type.");
        }
    }

    //  Private sale with whitelist
    function privateMint(bytes32[] calldata _merkleProof) public payable {
        bytes memory tempEmptyStringTest = bytes(mintType);
        bytes32 leaf;
        uint256 mintIndex;

        require(
            keccak256(abi.encodePacked((mintType))) !=
                keccak256(abi.encodePacked(("public"))),
            "Public sale period!"
        );
        require(
            msg.sender == tx.origin,
            "Mint from other contract not allowed."
        );
        // require(mintedNfts[msg.sender] < 1, "You already mint a NFT.");
        require(tempEmptyStringTest.length > 0, "Mint type isn't set yet.");
        require(whitelistMerkleRoot.length > 0, "Whitelist isn't provided.");

        //  Presale
        if (
            keccak256(abi.encodePacked((mintType))) ==
            keccak256(abi.encodePacked(("presale")))
        ) {
            require(totalSupply() <= 1000, "Presale is finished.");
        } 

        leaf = keccak256(abi.encodePacked(msg.sender));
        require(
            MerkleProof.verify(_merkleProof, whitelistMerkleRoot, leaf),
            "Not whitelisted address."
        );

        mintIndex = totalSupply() + 1;
        _safeMint(msg.sender, 1);
        mintedNfts[msg.sender] = mintIndex;
        mintedTimestamp[msg.sender] = block.timestamp;
    }

    //  Public sale without whitelist
    function publicMint() public payable {
        uint256 mintIndex;

        require(
            keccak256(abi.encodePacked((mintType))) ==
                keccak256(abi.encodePacked(("public"))),
            "Presale period!."
        );
        require(
            msg.sender == tx.origin,
            "Mint from other contract not allowed."
        );
        // require(mintedNfts[msg.sender] < 1, "You already mint a NFT.");

        require(totalSupply() <= TOTAL_MINT_AMOUNT, "Public sale is finished.");

        mintIndex = totalSupply() + 1;
        _safeMint(msg.sender, 1);
        mintedNfts[msg.sender] = mintIndex;
        mintedTimestamp[msg.sender] = block.timestamp;
    }

    function withdraw(address ownerWallet) external onlyOwner {
        payable(ownerWallet).transfer(address(this).balance);
    }
}
[{"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"},{"inputs":[],"name":"URIQueryForNonexistentToken","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":[],"name":"TOTAL_MINT_AMOUNT","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"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":[],"name":"baseTokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"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":"mintAmountPerMintType","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mintType","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"mintedNfts","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"mintedTimestamp","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":[],"name":"pricePerNft","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"_merkleProof","type":"bytes32[]"}],"name":"privateMint","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"publicMint","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","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":"_baseTokenURI","type":"string"}],"name":"setBaseURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"_mintAmountPerMintType","type":"uint16"}],"name":"setMintAmountPerMintType","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_mintType","type":"string"}],"name":"setMintType","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_pricePerNft","type":"uint256"}],"name":"setPricePerNft","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_whitelistMerkleRoot","type":"bytes32"}],"name":"setWhitelistMerkleRoot","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":[{"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":[],"name":"whitelistMerkleRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"ownerWallet","type":"address"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]