#pragma once

#include "CoreMinimal.h"
#include "UObject/Object.h"
#include "UObject/StrongObjectPtr.h"
#include "WingBasics.generated.h"

class UEdGraphNode;
class UEdGraphPin;
class UBlueprint;
class FJsonObject;
class FJsonValue;

////////////////////////////////////////////////////////////
//
// Handlers.
//
// Each command supported by the MCP is implemented by a
// 'handler'.  The server receives the request, which
// contains a command.  The server looks up the right
// handler class for the command, and creates a handler
// object.  Using reflection, it inserts the command
// parameters into uproperty fields of the handler, then
// calls the handle method, which executes the command.
//
////////////////////////////////////////////////////////////

UENUM()
enum class EWingHandlerKind
{
	Normal,
	Create
};

USTRUCT()
struct FWingHandlerConfig
{
	GENERATED_BODY()
	FString Name;
	FString Documentation;
	TStrongObjectPtr<UClass> HandlerClass;
	TStrongObjectPtr<UObject> Config;
	TStrongObjectPtr<UClass> FactoryClass; 
	EWingHandlerKind Kind = EWingHandlerKind::Normal;
};

UCLASS(Abstract)
class UEWINGMAN_API UWingHandler : public UObject
{
	GENERATED_BODY()

public:
	// Register this handler's commands with the server.
	virtual void Register() PURE_VIRTUAL(UWingHandler::Register);

	// Called after parameter fields have been populated from JSON.
	virtual void Handle() {}

	// The configuration object.
	FWingHandlerConfig *Configuration;
};

////////////////////////////////////////////////////////////
//
// A simple type to store the remaining arguments in
// an Argv Array.
//
////////////////////////////////////////////////////////////

USTRUCT()
struct FWingRestOfArgv
{
	GENERATED_BODY()

	UPROPERTY()
	TArray<FString> Argv;
};

////////////////////////////////////////////////////////////
//
// WingOut.
//
// A thin wrapper around unreal string builders. Handlers
// put their output into a WingOut (string builder), which
// eventually gets forwarded to the agent. This output
// buffer is called WingOut::Stdout.
//
// Subroutines that can generate error messages take a
// parameter 'WingOut Errors', a string builder where they
// can put the error message.  Unlike a raw string builder,
// a WingOut can be nullptr, which is useful if you want to
// ignore error messages from certain subroutines.  You can
// also direct error messages directly to WingOut::Stdout.
//
////////////////////////////////////////////////////////////

class WingOut
{
public:
	WingOut(FStringBuilderBase *O) : Buffer(O) {}
	WingOut(FStringBuilderBase &O) : Buffer(&O) {}
	static WingOut Stdout;
	static WingOut None;

	void PrintChar(TCHAR Ch) { if (Buffer) Buffer->AppendChar(Ch); }
	void Print(const TCHAR* Text) { if (Buffer) Buffer->Append(Text); }
	void Print(const FString& Text) { if (Buffer) Buffer->Append(Text); }
	template <typename FmtType, typename... ArgTypes>
	void Printf(const FmtType& Fmt, ArgTypes&&... Args)
		{ if (Buffer) Buffer->Appendf(Fmt, Forward<ArgTypes>(Args)...); }

	static TStringBuilder<65536> StdoutBuffer;

private:
	FStringBuilderBase *Buffer;
};

////////////////////////////////////////////////////////////
//
// References.
//
// The MCP has a variety of operations that can manipulate
// any object, including: actors, components, graphs, graph
// nodes, graph pins, widgets, and so forth.  For example,
// the agent can request a 'details panel' for any of these.
//
// However, some of those objects aren't UObjects.  This
// makes it awkward to write code that deals with them
// uniformly. To homogenize them, we turn them all into
// UObjects by creating wrappers for those that aren't.
//
// Some things that you would think are UObjects, like
// components, are actually only UObjects at runtime.
// When a blueprint is still being edited, the component
// is represented by a bunch of related data structures
// inside the blueprint.  At that time, there is no one
// UObject to point to as 'the component.'
//
////////////////////////////////////////////////////////////


// Pin Ref: A pointer to a graph node, plus a pin name.
UCLASS()
class UWingGraphPinRef : public UObject
{
	GENERATED_BODY()

public:
	UPROPERTY()
	UEdGraphNode* Node = nullptr;

	FName PinName;
};

// Component Ref: A pointer to a blueprint plus a component
// name. The component can be an inherited component, as
// opposed to one that is defined in the blueprint itself.
UCLASS()
class UWingComponentRef : public UObject
{
	GENERATED_BODY()
public:
	UPROPERTY()
	UBlueprint* BP = nullptr;
	
	// The component name.
	FName VariableName;
};

// Struct Ref: A pointer to an object, and a struct inside
// that object.  The editable flag indicates whether the
// struct is marked CPF_Edit.  If not, then the intent is
// that this struct is for viewing only.
UCLASS()
class UWingStructRef : public UObject
{
	GENERATED_BODY()
public:
	UPROPERTY()
	UObject* Object;
			
	void *StructBase;

	UPROPERTY()
	UStruct* StructType;

	bool Editable;
};

