contract Regenz is ERC721Burnable, Ownable {

  uint256 constant public PRICE = 0.033 ether;
  uint256 constant public TOTAL_SUPPLY = 10000;
  uint256 constant public REVEAL_TIMESTAMP = 0;
  uint256 constant public MAX_PER_TX = 30;
  uint256 constant public NUM_TO_RESERVE = 250;

  uint256 public startingIndex;
  uint256 public startingIndexBlock;

  string constant public METADATA_HASH = "";
  
  address payable claimEthAddress;

  bool private saleIsActive;

  struct RegenzBought {
      address wallet;
      uint[] regenz;
  }
  mapping (address => RegenzBought) regenzBought;

  constructor(address payable _claimEthAddress) public ERC721("Regenz", "REGENZ") {
      claimEthAddress = _claimEthAddress;
  }

  function reserveRegenz() public onlyOwner {
      uint supply = totalSupply();
      uint i;
      for (i = 0; i < NUM_TO_RESERVE; i++) {
          _safeMint(msg.sender, supply + i);
      }
  }

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

  /*
  * Pause sale if active, make active if paused
  */
  function flipSaleState() public onlyOwner {
      saleIsActive = !saleIsActive;
  }

  /**
  * Mints Regenz
  */
  function mintRegenz(uint numberOfTokens) public payable {
      require(saleIsActive, "Sale must be active to mint");
      require(numberOfTokens <= MAX_PER_TX, "Above max tx count");
      require(totalSupply().add(numberOfTokens) <= TOTAL_SUPPLY, "Exceeds max supply");
      require(PRICE.mul(numberOfTokens) <= msg.value, "ETH sent is incorrect");
      for(uint i = 0; i < numberOfTokens; i++) {
          uint mintIndex = totalSupply();
          _safeMint(msg.sender, mintIndex);

          regenzBought[msg.sender].regenz.push(mintIndex);
          regenzBought[msg.sender].wallet = msg.sender;
      }
        

      // If we haven't set the starting index and this is either
      // 1) the last saleable token or
      // 2) the first token to be sold after the end of pre-sale, set the starting index block
      if (startingIndexBlock == 0 && (totalSupply() == TOTAL_SUPPLY || block.timestamp >= REVEAL_TIMESTAMP)) {
          startingIndexBlock = block.number;
      }
  }

  function getMintedRegenz(address wallet) public view returns (uint [] memory) {
       return regenzBought[wallet].regenz;
  }

  
    /**
    * Set the starting index for the collection
    */
    function setStartingIndex() public {
        require(startingIndex == 0, "Starting index is already set");
        require(startingIndexBlock != 0, "Starting index block must be set");
        startingIndex = uint(blockhash(startingIndexBlock)) % TOTAL_SUPPLY;
        // Just a sanity case in the worst case if this function is called late (EVM only stores last 256 block hashes)
        if (block.number.sub(startingIndexBlock) > 255) {
            startingIndex = uint(blockhash(block.number - 1)) % TOTAL_SUPPLY;
        }
    }

    function claimETH() public {
        require(claimEthAddress == _msgSender(), "Ownable: caller is not the claimEthAddress");
        payable(address(claimEthAddress)).transfer(address(this).balance);
    }

    function tokenURI(uint256 tokenId) public view override returns (string memory) {
        if (startingIndex == 0) {
        return super.tokenURI(0);
        }
        uint256 moddedId = (tokenId + startingIndex) % TOTAL_SUPPLY;
        return super.tokenURI(moddedId);
    }
}
function setStartingIndex() public {
    require(startingIndex == 0, "Starting index is already set");
    require(startingIndexBlock != 0, "Starting index block must be set");
    startingIndex = uint(blockhash(startingIndexBlock)) % TOTAL_SUPPLY;
    // Just a sanity case in the worst case if this function is called late (EVM only stores last 256 block hashes)
    if (block.number.sub(startingIndexBlock) > 255) {
        startingIndex = uint(blockhash(block.number - 1)) % TOTAL_SUPPLY;
    }
}

/**
 * Set the starting index for the collection
 */