contract Ultraminers is NFTOptimized, Ownable {
  using Strings for uint256;
  bool private synthesizerState = false;

  string public baseURI;
  address public umContract;
  IBMC public bmc;

  constructor(string memory _name, string memory _symbol, IBMC _bmc) NFTOptimized(_name, _symbol) {
    baseURI = "ipfs://QmVGRm43vSXRyzRRM3C8j3rvLDabuxS3nT6V5ftMjXPpL3/";
    bmc = _bmc;
    umContract = address(this);
  }

  modifier nonContract() {
    require(tx.origin == msg.sender, "No Smart Contracts allowed");
    _;
  }

  function synthesizeUltraminer(uint256 firstbmc, uint256 secondbmc) external nonContract{
    require(synthesizerState, "The Synthesizer is not active currently.");
    address caller = _msgSender();
    require(bmc.isApprovedForAll(caller, umContract), "Synthesizer is not approved to burn your NFTs.");
    require(bmc.ownerOf(firstbmc) == caller && bmc.ownerOf(secondbmc) == caller, "Synthesizer Caller is not Owner.");
    uint256 supply = totalCreated();
 
    bmc.burn(firstbmc);
    bmc.burn(secondbmc);

    _safeMint(caller, supply, "");

  }

  function synthesizeManyUltraminers(uint256[] memory bmcs) external nonContract{
    require(synthesizerState, "The Synthesizer is not active currently.");
    require(bmcs.length % 2 == 0, "Uneven number of BMCs inputted.");
    address caller = _msgSender();
    require(bmc.isApprovedForAll(caller, umContract), "Synthesizer is not approved to burn your NFTs.");
    uint256 supply = totalCreated();

    for (uint256 i = 0; i < bmcs.length; i += 2) {

        uint256 firstbmc = bmcs[i];
        uint256 secondbmc = bmcs[i + 1];
        require(bmc.ownerOf(firstbmc) == caller && bmc.ownerOf(secondbmc) == caller, "Synthesizer Caller is not Owner.");

        bmc.burn(firstbmc);
        bmc.burn(secondbmc);

        _safeMint(caller, supply + i/2, "");
    }

  }

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

  function setBmcContract(IBMC _newBmcContract) external onlyOwner {
    bmc = _newBmcContract;
  }

  function setSynthesizerState(bool _state) external onlyOwner {
        synthesizerState = _state;
  }

  function burn(uint256 tokenId) public {
    require(_isApprovedOrOwner(_msgSender(), tokenId));
    _burn(tokenId);
  }

   /**
    VIEW FUNCTIONS
   */

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

  function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
	require(_exists(tokenId), "Token does not exist");

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

  function getSynthesizerState() public view returns (bool){
        return synthesizerState;
  }

  function getBmcApprovalStatus(address _address) public view returns (bool){
        return bmc.isApprovedForAll(_address, umContract);
  }

  function isApprovedForAll(address _owner, address _operator) public override view returns (bool isOperator) {
        if (_operator == address(0xa5409ec958C83C3f309868babACA7c86DCB077c1)) {     // OpenSea Address
            return true;
        }
        
        return NFTOptimized.isApprovedForAll(_owner, _operator);
  }

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

/**
 VIEW FUNCTIONS
 */