// Contract: HomeWorkDeployer
// Target function: function _deriveKeyAndPrepareToDeploy(bytes32 salt)
 also an `isDeployable` method for determining if a contract
   * can be deployed to the address, but in extreme cases it must actually
   * perform a dry-run to determine if the contract is deployable, which means
   * that it does not support staticcalls. There is also a convenience method,
   * `hasNeverBeenDeployed`, but the information it conveys can be determined
   * from this method alone as well.
   */
  function getHomeAddressInformation(bytes32 key)
    external
    view
    returns (
      address homeAddress,
      address controller,
      uint256 deploys,
      bytes32 currentRuntimeCodeHash
    )
  {
    // Derive home address and retrieve other information using supplied key.
    homeAddress = _getHomeAddress(key);
    HomeAddress memory home = _home[key];

    // If the home address has not been seen before, use the default controller.
    if (!home.exists) {
      controller = address(bytes20(key));
    } else {
      controller = home.controller;
    }

    // Retrieve the count of total deploys to the home address.
    deploys = home.deploys;

    // Retrieve keccak256 hash of runtime code currently at the home address.
    assembly { currentRuntimeCodeHash := extcodehash(homeAddress) }
  }

  /**
   * @notice View function to determine if no contract has ever been deployed to
   * a home address given the corresponding key. This can be used to ensure that
   * a given key or corresponding token is "new" or not.
   * @param key bytes32 The unique value used to derive the home address.
   * @return A boolean signifying if a contract has never been deployed using
   * the supplied key before.
   */
  function hasNeverBeenDeployed(bytes32 key)
    external
    view
    returns (bool neverBeenDeployed)
  {
    neverBeenDeployed = (_home[key].deploys == 0);
  }

  /**
   * @notice View function to search for a known key, salt, and/or submitter
   * given a supplied home address. Keys can be controlled directly by an
   * address that matches the first 20 bytes of the key, or they can be derived
   * from a salt and a submitter - if the key is not a derived key, the salt and
   * submitter fields will both have a value of zero.
   * @param homeAddress address The home address to check for key information.
   * @return The key, salt, and/or submitter used to deploy to the home address,
   * assuming they have been submitted to the reverse lookup.
   * @dev To populate these values, call `setReverseLookup` for cases where keys
   * are used directly or are the only value known, or `setDerivedReverseLookup`
   * for cases where keys are derived from a known salt and submitter.
   */
  function reverseLookup(address homeAddress)
    external
    view
    returns (bytes32 key, bytes32 salt, address submitter)
  {
    KeyInformation memory keyInformation = _key[homeAddress];
    key = keyInformation.key;
    salt = keyInformation.salt;
    submitter = keyInformation.submitter;
  }

  /**
   * @notice View function used by the metamorphic initialization code when
   * deploying a contract to a home address. It returns the address of the
   * runtime storage contract that holds the contract creation code, which the
   * metamorphic creation code then `DELEGATECALL`s into in order to set up the
   * contract and deploy the target runtime code.
   * @return The current runtime storage contract that contains the target
   * contract creation code.
   * @dev This method is not meant to be part of the user-facing contract API,
   * but is rather a mechanism for enabling the deployment of arbitrary code via
   * fixed initialization code. The odd naming is chosen so that function
   * selector will be 0x00000009 - that way, the metamorphic contract can simply
   * use the `PC` opcode in order to push the selector to the stack.
   */
  function getInitializationCodeFromContractRuntime_6CLUNS()
    external
    view
    returns (address initializationRuntimeStorageContract)
  {
    // Return address of contract with initialization code set as runtime code.
    initializationRuntimeStorageContract = _initializationRuntimeStorageContract;
  }

  /**
   * @notice View function to return an URI for a given token ID. Throws if the
   * token ID does not exist.
   * @param tokenId uint256 ID of the token to query.
   * @return String representing the URI data encoding of JSON metadata.
   * @dev The URI returned by this method takes the following form (with all
   * returns and initial whitespace removed - it's just here for clarity):
   *
   * data:application/json,{
   *   "name":"Home%20Address%20-%200x********************",
   *   "description":"< ... HomeWork NFT desription ... >",
   *   "image":"data:image/svg+xml;charset=utf-8;base64,< ... Image ... >"}
   *
   * where ******************** represents the checksummed home address that the
   * token confers control over.
   */
  function tokenURI(uint256 tokenId)
    external
    view
    returns (string memory)
  {
    // Only return a URI for tokens that exist.
    require(_exists(tokenId), "A token with the given ID does not exist.");

    // Get the home address that the given tokenId corresponds to.
    address homeAddress = _getHomeAddress(bytes32(tokenId));

    // Get the checksummed, ascii-encoded representation of the home address.
    string memory asciiHomeAddress = _toChecksummedAsciiString(homeAddress);
    
    bytes memory uriEndSegment = _getTokenURIStorageRuntime();

    // Insert checksummed address into URI in name and image fields and return.
    return string(
      abi.encodePacked(      // Concatenate all the string segments together.
        _URI_START_SEGMENT,  // Data URI ID and initial formatting is constant.
        asciiHomeAddress,    // Checksummed home address is in the name field.
        uriEndSegment        // Description, image, and formatting is constant.
      )
    );
  }

  /**
   * @notice Pure function to get the token name.
   * @return String representing the token name.
   */
  function name() external pure returns (string memory) {
    return _NAME;
  }

  /**
   * @notice Pure function to get the token symbol.
   * @return String representing the token symbol.
   */
  function symbol() external pure returns (string memory) {
    return _SYMBOL;
  }

  /**
   * @notice Pure function to determine the key that is derived from a given
   * salt and submitting address.
   * @param salt bytes32 The salt value that is used to derive the key.
   * @param submitter address The submitter of the salt value used to derive the
   * key.
   * @return The derived key.
   */
  function getDerivedKey(bytes32 salt, address submitter)
    external
    pure
    returns (bytes32 key)
  {
    // Derive the key using the supplied salt and submitter.
    key = _deriveKey(salt, submitter);
  }

  /**
   * @notice Pure function to determine the home address that corresponds to
   * a given key.
   * @param key bytes32 The unique value used to derive the home address.
   * @return The home address.
   */
  function getHomeAddress(bytes32 key)
    external
    pure
    returns (address homeAddress)
  {
    // Derive the home address using the supplied key.
    homeAddress = _getHomeAddress(key);
  }

  /**
   * @notice Pure function for retrieving the metamorphic initialization code
   * used to deploy arbitrary contracts to home addresses. Provided for easy
   * verification and for use in other applications.
   * @return The 32-byte metamorphic initialization code.
   * @dev This metamorphic init code works via the "metamorphic delegator"
   * mechanism, which is explained in greater detail at `_deployToHomeAddress`.
   */
  function getMetamorphicDelegatorInitializationCode()
    external
    pure
    returns (bytes32 metamorphicDelegatorInitializationCode)
  {
    metamorphicDelegatorInitializationCode = _HOME_INIT_CODE;
  }

  /**
   * @notice Pure function for retrieving the keccak256 of the metamorphic
   * initialization code used to deploy arbitrary contracts to home addresses.
   * This is the value that you should use, along with this contract's address
   * and a caller address that you control, to mine for an partucular type of
   * home address (such as one at a compact or gas-efficient address).
   * @return The keccak256 hash of the metamorphic initialization code.
   */
  function getMetamorphicDelegatorInitializationCodeHash()
    external
    pure
    returns (bytes32 metamorphicDelegatorInitializationCodeHash)
  {
    metamorphicDelegatorInitializationCodeHash = _HOME_INIT_CODE_HASH;
  }

  /**
   * @notice Pure function for retrieving the prelude that will be inserted
   * ahead of the code payload in order to deploy a runtime storage contract.
   * @return The 11-byte "arbitrary runtime" prelude.
   */
  function getArbitraryRuntimeCodePrelude()
    external
    pure
    returns (bytes11 prelude)
  {
    prelude = _ARBITRARY_RUNTIME_PRELUDE;
  }

  /**
   * @notice Internal function for deploying a runtime storage contract given a
   * particular payload.
   * @return The address of the runtime storage contract.
   * @dev To take the provided code payload and deploy a contract with that
   * payload as its runtime code, use the following prelude:
   *
   * 0x600b5981380380925939f3...
   *
   * 00  60  push1 0b      [11 -> offset]
   * 02  59  msize         [offset, 0]
   * 03  81  dup2          [offset, 0, offset]
   * 04  38  codesize      [offset, 0, offset, codesize]
   * 05  03  sub           [offset, 0, codesize - offset]
   * 06  80  dup1          [offset, 0, codesize - offset, codesize - offset]
   * 07  92  swap3         [codesize - offset, 0, codesize - offset, offset]
   * 08  59  msize         [codesize - offset, 0, codesize - offset, offset, 0]
   * 09  39  codecopy      [codesize - offset, 0] <init_code_in_runtime>
   * 10  f3  return        [] *init_code_in_runtime*
   * ... init_code
   */
  function _deployRuntimeStorageContract(bytes memory payload)
    internal
    returns (address runtimeStorageContract)
  {
    // Construct the contract creation code using the prelude and the payload.
    bytes memory runtimeStorageContractCreationCode = abi.encodePacked(
      _ARBITRARY_RUNTIME_PRELUDE,
      payload
    );

    assembly {
      // Get the location and length of the newly-constructed creation code.
      let encoded_data := add(0x20, runtimeStorageContractCreationCode)
      let encoded_size := mload(runtimeStorageContractCreationCode)

      // Deploy the runtime storage contract via standard `CREATE`.
      runtimeStorageContract := create(0, encoded_data, encoded_size)

      // Pass along revert message if the contract did not deploy successfully.
      if iszero(runtimeStorageContract) {
        returndatacopy(0, 0, returndatasize)
        revert(0, returndatasize)
      }
    }

    // Emit an event with address of newly-deployed runtime storage contract.
    emit NewRuntimeStorageContract(runtimeStorageContract, keccak256(payload));
  }

  /**
   * @notice Internal function for deploying arbitrary contract code to the home
   * address corresponding to a suppied key via metamorphic initialization code.
   * @return The home address and the hash of the deployed runtime code.
   * @dev This deployment method uses the "metamorphic delegator" pattern, where
   * it will retrieve the address of the contract that contains the target
   * initialization code, then delegatecall into it, which executes the
   * initialization code stored there and returns the runtime code (or reverts).
   * Then, the runtime code returned by the delegatecall is returned, and since
   * we are still in the initialization context, it will be set as the runtime
   * code of the metamorphic contract. The 32-byte metamorphic initialization
   * code is as follows:
   *
   * 0x5859385958601c335a585952fa1582838382515af43d3d93833e601e57fd5bf3
   *
   * 00  58  PC               [0]
   * 01  59  MSIZE            [0, 0]
   * 02  38  CODESIZE         [0, 0, codesize -> 32]
   * returndatac03  59  MSIZE            [0, 0, 32, 0]
   * 04  58  PC               [0, 0, 32, 0, 4]
   * 05  60  PUSH1 0x1c       [0, 0, 32, 0, 4, 28]
   * 07  33  CALLER           [0, 0, 32, 0, 4, 28, caller]
   * 08  5a  GAS              [0, 0, 32, 0, 4, 28, caller, gas]
   * 09  58  PC               [0, 0, 32, 0, 4, 28, caller, gas, 9 -> selector]
   * 10  59  MSIZE            [0, 0, 32, 0, 4, 28, caller, gas, selector, 0]
   * 11  52  MSTORE           [0, 0, 32, 0, 4, 28, caller, gas] <selector>
   * 12  fa  STATICCALL       [0, 0, 1 => success] <init_in_runtime_address>
   * 13  15  ISZERO           [0, 0, 0]
   * 14  82  DUP3             [0, 0, 0, 0]
   * 15  83  DUP4             [0, 0, 0, 0, 0]
   * 16  83  DUP4             [0, 0, 0, 0, 0, 0]
   * 17  82  DUP3             [0, 0, 0, 0, 0, 0, 0]
   * 18  51  MLOAD            [0, 0, 0, 0, 0, 0, init_in_runtime_address]
   * 19  5a  GAS              [0, 0, 0, 0, 0, 0, init_in_runtime_address, gas]
   * 20  f4  DELEGATECALL     [0, 0, 1 => success] {runtime_code}
   * 21  3d  RETURNDATASIZE   [0, 0, 1 => success, size]
   * 22  3d  RETURNDATASIZE   [0, 0, 1 => success, size, size]
   * 23  93  SWAP4            [size, 0, 1 => success, size, 0]
   * 24  83  DUP4             [size, 0, 1 => success, size, 0, 0]
   * 25  3e  RETURNDATACOPY   [size, 0, 1 => success] <runtime_code>
   * 26  60  PUSH1 0x1e       [size, 0, 1 => success, 30]
   * 28  57  JUMPI            [size, 0]
   * 29  fd  REVERT           [] *runtime_code*
   * 30  5b  JUMPDEST         [size, 0]
   * 31  f3  RETURN           []
   */
  function _deployToHomeAddress(bytes32 key)
    internal
    returns (address homeAddress, bytes32 runtimeCodeHash)
  {    
    assembly {
      // Write the 32-byte metamorphic initialization code to scratch space.
      mstore(
        0,
        0x5859385958601c335a585952fa1582838382515af43d3d93833e601e57fd5bf3
      )

      // Call `CREATE2` using above init code with the supplied key as the salt.
      homeAddress := create2(callvalue, 0, 32, key)

      // Pass along revert message if the contract did not deploy successfully.
      if iszero(homeAddress) {
        returndatacopy(0, 0, returndatasize)
        revert(0, returndatasize)
      }

      // Get the runtime hash of the deployed contract.
      runtimeCodeHash := extcodehash(homeAddress)
    }

    // Clear the address of the runtime storage contract from storage.
    delete _initializationRuntimeStorageContract;

    // Emit an event with home address, key, and runtime hash of new contract.
    emit NewResident(homeAddress, key, runtimeCodeHash);
  }