#include "WingManual.h"
#include "WingServer.h"
#include "WingBasics.h"
#include "WingTypes.h"
#include "WingUtils.h"

void WingManual::PrintHandlerPrototype(const FWingHandlerConfig& Handler)
{
	WingOut::Stdout.Print(TEXT("ue-wingman "));
	WingOut::Stdout.Print(Handler.Name);
	for (TFieldIterator<FProperty> PropIt(Handler.HandlerClass.Get(), EFieldIterationFlags::None); PropIt; ++PropIt)
	{
		FStructProperty* StructProp = CastField<FStructProperty>(*PropIt);
		const bool bIsRest =
			StructProp && (StructProp->Struct == FWingRestOfArgv::StaticStruct());
		if (bIsRest)
		{
			WingOut::Stdout.Printf(TEXT(" [%s...]"), *PropIt->GetName());
		}
		else
		{
			WingOut::Stdout.Printf(TEXT(" %s"), *PropIt->GetName());
		}
	}
	WingOut::Stdout.Print(TEXT("\n"));
}

void WingManual::PrintHandlerArguments(const FWingHandlerConfig& Handler)
{
	// parameter details
	for (TFieldIterator<FProperty> PropIt(Handler.HandlerClass.Get(), EFieldIterationFlags::None); PropIt; ++PropIt)
	{
		FProperty* Prop = *PropIt;
		FString Name = Prop->GetName();
		FString Desc = Prop->GetMetaData(TEXT("Description"));
		if (Desc.IsEmpty()) Desc = TEXT("No documentation");

		WingOut::Stdout.Printf(TEXT("   %s - %s\n"), *Name, *Desc);
	}
}

void WingManual::PrintHandlerDescription(const FWingHandlerConfig& Handler)
{
	if (Handler.Documentation.IsEmpty()) return;
	WingOut::Stdout.Printf(TEXT("\n%s\n\n"), *Handler.Documentation);
}

void WingManual::PrintHandlerHelp(const FWingHandlerConfig& Handler)
{
	WingOut::Stdout.Print(TEXT("\n"));
	PrintHandlerPrototype(Handler);
	PrintHandlerArguments(Handler);
	PrintHandlerDescription(Handler);
	WingOut::Stdout.Print(TEXT("\n"));
}

void UWingManualSections::FetcherPaths()
{
	WingOut::Stdout.Print(TEXT(
		"\n FETCHER PATHS:"
		"\n"
		"\n   Most commands require you to specify a 'fetcher path'."
		"\n   A fetcher path starts with an asset name, followed by"
		"\n   steps that navigate into the asset.  Some Examples:"
		"\n"
		"\n    /Game/Widgets/WB_Hotkeys,widget:Canvas.122"
		"\n    /Game/Testing/BP_Test,graph:Rescale.Actor,node:K2Node_CallFunction_0,pin:Scale"
		"\n    /Game/Chars/BP_Manny,component:Camera.Boom"
		"\n"
		"\n   The navigation steps supported are:"
		"\n"
		"\n    graph — move from a blueprint or material to a graph."
		"\n    node — move from a graph to a graph node"
		"\n    pin — move from a graph node to a pin"
		"\n    component — move from a blueprint to a component"
		"\n    levelblueprint — move from a world to a blueprint"
		"\n    widget — move from a widget blueprint to a widget"
		"\n    structprop — move into a struct property of an object"
		"\n"
		"\n   Notice that paths use escaped fnames.  See the section"
		"\n   on escape sequences in fnames below sfor more information."
		"\n"
		"\n   Steps do not always require a parameter.  For example, materials"
		"\n   only have one graph, so you can just say:"
		"\n"
		"\n    /Game/Materials/MyMaterial,graph"
		"\n"
	));
}

void UWingManualSections::ExpressingTypes()
{
	WingOut::Stdout.Print(TEXT(
		"\n EXPRESSING TYPES:"
		"\n"
		"\n To change the type of a variable, or to express function parameters,"
		"\n you will use our syntax for types.  Here are some valid examples:"
		"\n"
		"\n   Bool, String, Vector, Rotator, HitResult, Actor, Character,"
		"\n   PlayerController, EBlendMode, EMovementMode, BP_Manny, BP_Quinn,"
		"\n   Array<Int>, Set<String>, Map<Int,Actor>"
		"\n   Soft<ABP_Manny>, Class<Pawn>, SoftClass<Pawn>"
		"\n"
		"\n Notice that it's 'Actor', not 'AActor'.  Type names are not"
		"\n case-sensitive.  When a blueprint like /Game/Testing/BP_Foo"
		"\n is used as a type, the typename is just BP_Foo.  You can search"
		"\n for valid types using the TypeName_Search command."
		"\n"
	));
}

void UWingManualSections::VariableDeclarations()
{
	WingOut::Stdout.Print(TEXT(
		"\n VARIABLE DECLARATIONS:"
		"\n"
		"\n   We have our own syntax for variable declarations:"
		"\n"
		"\n     kind type name (optional flags) = optional default value"
		"\n"
		"\n   Kind can be:"
		"\n"
		"\n     blueprint - eg, instance variables"
		"\n     input - a function argument or macro input"
		"\n     output - a function return value or macro output"
		"\n     local - local variables"
		"\n"
		"\n   Here are some examples:"
		"\n"
		"\n     input Array<Actor> Actors"
		"\n     output Float F (InstanceEditable)"
		"\n     blueprint String S = This is the default value"
		"\n"
		"\n   The commands Variables_Add, Variables_Modify,"
		"\n   and Variables_Remove can be used to edit "
		"\n   blueprint variables, graph local variables, graph input"
		"\n   variables, graph output variables, and custom"
		"\n   event node input variables.  Event dispatchers are"
		"\n   also graphs, so they too can be edited."
		"\n"
	));
}

void UWingManualSections::EscapeSequencesInFNames()
{
	WingOut::Stdout.Print(TEXT(
		"\n ESCAPE SEQUENCES IN FNAMES:"
		"\n"
		"\n   When we output FNames, we use HTML escape sequences for the"
		"\n   following marks: \\\"'(),.:;<=>&, and for certain other characters."
		"\n   We also translate spaces to periods."
		"\n"
		"\n   When sending FNames to UE Wingman, you *must* escape the marks"
		"\n   listed above, but you *may* escape any character.  To send an FName"
		"\n   with a space in it, either use &#32; or a period."
		"\n"
	));
}

void UWingManualSections::MaterialEditing()
{
	WingOut::Stdout.Print(TEXT(
		"\n MATERIAL EDITING:"
		"\n"
		"\n   We do not expose material expressions directly.  Instead, you"
		"\n   will be editing the material graph.  However, if you Graph_Dump"
		"\n   a material graph, you will see that the nodes contain"
		"\n   properties which actually come from the material expressions."
		"\n   You can edit these using Details_Set on the node."
		"\n"
		"\n   Don't overlook custom HLSL nodes.  These can accomplish in\n"
		"\n   a single node what would otherwise take many.\n"
		"\n"
	));
}

void UWingManualSections::NodeContextMenus()
{
	WingOut::Stdout.Print(TEXT(
		"\n NODE CONTEXT MENUS:"
		"\n"
		"\n   GraphNode_ShowMenu and GraphNode_ChooseMenu give access"
		"\n   to the node context menu.  This menu includes both node"
		"\n   operations and pin operations (e.g. Split Struct Pin,"
		"\n   Add Pin)."
		"\n"
	));
}

void UWingManualSections::VariableGettersAndSetters()
{
	WingOut::Stdout.Print(TEXT(
		"\n VARIABLE GETTERS AND SETTERS:"
		"\n"
		"\n   Access to local vars, function parameters, and "
		"\n   blueprint vars is through getter and setter nodes. "
		"\n   These can be found in GraphNode_SearchTypes by "
		"\n   searching for 'Variable'.  Some examples:"
		"\n"
		"\n   SKEL_WB_Menu_C|Variables|Default|GetPlaceTangible"
		"\n   SKEL_WB_Menu_C|Variables|Default|SetPlaceTangible"
		"\n   SKEL_WB_Menu_C|Variables|WB_Menu|GetMenuPanel"
		"\n"
	));
}

void UWingManualSections::BestPerformance()
{
	WingOut::Stdout.Print(TEXT(
		"\n BEST PERFORMANCE:"
		"\n"
		"\n   UE Wingman is much faster than the LLM. Therefore, it is"
		"\n   advantageous to batch: chain multiple ue-wingman commands"
		"\n   together using bash semicolon."
		"\n"
	));
}

void UWingManualSections::ImportantCommands()
{
	WingOut::Stdout.Print(TEXT(
		"\n IMPORTANT COMMANDS:"
		"\n"
		"\n   Documentation_Manual: print the entire manual"
		"\n   Documentation_Section: print a single section of the manual"
		"\n   Documentation_Commands: print concise list of all ue-wingman commands"
		"\n   Documentation_Command: detailed documentation for a single ue-wingman command"
		"\n   Documentation_CreateAssets: list of commands that create new assets"
		"\n   Blueprint_Dump: a summary of any blueprint"
		"\n   Graph_Dump: a fairly detailed listing of any Graph"
		"\n   Details_Dump: Dump the details panel for a given object"
		"\n   Details_Set: Manipulate the details panel for a given object"
		"\n"
	));
}

TSet<FName> WingManual::GetSections()
{
	TSet<FName> Result;
	for (TFieldIterator<UFunction> It(UWingManualSections::StaticClass(), EFieldIterationFlags::None); It; ++It)
	{
		Result.Add(It->GetFName());
	}
	return Result;
}

void WingManual::PrintSectionNames(const TCHAR *Prefix, const TSet<FName>& Sections, WingOut Output)
{
	if (Sections.IsEmpty()) return;
	if (Prefix) Output.Print(Prefix);
	bool bFirst = true;
	for (const FName& Section : Sections)
	{
		if (!bFirst) WingOut::Stdout.Print(TEXT(", "));
		bFirst = false;
		Output.Printf(TEXT("%s"), *Section.ToString());
	}
	if (Prefix) Output.Print(TEXT("\n"));
}

bool WingManual::PrintSection(FName Section)
{
	UFunction* Func = UWingManualSections::StaticClass()->FindFunctionByName(Section);
	if (!Func) return false;
	UWingManualSections::StaticClass()->GetDefaultObject()->ProcessEvent(Func, nullptr);
	return true;
}

void WingManual::Commands(EWingHandlerKind Kind, const FString& Query, bool Verbose)
{
	FString QueryLower = Query.ToLower();
	FString PrevGroup;

	bool any = false;
	for (const FWingHandlerConfig& H : UWingServer::AllHandlers())
	{
		if (H.Kind != Kind) continue;
		if (!H.Name.ToLower().Contains(QueryLower)) continue;
		any = true;

		// Blank line between groups
		if (!Verbose)
		{
			FString Group = WingUtils::GetHandlerGroup(H.HandlerClass.Get());
			if (Group != PrevGroup)
			{
				if (!PrevGroup.IsEmpty())
					WingOut::Stdout.Print(TEXT("\n"));
				PrevGroup = Group;
			}
		}

		if (Verbose)
			PrintHandlerHelp(H);
		else
			PrintHandlerPrototype(H);
	}
	if (!any)
	{
		WingOut::Stdout.Print(TEXT("No matching commands.  To see a full list, type:\n"));
		WingOut::Stdout.Print(TEXT("  ue-wingman Documentation_Commands.\n"));
	}
}
