//---------------------------------------------------------------------------------------
void FSkookumScriptRuntime::on_world_init_pre(UWorld * world_p, const UWorld::InitializationValues init_vals)
  {
  //A_DPRINT("on_world_init_pre: %S %p\n", *world_p->GetName(), world_p);

  // Use this callback as an opportunity to take care of connecting to the IDE
  #ifdef SKOOKUM_REMOTE_UNREAL
    if (!IsRunningCommandlet() && !m_remote_client.is_authenticated())
      {
      m_remote_client.attempt_connect(0.0, true, true);
      }
  #endif  

  if (world_p->IsGameWorld())
    {
    if (!m_game_world_p)
      {
      m_game_world_p = world_p;
      if (is_skookum_initialized())
        {
        SkUEClassBindingHelper::set_world(world_p);
        }
      m_game_tick_handle = world_p->OnTickDispatch().AddRaw(this, &FSkookumScriptRuntime::tick_game);
      }
    }
  else if (world_p->WorldType == EWorldType::Editor)
    {
    if (!m_editor_world_p)
      {
      m_editor_world_p = world_p;
      m_editor_tick_handle = world_p->OnTickDispatch().AddRaw(this, &FSkookumScriptRuntime::tick_editor);
      }
    }
  }
//---------------------------------------------------------------------------------------
// 
void FSkookumScriptRuntime::tick_remote()
  {
  if (!IsRunningCommandlet())
    {
    // Request recompilation of binaries if script files changed
    if (m_freshen_binaries_requested)
      {
      m_remote_client.cmd_compiled_state(true);
      m_freshen_binaries_requested = false;
      }

    // Remote communication to and from SkookumScript IDE.
    // Needs to be called whether in editor or game and whether paused or not
    // $Revisit - CReis This is probably a hack. The remote client update should probably
    // live somewhere other than a tick method such as its own thread.
    m_remote_client.process_incoming();

    // Re-load compiled binaries?
    if (m_remote_client.is_load_compiled_binaries_requested())
      {
      // Load the Skookum class hierarchy scripts in compiled binary form
      #if WITH_EDITOR
        bool is_first_time = !is_skookum_initialized();
      #endif

      bool success_b = m_runtime.load_compiled_scripts();
      SK_ASSERTX(success_b, AErrMsg("Unable to load SkookumScript compiled binaries!", AErrLevel_notify));
      m_remote_client.clear_load_compiled_binaries_requested();

      #if WITH_EDITOR
        if (is_first_time && is_skookum_initialized())
          {
          // When we load the binaries for the very first time, try regenerating all generated class script files again,
          // as the editor might have tried to generate them before but skipped because SkookumScript was not initialized yet
          m_runtime.get_editor_interface()->generate_all_class_script_files();
          // Also recompile Blueprints in error state as such error state might have been due to SkookumScript not being initialized at the time of compile
          m_runtime.get_editor_interface()->recompile_blueprints_with_errors();
          // Set world pointer now
          SkUEClassBindingHelper::set_world(m_game_world_p);
          }
      #endif
      }
    }
  }
//---------------------------------------------------------------------------------------
void FSkookumScriptRuntime::on_world_init_pre(UWorld * world_p, const UWorld::InitializationValues init_vals)
  {
  //A_DPRINT("on_world_init_pre: %S %p\n", *world_p->GetName(), world_p);

  // Make sure atomics are bound by now
  if (m_runtime.is_compiled_scripts_loaded() && !m_runtime.is_compiled_scripts_bound())
    {
    m_runtime.bind_compiled_scripts();
    }

  // Use this callback as an opportunity to take care of connecting to the IDE
  #ifdef SKOOKUM_REMOTE_UNREAL
    if (!IsRunningCommandlet() && !m_remote_client.is_authenticated())
      {
      m_remote_client.attempt_connect(0.0, true, true);
      }
  #endif  

  if (world_p->IsGameWorld())
    {
    // Keep track of how many game worlds we got
    ++m_num_game_worlds;

    if (!m_game_world_p)
      {
      m_game_world_p = world_p;
      if (is_skookum_initialized())
        {
        SkUEClassBindingHelper::set_world(world_p);
        SkookumScript::initialize_gameplay();
        }
      m_game_tick_handle = world_p->OnTickDispatch().AddRaw(this, &FSkookumScriptRuntime::tick_game);
      }
    }
  else if (world_p->WorldType == EWorldType::Editor)
    {
    if (!m_editor_world_p)
      {
      m_editor_world_p = world_p;
      m_editor_tick_handle = world_p->OnTickDispatch().AddRaw(this, &FSkookumScriptRuntime::tick_editor);
      }
    }
  }
//---------------------------------------------------------------------------------------
void FSkookumScriptRuntime::on_world_cleanup(UWorld * world_p, bool session_ended_b, bool cleanup_resources_b)
  {
  //A_DPRINT("on_world_cleanup: %S %p\n", *world_p->GetName(), world_p);

  if (world_p->IsGameWorld())
    {
    // Keep track of how many game worlds we got
    --m_num_game_worlds;

    // Set world pointer to null if it was pointing to us
    if (m_game_world_p == world_p)
      {
      m_game_world_p->OnTickDispatch().Remove(m_game_tick_handle);
      m_game_world_p = nullptr;
      SkUEClassBindingHelper::set_world(nullptr);
      }

    // Restart SkookumScript if initialized
    if (m_num_game_worlds == 0 && is_skookum_initialized())
      {
      // Simple shutdown
      //SkookumScript::get_world()->clear_coroutines();
      A_DPRINT(
        "SkookumScript resetting session...\n"
        "  cleaning up...\n");
      SkookumScript::deinitialize_gameplay();
      SkookumScript::deinitialize_sim();
      SkookumScript::initialize_sim();
      A_DPRINT("  ...done!\n\n");
      }
    }
  else if (world_p->WorldType == EWorldType::Editor)
    {
    // Set world pointer to null if it was pointing to us
    if (m_editor_world_p == world_p)
      {
      m_editor_world_p->OnTickDispatch().Remove(m_editor_tick_handle);
      m_editor_world_p = nullptr;
      }
    }
  }
//---------------------------------------------------------------------------------------
// 
void FSkookumScriptRuntime::tick_remote()
  {
  if (!IsRunningCommandlet())
    {
    // Request recompilation of binaries if script files changed
    if (m_freshen_binaries_requested)
      {
      m_remote_client.cmd_compiled_state(true);
      m_freshen_binaries_requested = false;
      }

    // Remote communication to and from SkookumScript IDE.
    // Needs to be called whether in editor or game and whether paused or not
    // $Revisit - CReis This is probably a hack. The remote client update should probably
    // live somewhere other than a tick method such as its own thread.
    m_remote_client.process_incoming();

    // Re-load compiled binaries?
    // If the game is currently running, delay until it's not
    if (m_remote_client.is_load_compiled_binaries_requested() 
     && SkookumScript::get_initialization_level() < SkookumScript::InitializationLevel_gameplay)
      {
      // Makes sure the SkookumScript runtime object is initialized at this point
      ensure_runtime_initialized();

      // Load the Skookum class hierarchy scripts in compiled binary form
      bool is_first_time = !is_skookum_initialized();

      bool success_b = m_runtime.load_and_bind_compiled_scripts();
      SK_ASSERTX(success_b, AErrMsg("Unable to load SkookumScript compiled binaries!", AErrLevel_notify));
      m_remote_client.clear_load_compiled_binaries_requested();

      // After reloading, re-resolve the raw data of all dynamic classes
      #if WITH_EDITORONLY_DATA
        TArray<UObject*> blueprint_array;
        GetObjectsOfClass(UBlueprint::StaticClass(), blueprint_array, true, RF_ClassDefaultObject);
        for (UObject * obj_p : blueprint_array)
          {
          UBlueprint * blueprint_p = static_cast<UBlueprint *>(obj_p);
          if (blueprint_p->GeneratedClass)
            {
            SkClass * sk_class_p = SkUEClassBindingHelper::get_sk_class_from_ue_class(blueprint_p->GeneratedClass);
            if (sk_class_p)
              {
              SkUEClassBindingHelper::resolve_raw_data(sk_class_p, blueprint_p->GeneratedClass);
              }
            }
          }
      #endif

      if (is_first_time && is_skookum_initialized())
        {
        #if WITH_EDITOR
          // Recompile Blueprints in error state as such error state might have been due to SkookumScript not being initialized at the time of compile
          if (m_runtime.get_editor_interface())
            {
            m_runtime.get_editor_interface()->recompile_blueprints_with_errors();
            }
        #endif
        }
      }
    }
  }