contract SunBlocksTraining1 is ERC721Enumerable, Ownable, RandomlyAssigned {
  using Strings for uint256;

  string public baseExtension = ".json";
  uint256 public cost = 0.042 ether;
  uint256 public maxBlocksSupply = 5555;

  uint256 public maxPresaleMintAmount = 5;

  uint256 public maxMintAmount = 10;

  bool public isPresaleActive = false;
  bool public isFreeMintActive = false;
  bool public isPublicSaleActive = false;
 // bool public paused = false;
 // bool public publicPaused = true;

  uint public freeMintSupplyMinted = 0;
  uint public freeMintMaxSupply = 1130; //1110 SunGapers + 20 SunGap Collectors whitelisted in fremintList


  uint public genesisSupplyMinted = 0;
  uint public genesisMaxSupply = 555; //555 Genesis items Collectors whitelisted in genesisFremintList


  uint public publicSupplyMinted = 0; //Used for public and whitelist mints
  uint public publicMaxSupply = 3870;  //Used for public and whitelist mints


  mapping(address => uint256) public whitelist;
  mapping(address => uint256) public freeMintWhitelist;
  mapping(address => uint256) public genesisWhitelist;

  uint public numberWhitlisted = 0;
  uint public numberFreeMintWhitlisted = 0;

  string public baseURI;

  constructor(
  ) ERC721("SunBlocksTraining1", "SBLOCKST1")
  RandomlyAssigned(5555, 1) {
  }

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

  function setBaseURI(string memory _newBaseURI) public onlyOwner {
    baseURI = _newBaseURI;
  }

  function mint(uint256 _mintAmount) public payable {
    require(isPublicSaleActive, "Public Sale is not active");
    require(_mintAmount > 0);
    require(_mintAmount <= maxMintAmount);
    require(totalSupply() + _mintAmount <= maxBlocksSupply);
    require(publicSupplyMinted + _mintAmount <= publicMaxSupply);
    require(msg.value >= cost * _mintAmount);

    for (uint256 i = 1; i <= _mintAmount; i++) {
      uint256 mintIndex = nextToken();

      _safeMint(_msgSender(), mintIndex);
    }

    publicSupplyMinted = publicSupplyMinted + _mintAmount;
  }

  function presaleMint(uint256 _mintAmount) external payable {
      require(isPresaleActive, "Presale is not active");
      require(_mintAmount > 0);
      require(_mintAmount <= maxPresaleMintAmount);
      require(totalSupply() + _mintAmount <= maxBlocksSupply);
      require(publicSupplyMinted + _mintAmount <= publicMaxSupply);
      require(msg.value >= cost * _mintAmount);

      uint256 senderLimit = whitelist[msg.sender];

      require(senderLimit > 0, "You have no tokens left");
      require(_mintAmount <= senderLimit, "Your max token holding exceeded");


      for (uint256 i = 0; i < _mintAmount; i++) {
        uint256 mintIndex = nextToken();
        _safeMint(_msgSender(), mintIndex);
        senderLimit -= 1;
      }

      publicSupplyMinted = publicSupplyMinted + _mintAmount;
      whitelist[msg.sender] = senderLimit;
  }


  function freeMint(uint256 _mintAmount) external payable {
      require(isFreeMintActive, "Freemint is not active");
      require(_mintAmount > 0);
      require(_mintAmount <= maxMintAmount);
      require(totalSupply() + _mintAmount <= maxBlocksSupply);
      require(freeMintSupplyMinted + _mintAmount <= freeMintMaxSupply);

      uint256 senderLimit = freeMintWhitelist[msg.sender];

      require(senderLimit > 0, "You have no tokens left");
      require(_mintAmount <= senderLimit, "Your max token holding exceeded");


      for (uint256 i = 0; i < _mintAmount; i++) {
        uint256 mintIndex = nextToken();
        _safeMint(_msgSender(), mintIndex);
        senderLimit -= 1;
      }

      freeMintSupplyMinted = freeMintSupplyMinted + _mintAmount;
      freeMintWhitelist[msg.sender] = senderLimit;
  }

  function genesisFreeMint(uint256 _mintAmount) external payable {
      require(isPresaleActive, "Genesis Freemint is not active");
      require(_mintAmount > 0);
      require(_mintAmount <= maxMintAmount);
      require(totalSupply() + _mintAmount <= maxBlocksSupply);
      require(genesisSupplyMinted + _mintAmount <= genesisMaxSupply);

      uint256 senderLimit = genesisWhitelist[msg.sender];

      require(senderLimit > 0, "You have no tokens left");
      require(_mintAmount <= senderLimit, "Your max token holding exceeded");


      for (uint256 i = 0; i < _mintAmount; i++) {
        uint256 mintIndex = nextToken();
        _safeMint(_msgSender(), mintIndex);
        senderLimit -= 1;
      }

      genesisSupplyMinted = genesisSupplyMinted + _mintAmount;
      genesisWhitelist[msg.sender] = senderLimit;
  }


  function addWhitelist(
      address[] calldata _addrs,
      uint256[] calldata _limit
  ) external onlyOwner {
      require(_addrs.length == _limit.length);
      for (uint256 i = 0; i < _addrs.length; i++) {
          whitelist[_addrs[i]] = _limit[i];
          numberWhitlisted++;
      }
  }

  function addFreeMintWhitelist(
      address[] calldata _addrs,
      uint256[] calldata _limit
  ) external onlyOwner {
      require(_addrs.length == _limit.length);
      for (uint256 i = 0; i < _addrs.length; i++) {
          freeMintWhitelist[_addrs[i]] = _limit[i];
          numberFreeMintWhitlisted++;
      }
  }

  function updateWhitelist(
      address[] calldata _addrs,
      uint256[] calldata _limit
  ) external onlyOwner {
      require(_addrs.length == _limit.length);
       uint256 whitelistLength = numberWhitlisted;
      for (uint256 i = whitelistLength; i < _addrs.length+whitelistLength; i++) {
          whitelist[_addrs[i]] = _limit[i];
          numberWhitlisted++;
      }
  }

  function updateFreeMintWhitelist(
      address[] calldata _addrs,
      uint256[] calldata _limit
  ) external onlyOwner {
      require(_addrs.length == _limit.length);
       uint256 freeMintWhitelistLength = numberFreeMintWhitlisted;
      for (uint256 i = freeMintWhitelistLength; i < _addrs.length+freeMintWhitelistLength; i++) {
          freeMintWhitelist[_addrs[i]] = _limit[i];
          numberFreeMintWhitlisted++;
      }
  }

  function addGenesisMintWhitelist(
      address[] calldata _addrs,
      uint256[] calldata _limit
  ) external onlyOwner {
      require(_addrs.length == _limit.length);
      for (uint256 i = 0; i < _addrs.length; i++) {
          genesisWhitelist[_addrs[i]] = _limit[i];
      }
  }

  function walletOfOwner(address _owner)
    public
    view
    returns (uint256[] memory)
  {
    uint256 ownerTokenCount = balanceOf(_owner);
    uint256[] memory tokenIds = new uint256[](ownerTokenCount);
    for (uint256 i; i < ownerTokenCount; i++) {
      tokenIds[i] = tokenOfOwnerByIndex(_owner, i);
    }
    return tokenIds;
  }

  function tokenURI(uint256 tokenId)
    public
    view
    virtual
    override
    returns (string memory)
  {
    require(
      _exists(tokenId),
      "ERC721Metadata: URI query for nonexistent token"
    );

    string memory currentBaseURI = _baseURI();
    return bytes(currentBaseURI).length > 0
        ? string(abi.encodePacked(currentBaseURI, tokenId.toString(), baseExtension))
        : "";
  }
  //only owner


    function togglePresaleStatus() external onlyOwner {
        isPresaleActive = !isPresaleActive;
    }

    function toggleFreeMintStatus() external onlyOwner {
        isFreeMintActive = !isFreeMintActive;
    }

    function togglePublicSaleStatus() external onlyOwner {
        isPublicSaleActive = !isPublicSaleActive;
    }

  function withdraw() public payable onlyOwner {
    require(payable(msg.sender).send(address(this).balance));
  }

}
function togglePresaleStatus() external onlyOwner {
    isPresaleActive = !isPresaleActive;
}

//only owner