//---------------------------------------------------------------------------------------
void USkookumScriptComponent::create_sk_instance()
  {
  SK_ASSERTX(!m_instance_p, "Tried to create instance when instance already present!");

  // Find the actor I belong to
  AActor * actor_p = GetOwner();
  SK_ASSERTX(actor_p, "USkookumScriptComponent must be attached to an actor.");

  // Determine SkookumScript class of my actor
  SkClass * class_p = nullptr;
  FString class_name = ScriptClassName;
  if (!class_name.IsEmpty())
    {
    AString class_name_ascii(*class_name, class_name.Len());
    class_p = SkBrain::get_class(class_name_ascii.as_cstr());
    SK_ASSERTX(class_p, a_cstr_format("Cannot find Script Class Name '%s' specified in SkookumScriptComponent of '%S'. Misspelled?", class_name_ascii.as_cstr(), *actor_p->GetName()));
    if (!class_p) goto set_default_class; // Recover from bad user input

    // Do some extra checking in non-shipping builds
    #if (SKOOKUM & SK_DEBUG)
      // Find most derived SkookumScript class known to UE4
      SkClass * known_super_class_p;
      for (known_super_class_p = class_p; known_super_class_p; known_super_class_p = known_super_class_p->get_superclass())
        {
        if (SkUEClassBindingHelper::get_ue_class_from_sk_class(known_super_class_p)) break;
        }

      // Find most derived UE4 class known to SkookumScript
      SkClass * mapped_class_p = nullptr;
      for (UClass * obj_uclass_p = actor_p->GetClass(); !mapped_class_p && obj_uclass_p; obj_uclass_p = obj_uclass_p->GetSuperClass())
        {
        mapped_class_p = SkUEClassBindingHelper::get_sk_class_from_ue_class(obj_uclass_p);
        }
      SK_ASSERTX(mapped_class_p && mapped_class_p == known_super_class_p, a_cstr_format("Script Class Name '%s' in SkookumScriptComponent of '%S' is not properly related to Actor. Both the Script Class Name '%s' and the UE4 class of '%S' ('%S') must share the topmost ancestor class known to both SkookumScript and UE4. Right now these ancestor classes are different ('%s' for '%s' and '%s' for '%S').", class_name_ascii.as_cstr(), *actor_p->GetName(), class_name_ascii.as_cstr(), *actor_p->GetName(), *actor_p->GetClass()->GetName(), known_super_class_p ? known_super_class_p->get_name_cstr_dbg() : "<none>", class_name_ascii.as_cstr(), mapped_class_p ? mapped_class_p->get_name_cstr_dbg() : "<none>", *actor_p->GetClass()->GetName()));
    #endif
    }
  else
    {
  set_default_class:
    // Find most derived UE4 class known to SkookumScript
    class_p = nullptr; // Is already null when we get here, but set again for clarity
    for (UClass * obj_uclass_p = actor_p->GetClass(); !class_p && obj_uclass_p; obj_uclass_p = obj_uclass_p->GetSuperClass())
      {
      class_p = SkUEClassBindingHelper::get_sk_class_from_ue_class(obj_uclass_p);
      }
    SK_ASSERTX(class_p, a_cstr_format("No parent class of %S is known to SkookumScript!", *actor_p->GetClass()->GetName()));
    if (!class_p)
      {
      class_p = SkBrain::get_class(ASymbol_Actor); // Recover from bad user input
      }
    }

  // Based on the desired class, create SkInstance or SkDataInstance
  SkInstance * instance_p = class_p->new_instance();
  instance_p->construct<SkUEActor>(actor_p); // Keep track of owner actor
  m_instance_p = instance_p;
  }
 static void assert_actor_has_overlap_events_enabled(AActor * actor_p)
   {
   // Check that events will properly fire
   TArray<UActorComponent *> components = actor_p->GetComponentsByClass(UPrimitiveComponent::StaticClass());
   SK_ASSERTX(components.Num() > 0, a_cstr_format("Trying to receive overlap events on actor '%S' but it has no primitive (collision) component.", *actor_p->GetName()));
   bool found_enabled_overlap_event = false;
   for (UActorComponent * component_p : components)
     {
     if (Cast<UPrimitiveComponent>(component_p)->bGenerateOverlapEvents)
       {
       found_enabled_overlap_event = true;
       break;
       }
     }
   SK_ASSERTX(found_enabled_overlap_event, a_cstr_format("Trying to receive overlap events on actor '%S' but it has no primitive component that has overlap events turned on. To fix this, check the box 'Generate Overlap Events' for the primitive component (e.g. SkeletalMeshComponent, CapsuleComponent etc.) that you would like to trigger the overlap events. You might also simply have picked the wrong actor.", *actor_p->GetName()));
   }
//---------------------------------------------------------------------------------------
// Gets memory representing binary for group of classes with specified class as root.
// Used as a mechanism to "demand load" scripts.
// 
// #See Also:   load_compiled_scripts(), SkCompiler::get_binary_class_group()
// #Modifiers:  virtual - overridden from SkookumRuntimeBase
// #Author(s):  Conan Reis
SkBinaryHandle * SkUERuntime::get_binary_class_group(const SkClass & cls)
  {
  FString compiled_file = get_compiled_path();
  
  // $Revisit - CReis Should use fast custom uint32_t to hex string function.
  compiled_file += a_cstr_format("/Class[%x].sk-bin", cls.get_name_id());
  return SkBinaryHandleUE::create(*compiled_file);
  }
 //---------------------------------------------------------------------------------------
 // Get Unreal Engine class of this class
 static void mthdc_static_class(SkInvokedMethod * scope_p, SkInstance ** result_pp)
   {
   if (result_pp) // Do nothing if result not desired
     {
     // Determine class
     SkClass * sk_class_p = ((SkMetaClass *)scope_p->get_topmost_scope())->get_class_info();
     UClass * ue_class_p = SkUEClassBindingHelper::get_ue_class_from_sk_class(sk_class_p);
     SK_ASSERTX(ue_class_p, a_cstr_format("The UE4 equivalent of class type '%s' is not known to SkookumScript. Maybe it is the class of a Blueprint that is not loaded yet?", sk_class_p->get_name_cstr_dbg()));
     *result_pp = ue_class_p ? SkUEEntityClass::new_instance(ue_class_p) : SkBrain::ms_nil_p;
     }
   }
//---------------------------------------------------------------------------------------
// Determines if the symbol order and symbol ids are valid.
// Notes:      This is a test function that should hopefully never need to be called.
//             It was written to discover a potential memory stomp bug.
// Author(s):   Conan Reis
void ASymbolTable::validate() const
  {
  uint32_t length = m_sym_refs.get_length();

  if (length)
    {
    ASymbolRef ** syms_pp     = m_sym_refs.get_array();  // for faster than class member access
    ASymbolRef ** syms_end_pp = syms_pp + length;

    uint32_t     id;
    uint32_t     sym_id;
    ASymbolRef * sym_p;
    ASymbolRef * prev_sym_p = nullptr;

    for (; syms_pp < syms_end_pp; syms_pp++)
      {
      sym_p  = *syms_pp;
      id     = ASYMBOL_CSTR_TO_ID(sym_p->m_str_ref_p->m_cstr_p, sym_p->m_str_ref_p->m_length);
	  sym_id = sym_p->m_uid;

	  A_VERIFYX(
        id == sym_id,
		a_cstr_format(
          "Stored symbol '%s'#%u should have id #%u!",
          sym_p->m_str_ref_p->m_cstr_p, sym_id, id));

      if (prev_sym_p)
        {
	    A_VERIFYX(
          (prev_sym_p->m_uid < sym_id),
		  a_cstr_format(
            "Symbol ids '%s'#%u and '%s'#%u are not in proper sequence!",
            prev_sym_p->m_str_ref_p->m_cstr_p, prev_sym_p->m_uid, sym_p->m_str_ref_p->m_cstr_p, sym_id));
        }

      prev_sym_p = sym_p;
      }
    }
  }
  //---------------------------------------------------------------------------------------
  // !new constructor - creates new object
  void mthd_ctor_new(SkInvokedMethod * scope_p, SkInstance ** result_pp)
    {
    UObject * outer_p = scope_p->get_arg<SkUEEntity>(SkArg_1);
    FName name = scope_p->get_arg<SkUEName>(SkArg_2);
    uint32_t flags = scope_p->get_arg<SkInteger>(SkArg_3);
    
    // The scope of a constructor is always some form of an SkInstance
    SkInstance * receiver_p = static_cast<SkInstance *>(scope_p->m_scope_p.get_obj());

    UClass * ue_class_p = SkUEClassBindingHelper::get_ue_class_from_sk_class(receiver_p->get_class());
    SK_ASSERTX(ue_class_p, a_cstr_format("The UE4 equivalent of class type '%s' is not known to SkookumScript. Maybe it is the class of a Blueprint that is not loaded yet?", receiver_p->get_class()->get_name_cstr_dbg()));
    scope_p->get_this()->construct<SkUEEntity>(NewObject<UObject>(outer_p, ue_class_p, name, EObjectFlags(flags)));
    }
  //---------------------------------------------------------------------------------------
  // Entity@default() ThisClass_
  static void mthdc_default(SkInvokedMethod * scope_p, SkInstance ** result_pp)
    {
    if (result_pp) // Do nothing if result not desired
      {
      // Determine class of object to get
      SkClass * sk_class_p = ((SkMetaClass *)scope_p->get_topmost_scope())->get_class_info();
      UClass * ue_class_p = SkUEClassBindingHelper::get_ue_class_from_sk_class(sk_class_p);
      SK_ASSERTX(ue_class_p, a_cstr_format("Cannot get default instance of class '%s' as the UE4 equivalent of this class is not known to SkookumScript. Maybe it is the class of a Blueprint that is not loaded yet?", sk_class_p->get_name_cstr_dbg()));

      // Get default object
      UObject * obj_p = nullptr;
      if (ue_class_p)
        {
        obj_p = GetMutableDefault<UObject>(ue_class_p);
        }

      *result_pp = obj_p ? SkUEEntity::new_instance(obj_p, ue_class_p, sk_class_p) : SkBrain::ms_nil_p;
      }
    }
  //---------------------------------------------------------------------------------------
  // Entity@load(String name) ThisClass_
  static void mthdc_load(SkInvokedMethod * scope_p, SkInstance ** result_pp)
    {
    if (result_pp) // Do nothing if result not desired
      {
      // Determine class of object to load
      SkClass * class_p = &((SkMetaClass *)scope_p->get_topmost_scope())->get_class_info();
      UClass * uclass_p = SkUEClassBindingHelper::get_ue_class_from_sk_class(class_p);
      SK_ASSERTX(uclass_p, a_cstr_format("Cannot load entity '%s' as the UE4 equivalent of class type '%s' is not known to SkookumScript.", scope_p->get_arg<SkString>(SkArg_1).as_cstr(), class_p->get_name_cstr_dbg()));

      // Load object
      UObject * obj_p = nullptr;
      if (uclass_p)
        {
        obj_p = StaticLoadObject(uclass_p, SkUEClassBindingHelper::get_world(), *AStringToFString(scope_p->get_arg<SkString>(SkArg_1)));
        }

      *result_pp = obj_p ? SkUEEntity::new_instance(obj_p, uclass_p, class_p) : SkBrain::ms_nil_p;
      }
    }
  //---------------------------------------------------------------------------------------
  // Entity@load(String name) ThisClass_
  static void mthdc_load(SkInvokedMethod * scope_p, SkInstance ** result_pp)
    {
    // Load it regardless if a result is desired

    // Determine class of object to load
    SkClass * sk_class_p = ((SkMetaClass *)scope_p->get_topmost_scope())->get_class_info();
    UClass * ue_class_p = SkUEClassBindingHelper::get_ue_class_from_sk_class(sk_class_p);
    SK_ASSERTX(ue_class_p, a_cstr_format("Cannot load entity '%s' as the UE4 equivalent of class type '%s' is not known to SkookumScript. Maybe it is the class of a Blueprint that is not loaded yet?", scope_p->get_arg<SkString>(SkArg_1).as_cstr(), sk_class_p->get_name_cstr_dbg()));

    // Load object
    UObject * obj_p = nullptr;
    if (ue_class_p)
      {
      obj_p = StaticLoadObject(ue_class_p, SkUEClassBindingHelper::get_world(), *AStringToFString(scope_p->get_arg<SkString>(SkArg_1)));
      }

    // Set result if desired
    if (result_pp)
      {
      *result_pp = obj_p ? SkUEEntity::new_instance(obj_p, ue_class_p, sk_class_p) : SkBrain::ms_nil_p;
      }
    }
UProperty * SkUEBlueprintInterface::build_ue_param(UFunction * ue_function_p, SkClassDescBase * sk_parameter_class_p, const FName & param_name, ParamInfo * out_param_info_p)
  {
  // Based on Sk type, figure out the matching UProperty as well as fetcher and setter methods
  UProperty * property_p = nullptr;
  tK2ParamFetcher k2_param_fetcher_p = nullptr;
  tSkValueGetter sk_value_getter_p = nullptr;
  if (sk_parameter_class_p == SkBoolean::ms_class_p)
    {
    property_p = NewObject<UBoolProperty>(ue_function_p, param_name, RF_Public);
    k2_param_fetcher_p = &fetch_k2_param_boolean;
    sk_value_getter_p = &get_sk_value_boolean;
    }
  else if (sk_parameter_class_p == SkInteger::ms_class_p)
    {
    property_p = NewObject<UIntProperty>(ue_function_p, param_name, RF_Public);
    k2_param_fetcher_p = &fetch_k2_param_integer;
    sk_value_getter_p = &get_sk_value_integer;
    }
  else if (sk_parameter_class_p == SkReal::ms_class_p)
    {
    property_p = NewObject<UFloatProperty>(ue_function_p, param_name, RF_Public);
    k2_param_fetcher_p = &fetch_k2_param_real;
    sk_value_getter_p = &get_sk_value_real;
    }
  else if (sk_parameter_class_p == SkString::ms_class_p)
    {
    property_p = NewObject<UStrProperty>(ue_function_p, param_name, RF_Public);
    k2_param_fetcher_p = &fetch_k2_param_string;
    sk_value_getter_p = &get_sk_value_string;
    }
  else if (sk_parameter_class_p == SkVector3::ms_class_p)
    {
    UStructProperty * struct_property_p = NewObject<UStructProperty>(ue_function_p, param_name);
    struct_property_p->Struct = m_struct_vector3_p;
    property_p = struct_property_p;
    k2_param_fetcher_p = &fetch_k2_param_vector3;
    sk_value_getter_p = &get_sk_value_vector3;
    }
  else if (sk_parameter_class_p == SkRotationAngles::ms_class_p)
    {
    UStructProperty * struct_property_p = NewObject<UStructProperty>(ue_function_p, param_name);
    struct_property_p->Struct = m_struct_rotation_angles_p;
    property_p = struct_property_p;
    k2_param_fetcher_p = &fetch_k2_param_rotation_angles;
    sk_value_getter_p = &get_sk_value_rotation_angles;
    }
  else if (sk_parameter_class_p == SkTransform::ms_class_p)
    {
    UStructProperty * struct_property_p = NewObject<UStructProperty>(ue_function_p, param_name);
    struct_property_p->Struct = m_struct_transform_p;
    property_p = struct_property_p;
    k2_param_fetcher_p = &fetch_k2_param_transform;
    sk_value_getter_p = &get_sk_value_transform;
    }
  else if (sk_parameter_class_p->get_key_class()->is_class(*SkUEEntity::ms_class_p))
    {
    UClass * uclass_p = SkUEClassBindingHelper::get_ue_class_from_sk_class(sk_parameter_class_p);
    SK_ASSERTX(uclass_p, a_cstr_format("Class '%s' of parameter '%s' of method '%S.%S' being exported to Blueprints is not a known engine class.", sk_parameter_class_p->get_key_class_name().as_cstr_dbg(), param_name.GetPlainANSIString(), *ue_function_p->GetOwnerClass()->GetName(), *ue_function_p->GetName()));
    if (uclass_p)
      {
      property_p = NewObject<UObjectProperty>(ue_function_p, param_name, RF_Public);
      static_cast<UObjectProperty *>(property_p)->PropertyClass = uclass_p;
      k2_param_fetcher_p = &fetch_k2_param_entity;
      sk_value_getter_p = &get_sk_value_entity;
      }
    }
  else
    {
    //SK_ASSERTX(false, a_cstr_format("Class '%s' of parameter '%s' of method '%S.%S' being exported to Blueprints can not be mapped to a Blueprint-compatible type.", sk_parameter_class_p->get_key_class_name().as_cstr_dbg(), param_name.GetPlainANSIString(), *ue_function_p->GetOwnerClass()->GetName(), *ue_function_p->GetName()));
    }

  // Add flags
  if (property_p)
    {
    property_p->PropertyFlags |= CPF_Parm;
    ue_function_p->LinkChild(property_p);
    }

  // Set result
  if (out_param_info_p)
    {
    out_param_info_p->m_ue_param_p = property_p;
    out_param_info_p->m_k2_param_fetcher_p = k2_param_fetcher_p;
    out_param_info_p->m_sk_value_getter_p = sk_value_getter_p;
    }

  return property_p;
  }