text: // Licensed to the .NET Foundation under one or more agreements.
  // The .NET Foundation licenses this file to you under the MIT license.
  
  #include "host_context.h"
  #include <trace.h>
  
  namespace
  {
      const int32_t valid_host_context_marker = 0xabababab;
      const int32_t closed_host_context_marker = 0xcdcdcdcd;
  
      int create_context_common(
          const hostpolicy_contract_t &hostpolicy_contract,
          const host_interface_t *host_interface,
          const corehost_initialize_request_t *init_request,
          uint32_t initialization_options,
          bool already_loaded,
          /*out*/ corehost_context_contract *hostpolicy_context_contract)
      {
          if (hostpolicy_contract.initialize == nullptr)
          {
              trace::error(_X("This component must target .NET Core 3.0 or a higher version."));
              return StatusCode::HostApiUnsupportedVersion;
          }
  
          int rc = StatusCode::Success;
          {
              propagate_error_writer_t propagate_error_writer_to_corehost(hostpolicy_contract.set_error_writer);
              if (!already_loaded)
              {
                  assert (host_interface != nullptr);
                  rc = hostpolicy_contract.load(host_interface);
              }
  
              if (rc == StatusCode::Success)
              {
                  initialization_options |= initialization_options_t::context_contract_version_set;
                  hostpolicy_context_contract->version = sizeof(corehost_context_contract);
                  rc = hostpolicy_contract.initialize(init_request, initialization_options, hostpolicy_context_contract);
              }
          }
  
          return rc;
      }
  }
  
  int host_context_t::create(
      const hostpolicy_contract_t &hostpolicy_contract,
      corehost_init_t &init,
      uint32_t initialization_options,
      /*out*/ std::unique_ptr<host_context_t> &context)
  {
      const host_interface_t &host_interface = init.get_host_init_data();
      corehost_context_contract hostpolicy_context_contract = {};
      int rc = create_context_common(hostpolicy_contract, &host_interface, nullptr, initialization_options, /*already_loaded*/ false, &hostpolicy_context_contract);
      if (rc == StatusCode::Success)
      {
          std::unique_ptr<host_context_t> context_local(new host_context_t(host_context_type::initialized, hostpolicy_contract, hostpolicy_context_contract));
          context_local->initialize_frameworks(init);
          context = std::move(context_local);
      }
  
      return rc;
  }
  
  int host_context_t::create_secondary(
      const hostpolicy_contract_t &hostpolicy_contract,
      std::unordered_map<pal::string_t, pal::string_t> &config_properties,
      uint32_t initialization_options,
      /*out*/ std::unique_ptr<host_context_t> &context)
  {
      std::vector<const pal::char_t*> config_keys;
      std::vector<const pal::char_t*> config_values;
      for (auto &kv : config_properties)
      {
          config_keys.push_back(kv.first.c_str());
          config_values.push_back(kv.second.c_str());
      }
  
      corehost_initialize_request_t init_request;
      init_request.version = sizeof(corehost_initialize_request_t);
      init_request.config_keys.len = config_keys.size();
      init_request.config_keys.arr = config_keys.data();
      init_request.config_values.len = config_values.size();
      init_request.config_values.arr = config_values.data();
  
      corehost_context_contract hostpolicy_context_contract = {};
      int rc = create_context_common(hostpolicy_contract, nullptr, &init_request, initialization_options, /*already_loaded*/ true, &hostpolicy_context_contract);
      if (STATUS_CODE_SUCCEEDED(rc))
      {
          std::unique_ptr<host_context_t> context_local(new host_context_t(host_context_type::secondary, hostpolicy_contract, hostpolicy_context_contract));
          context_local->config_properties = config_properties;
          context = std::move(context_local);
      }
  
      assert(rc != StatusCode::Success);
      return rc;
  }
  
  host_context_t* host_context_t::from_handle(const hostfxr_handle handle, bool allow_invalid_type)
  {
      if (handle == nullptr)
          return nullptr;
  
      host_context_t *context = static_cast<host_context_t*>(handle);
      int32_t marker = context->marker;
      if (marker == valid_host_context_marker)
      {
          if (allow_invalid_type || context->type != host_context_type::invalid)
              return context;
  
          trace::error(_X("Host context is in an invalid state"));
      }
      else if (marker == closed_host_context_marker)
      {
          trace::error(_X("Host context has already been closed"));
      }
      else
      {
          trace::error(_X("Invalid host context handle marker: 0x%x"), marker);
      }
  
      return nullptr;
  }
  
  host_context_t::host_context_t(
      host_context_type type,
      const hostpolicy_contract_t &hostpolicy_contract,
      const corehost_context_contract &hostpolicy_context_contract)
      : marker { valid_host_context_marker }
      , type { type }
      , hostpolicy_contract { hostpolicy_contract }
      , hostpolicy_context_contract { hostpolicy_context_contract }
  { }
  
  void host_context_t::initialize_frameworks(const corehost_init_t& init)
  {
      init.get_found_fx_versions(fx_versions_by_name);
      init.get_included_frameworks(included_fx_versions_by_name);
  }
  
  void host_context_t::close()
  {
      marker = closed_host_context_marker;
  }
  