//---------------------------------------------------------------------------------------
void SkUERemote::get_project_info(SkProjectInfo * out_project_info_p)
  {
  // Get platform id string
  out_project_info_p->m_platform_id = FStringToAString(UGameplayStatics::GetPlatformName());

  // Get engine id string
  out_project_info_p->m_engine_id.ensure_size(20);
  out_project_info_p->m_engine_id.format("UE%d.%d.%d-%s", ENGINE_MAJOR_VERSION, ENGINE_MINOR_VERSION, ENGINE_PATCH_VERSION, BUILT_FROM_CHANGELIST ? "Installed" : "Compiled");

  // Get game name
  out_project_info_p->m_project_name = FStringToAString(FApp::GetGameName());

  // Name of generated scripts overlay
  TCHAR const * const generated_overlay_name_p = TEXT("Project-Generated");

  // Look for default SkookumScript project file in engine folder.
  FString default_project_path(FPaths::EnginePluginsDir() / TEXT("SkookumScript/Scripts/Skookum-project-default.ini"));
  SK_ASSERTX(FPaths::FileExists(default_project_path), a_str_format("Cannot find default project settings file '%S'!", *default_project_path));
  out_project_info_p->m_default_project_path = FStringToAString(FPaths::ConvertRelativePathToFull(default_project_path));

  // Check if we have loaded any game
  if (!out_project_info_p->m_project_name.is_empty())
    {
    // Look for specific SkookumScript project in game/project folder.
    // 1) Check permanent location
    FString project_path(FPaths::GameDir() / TEXT("Scripts/Skookum-project.ini"));
    if (FPaths::FileExists(project_path))
      {
      #if WITH_EDITORONLY_DATA
        if (m_editor_interface_p)
          {
          m_editor_interface_p->set_overlay_path(FPaths::GetPath(project_path), generated_overlay_name_p);
          }
      #endif
      }
    else
      {
      project_path.Empty();
      #if WITH_EDITORONLY_DATA
        if (m_editor_interface_p)
          {
          // 2) Check/create temp location
          project_path = m_editor_interface_p->ensure_temp_project(generated_overlay_name_p);
          SK_ASSERTX(!project_path.IsEmpty(), a_str_format("Could not generated project file '%S' for project '%s'!", *project_path, out_project_info_p->m_project_name.as_cstr()));
          }
      #endif
      }

    out_project_info_p->m_project_path = FStringToAString(FPaths::ConvertRelativePathToFull(project_path));
    }
  }
 //---------------------------------------------------------------------------------------
 // Get name of this Entity
 static void mthd_name(SkInvokedMethod * scope_p, SkInstance ** result_pp)
   {    
   if (result_pp) // Do nothing if result not desired
     {
     UObject * this_p = scope_p->this_as<SkUEEntity>();
     AString obj_name = this_p ? FStringToAString(this_p->GetName()) : "null";
     *result_pp = SkString::new_instance(obj_name);
     }
   }
 //---------------------------------------------------------------------------------------
 // # Skookum:   Delegate@String() String
 // # Author(s): Markus Breyer
 static void mthd_String(SkInvokedMethod * scope_p, SkInstance ** result_pp)
   {
   // Do nothing if result not desired
   if (result_pp)
     {
     const FScriptDelegate & script_delegate = scope_p->this_as<SkUEDelegate>();
     *result_pp = SkString::new_instance(FStringToAString(script_delegate.ToString<UObject>()));
     }
   }
//---------------------------------------------------------------------------------------
// 
void FSkookumScriptRuntime::show_ide(const FString & focus_class_name, const FString & focus_member_name, bool is_data_member, bool is_class_member)
  {
  #ifdef SKOOKUM_REMOTE_UNREAL
    // Remove qualifier from member name if present
    FString focus_class_name_ide = focus_class_name;
    FString focus_member_name_ide = focus_member_name;
    int32 at_pos = 0;
    if (focus_member_name_ide.FindChar('@', at_pos))
      {
      focus_class_name_ide = focus_member_name_ide.Left(at_pos).TrimTrailing();
      focus_member_name_ide = focus_member_name_ide.Mid(at_pos + 1).Trim();
      }

    // Convert to symbols and send off
    ASymbol focus_class_name_sym(ASymbol::create_existing(FStringToAString(focus_class_name_ide)));
    ASymbol focus_member_name_sym(ASymbol::create_existing(FStringToAString(focus_member_name_ide)));
    m_remote_client.cmd_show(AFlag_on, focus_class_name_sym, focus_member_name_sym, is_data_member, is_class_member);
  #endif
  }
  //---------------------------------------------------------------------------------------
  // Convert to String
  static void mthd_String(SkInvokedMethod * scope_p, SkInstance ** result_pp)
    {    
    if (result_pp) // Do nothing if result not desired
      {
      UObject * this_p = scope_p->this_as<SkUEEntity>();
      AString obj_name = this_p ? FStringToAString(this_p->GetName()) : "null";
      AString class_name = scope_p->get_this()->get_class()->get_name().as_string();
      AString uclass_name = this_p ? FStringToAString(this_p->GetClass()->GetName()) : "null";

      AString str(nullptr, 9u + obj_name.get_length() + class_name.get_length() + uclass_name.get_length(), 0u);
      str.append('"');
      str.append(obj_name);
      str.append("\" <", 3u);
      str.append(class_name);
      str.append("> (", 3u);
      str.append(uclass_name);
      str.append(')');
      *result_pp = SkString::new_instance(str);
      }
    }
  //---------------------------------------------------------------------------------------
  // # Skookum:   UClass@String() String
  // # Author(s): Markus Breyer
  static void mthd_String(SkInvokedMethod * scope_p, SkInstance ** result_pp)
    {
    if (result_pp) // Do nothing if result not desired
      {
      UClass * uclass_p = scope_p->this_as<SkUEEntityClass>();
      AString uclass_name = FStringToAString(uclass_p->GetName());

      AString str(nullptr, 3u + uclass_name.get_length(), 0u);
      str.append('(');
      str.append(uclass_name);
      str.append(')');
      *result_pp = SkString::new_instance(str);
      }
    }
//---------------------------------------------------------------------------------------
// Resolve the raw data info of each raw data member of the given class
void SkUEClassBindingHelper::resolve_raw_data(SkClass * class_p, UStruct * ue_struct_or_class_p)
  {
  // This loop assumes that the data members of the Sk class were created from this very UE4 class
  // I.e. that therefore, except for unsupported properties, they must be in the same order
  // So all we should have to do is loop forward and skip the occasional non-exported UE4 property
  UProperty * ue_var_p = nullptr;
  ASymbol ue_var_name;
  TFieldIterator<UProperty> property_it(ue_struct_or_class_p, EFieldIteratorFlags::ExcludeSuper);
  tSkTypedNameRawArray & raw_data = class_p->get_instance_data_raw_for_resolving();
  for (auto var_p : raw_data)
    {
    // Skip variable if already resolved
    if (var_p->m_raw_data_info != SkRawDataInfo_Invalid)
      { 
      continue;
      }

    // Try to find it in the UE4 reflection data
    while (property_it)
      {
      ue_var_p = *property_it;
      ue_var_name = ASymbol::create_existing(FStringToAString(FSkookumScriptGeneratorBase::skookify_var_name(ue_var_p->GetName(), ue_var_p->IsA(UBoolProperty::StaticClass()), true)));
      ++property_it;
      if (var_p->get_name() == ue_var_name) break;
      }

    // Store raw data info in the raw data member object
    if (var_p->get_name() == ue_var_name)
      {
      var_p->m_raw_data_info = compute_raw_data_info(ue_var_p);
      }
    else
      {
      // Oops didn't find matching variable
      // This is probably due to an unsaved blueprint variable change in the UE4 editor during the previous session
      // If this is the case, a recompile would have been triggered when this class was loaded by get_ue_class_from_sk_class()
      // Which means binaries would be recompiled and reloaded once more, fixing this issue
      // So make sure this assumption is true
      SK_ASSERTX(FModuleManager::Get().GetModulePtr<ISkookumScriptRuntime>("SkookumScriptRuntime")->is_freshen_binaries_pending(), a_str_format("Sk Variable '%s.%s' not found in UE4 reflection data.", class_p->get_name_cstr_dbg(), var_p->get_name_cstr()));
      }
    }
  }
//---------------------------------------------------------------------------------------
// Make this editable and tell IDE about it
void SkUERemote::on_cmd_make_editable()
  {
  SkProjectInfo project_info;

  FString error_msg(TEXT("Can't make project editable!"));
  #if WITH_EDITORONLY_DATA
    if (m_editor_interface_p)
      {
      error_msg = m_editor_interface_p->make_project_editable();
      }
  #endif
  if (error_msg.IsEmpty())
    {
    get_project_info(&project_info);
    SkookumRuntimeBase::ms_singleton_p->on_binary_hierarchy_path_changed();
    }

  // Send result back
  cmd_make_editable_reply(FStringToAString(error_msg), project_info);
  }
  //---------------------------------------------------------------------------------------
  // Description Returns constants / values used by the SkookumScript library.
  // Returns     constants / values used by the SkookumScript library.
  // Examples    Called internally
  // Author(s)   Conan Reis
  SkookumVals & get_lib_vals()
    {
    static SkookumVals s_values;

    if (s_values.m_using_defaults)
      {
      // Set custom initial values
      s_values.m_using_defaults = false;

      // Unreal uses its own actor class
      s_values.m_use_builtin_actor = false; 
      s_values.m_custom_actor_class_name = "Actor";

      // Get platform name
      s_values.m_platform_id_string = FStringToAString(UGameplayStatics::GetPlatformName());

      // Get engine name
      s_values.m_engine_id_string.ensure_size(20);
      s_values.m_engine_id_string.format("UE%d.%d.%d-%s", ENGINE_MAJOR_VERSION, ENGINE_MINOR_VERSION, ENGINE_PATCH_VERSION, BUILT_FROM_CHANGELIST ? "Installed" : "Compiled");
      }

    return s_values;
    }
SkInstance * SkUEBlueprintInterface::fetch_k2_param_string(FFrame & stack)
  {
  UStrProperty::TCppType value = UStrProperty::GetDefaultPropertyValue();
  stack.StepCompiledIn<UStrProperty>(&value);
  return SkString::new_instance(FStringToAString(value));
  }