#pragma once

#include "CoreMinimal.h"
#include "EditorSubsystem.h"
#include "WingBasics.h"
#include "WingTypes.generated.h"

struct FAssetData;
struct FEdGraphPinType;
struct WingTokenizer;

// ---------------------------------------------------------------------------
// UWingTypes — converts between FEdGraphPinType and a concise C++-like string.
// ---------------------------------------------------------------------------

UCLASS()
class UWingTypes : public UEditorSubsystem
{
	GENERATED_BODY()

public:
	// ---------------------------------------------------------------------------
	// Simple Accessors
	// ---------------------------------------------------------------------------

	// This struct is used to record the information about a type
	struct Info
	{
		FString Short;
		FName PinCategory;
		FName PinSubCategory;
		FString PinSubCategoryObject;
		FName BroadCategory;
		bool IsUserDefined = false;
	};

	// Get the full short-name-to-info map.
	static const TMap<FString, Info>& GetAllTypes();

public:
	// ---------------------------------------------------------------------------
	// Simple Utility Functions.
	// ---------------------------------------------------------------------------

	// True if the UClass, UStruct, or UEnum is a BlueprintType
	static bool IsBlueprintType(const UObject *Obj);

	// True if all parts of the pintype are blueprinttypes.
	static bool IsBlueprintType(const FEdGraphPinType &Type);

	// True if the pintype is a blueprintable interface or object.
	static bool IsBlueprintable(const FEdGraphPinType &Type);

	// True if the pintype is a child of the specified uclass.
	static bool IsChildOf(const FEdGraphPinType &Type, UClass *Class);
	
	// Get the native parent of any uclass.
	const UClass *FindNativeParent(const UClass *Class);

private:
	// ---------------------------------------------------------------------------
	// Choose Short Name
	// ---------------------------------------------------------------------------

	static FName BroadCategory(const UClass* Class);

	// Reserve the short name for a primitive type.
	void ReserveShortName(FName Name, FName PinCategory, FName PinSubCategory);
	void ReserveShortName(FName Name);

	// Get the short name if it already exists.
	FString GetShortName(const FString &Path);

	// Core version: choose a short name for a path that doesn't already
	// have a short name.  Records all the associated information.
	FString NewShortName(const FString &Path, FName PinCategory, FName BroadCategory, bool IsUserDefined);

	// Choose a short name for an already-loaded UObject.
	FString ChooseShortName(const UObject* Obj);

	// Choose a short name for an FAssetData.
	void ChooseShortName(const FAssetData &Data);

	// Choose short names for every type in the package.
	void ChooseShortNames(UPackage* Package);

private:
	// ---------------------------------------------------------------------------
	// TypeToText
	// ---------------------------------------------------------------------------

	// Return a type string for a primitive or wrapped type (No Containers)
	FString TypeToText(FName Category, FName SubCategory, UObject* SubCategoryObject);

public:
	// Convert a pin type to a type string.
	// Returns empty string on failure.
	static FString TypeToText(const FEdGraphPinType& PinType);

	// Convert a Property to a type string.
	// Returns empty string on failure.
	static FString TypeToText(const FProperty *Property);
	
	// Get the type name for a UClass, UScriptStruct, or UEnum.
	// Returns empty string on failure.
	static FString TypeToText(const UObject* Obj);

	// Get the type name for a UClass, UScriptStruct, or UEnum.
	// Check fail if the result is empty.
	static FString TypeToTextOrDie(const UObject* Obj);

private:
	// ---------------------------------------------------------------------------
	// Parser and ResolveShortName
	// ---------------------------------------------------------------------------

	static void PrintParseError(WingTokenizer& Tok, const TCHAR* Message, WingOut Errors);
	static bool ParseChar(WingTokenizer& Tok, TCHAR c, WingOut Errors);
	bool ParsePlainIdentifier(WingTokenizer& Tok, FEdGraphPinType& OutType, WingOut Errors);
	bool ParseWrapped(WingTokenizer& Tok, FName Wrapper, FEdGraphPinType& OutType, WingOut Errors);
	bool ParseMaybeWrapped(WingTokenizer& Tok, FEdGraphPinType& OutType, WingOut Errors);
	bool ParseArrayOrSet(WingTokenizer& Tok, FEdGraphPinType& OutType, WingOut Errors);
	bool ParseMap(WingTokenizer& Tok, FEdGraphPinType& OutType, WingOut Errors);
	bool ParseType(WingTokenizer& Tok, FEdGraphPinType& OutType, WingOut Errors);

	bool ResolveShortName(WingTokenizer& Tok, const FString &Name, FEdGraphPinType &OutType, WingOut Errors);

public:
	// ---------------------------------------------------------------------------
	// TextToType
	// ---------------------------------------------------------------------------

	struct Requirements
	{
		bool BlueprintType = false;
		bool Blueprintable = false;
		bool AllowContainer = false;
		bool AllowNone = false;
		UClass *IsChildOf = nullptr;
		FName PinCategory = FName();
	};


	// Parse a type.  If it doesn't parse, or if the type doesn't satisfy the
	// requirements, prints an error and returns false.  If CheckEOF is true,
	// makes sure that the input is *just* a type and nothing else after it.
	static bool TextToType(WingTokenizer &Tok, FEdGraphPinType& OutPinType, const Requirements &Require, bool CheckEOF, WingOut Errors);

	// Parse a type.  If it doesn't parse, or if the type doesn't satisfy the
	// requirements, prints an error and returns false.
	static bool TextToType(const FString& Text, FEdGraphPinType& OutPinType, const Requirements &Require, WingOut Errors);

private:
	// ---------------------------------------------------------------------------
	// Subsystem lifecycle
	// ---------------------------------------------------------------------------

	virtual void Initialize(FSubsystemCollectionBase& Collection) override;
	virtual void Deinitialize() override;

private:
	// ---------------------------------------------------------------------------
	// State
	// ---------------------------------------------------------------------------

	// Given a short name, get all the information about that type.
	TMap<FString, Info> ShortToInfo;

	// Given a class path, return the short name, e.g. "/Script/CoreUObject.Vector" -> "Vector"
	TMap<FString, FString> PathToShort;

};
