#pragma once

#include "CoreMinimal.h"
#include "WingUtils.h"
// A resolved property: the FProperty descriptor plus a pointer to
// the value's storage.  operator-> forwards to the FProperty.
struct FWingProperty
{

	FProperty* Prop = nullptr;
	void* Container = nullptr;

	FWingProperty() = default;
	FWingProperty(FProperty* InProp, void* InContainer);
	FWingProperty(FProperty* InProp, UObject* InContainer);

	bool SetDouble(double D);
	bool SetInt64(int64 I);
	bool SetBool(bool B);

	FString GetText() const;
	bool SetText(FString Value);

	// Store data from a json object.
	bool SetJson(const TSharedPtr<FJsonValue> &Value);

	// Get the Category metadata.
	FString GetCategory();

	// Get the text, replace newlines with whitespace, and
	// truncate to the specified maximum length.
	FString GetTruncatedText(int32 MaxLen) const;

	explicit operator bool() const { return Prop != nullptr; }
	FProperty* operator->() const { return Prop; }

	// Return the "Details Panel" properties for the specified object.
	//
	// This is the set of properties that would typically appear in the
	// details panel if you were to click the object in the editor.  Note
	// that this is not always the properties of the object itself.  For
	// example, if you click a widget, the details panel shows you
	// properties of the widget, but also properties of the slot.  If you
	// click a material graph node, the details panel shows you properties
	// of the node, but also properties of the material expression.
	//
	// When editing an inherited ActorComponent, you're actually editing
	// properties that *override* the original properties of the ActorComponent.
	// The mutable version, 'GetDetailsMutable', tells this function to first
	// create the overrides, and then edit those.
	// 
	// For a more direct "just give me the properties of this object,"
	// use the GetAll function.
	//
	static TArray<FWingProperty> GetDetailsMutable(UObject* Obj, EPropertyFlags Flags)
		{ return GetDetailsGeneral(Obj, Flags, true); }
	static TArray<FWingProperty> GetDetailsImmutable(UObject *Obj, EPropertyFlags Flags)
		{ return GetDetailsGeneral(Obj, Flags, false); }

	// Get the raw properties of the specified object or struct.
	//
	// This gets the properties that are literally present in the
	// specified object or struct.  No special interpretation is done.
	//
	static TArray<FWingProperty> GetAll(UObject* Object, EPropertyFlags Flags);
	static TArray<FWingProperty> GetAll(UStruct* StructType, void* Container, EPropertyFlags Flags);

	// Get the names of the properties in the specified class.
	//
	static TArray<FName> GetNames(UStruct *StructType, EPropertyFlags Flags);

	// Get the named properties.
	//
	static TArray<FWingProperty> GetNamed(UStruct* StructType, void* Container, const TArray<FName> &Names);

	// Functions to find items by name in an array of properties.
	//
	static TArray<FWingProperty> FindAllSubstring(const TArray<FWingProperty>& Props, const FString& Substring);
	static void Remove(TArray<FWingProperty>& Props, const FString& Name);
	static void Move(TArray<FWingProperty> &Out, TArray<FWingProperty> &In, const FString &Name);

	// Functions to populate properties from a JSON object.
	//
	static bool PopulateFromJson(FWingProperty& Prop, const FJsonObject* Json, bool AllOptional = false);
	static bool PopulateFromJson(TArray<FWingProperty>& Props, const FJsonObject* Json, bool AllOptional = false);
	static bool PopulateFromJson(UStruct* StructType, void* Container, const FJsonObject* Object);
	static bool PopulateFromJson(UStruct* StructType, void* Container, const TSharedPtr<FJsonValue>& Object);

private:
	static TArray<FWingProperty> GetDetailsGeneral(UObject* Obj, EPropertyFlags Flags, bool Mutable);
	void PrintExpectsReceived(const TCHAR *Type);
	static void Collect(UStruct* Struct, void* Container, TArray<FWingProperty> &Props, EPropertyFlags Flags);
	bool CheckImportTextResult(const FString &Value);
};
