contract SantaClausNFT is ERC721, Ownable, ReentrancyGuard {
  using SafeMath for uint256;
  using Strings for uint256;

  constructor() ERC721("Santa Claus", "SC") {}

  uint256 public price = 0.08 ether;
  address public operator;
  uint256 public tokenId;

  uint256 public freeToMintNumber = 100000 ether;
  uint256 public eligibleToMintNumber = 0;

  address public erc20token = 0x51c55b870590816310ce9C1299F8B20ed54a666B;

  bool public flagSaleOpen = false;

  uint256 public constant MAX = 8888;

  string private __baseURI;

  mapping (address=>bool) public freeMinted;

  event Mint(uint256 indexed tokenId, address indexed owner);

  function tokenURI(uint256 _tokenId) public view override returns(string memory) {
    return string(abi.encodePacked(baseURI(), _tokenId.toString()));
  }

  function baseURI() public view returns(string memory) {
    return __baseURI;
  }

  function grantOperator(address _operator) external onlyOwner {
    operator = _operator;
  }

  function revokeOperator() external onlyOwner {
    operator = address(0);
  } 

  modifier onlyOperator() {
    require(msg.sender == operator || msg.sender == owner(), "Permission error");
    _;
  }

  function isSufficient(address holder, address token, uint256 number) internal view returns(bool) {
    require(address(holder) != address(0) && address(token) != address(0), "Invalid token address");
    return (number <= IERC20(token).balanceOf(holder));
  }

  function bundlePrice(uint256 quantity) public view returns(uint256) {
    if (quantity >= 5 && quantity < 10) {
      return (price.sub(0.01 ether)).mul(quantity);
    } else if (quantity >= 10) {
      return (price.sub(0.02 ether)).mul(quantity);
    } else {
      return price;
    }
  }

  function mint(uint256 quantity) public payable nonReentrant { 
    require(flagSaleOpen, "Sale not open");
    require(quantity > 0, "Quantity argument incorrect");
    require(isSufficient(msg.sender, erc20token, eligibleToMintNumber), "Token($XMAS) quantity insufficient");
    require(msg.value >= bundlePrice(quantity), "Ether value sent is not correct");
    require(tokenId + quantity < MAX, "Purchase would exceed max supply");

    for (uint256 i = 0; i < quantity; i++) {
      tokenId++;
      _safeMint(msg.sender, tokenId);
      emit Mint(tokenId, msg.sender);
    }
  }

  // set varibles
  function freeMint() public nonReentrant {
    require(flagSaleOpen, "Sale not open");
    require(isSufficient(msg.sender, erc20token, freeToMintNumber), "Token($XMAS) quantity insufficient");
    require(tokenId < MAX, "Purchase would exceed max supply");
    require(freeMinted[msg.sender] == false, "Already claimed the airdrop");

    tokenId++;
    freeMinted[msg.sender] = true;
    _safeMint(msg.sender, tokenId);
    emit Mint(tokenId, msg.sender);
  }

  function setPrice(uint256 _price) external onlyOperator {
    require(_price > 0.03 ether, "Price can't be lower than 0.03 ether");
    price = _price;
  }

  function setErc20Token(address token) external onlyOperator {
    erc20token = token;
  }

  function setFreeMintNumber(uint256 newNumber) external onlyOperator {
    freeToMintNumber = newNumber;
  }

  function setEligibleMintNumber(uint256 newNumber) external onlyOperator {
    eligibleToMintNumber = newNumber;
  }

  function setBaseURI(string memory newBaseURI) public onlyOperator {
    __baseURI = newBaseURI;
  }

  function flipSaleOpen(bool flag) external onlyOperator {
    flagSaleOpen = flag;
  }

  function withdraw() external onlyOwner {
    uint256 balance = address(this).balance;
    payable(msg.sender).transfer(balance);
  }
}
function freeMint() public nonReentrant {
  require(flagSaleOpen, "Sale not open");
  require(isSufficient(msg.sender, erc20token, freeToMintNumber), "Token($XMAS) quantity insufficient");
  require(tokenId < MAX, "Purchase would exceed max supply");
  require(freeMinted[msg.sender] == false, "Already claimed the airdrop");

  tokenId++;
  freeMinted[msg.sender] = true;
  _safeMint(msg.sender, tokenId);
  emit Mint(tokenId, msg.sender);
}

// set varibles