#pragma once

#include "CoreMinimal.h"
#include "EdGraph/EdGraphPin.h"
#include "Engine/Blueprint.h"

struct WingTokenizer;
class UK2Node_CustomEvent;
class UK2Node_EditablePinBase;
class UK2Node_FunctionEntry;
struct FUserPinInfo;


class WingVariableList
{
public:
    struct Var
    {
        // Internal name.
        FName Name;

        // Type.  Must be a BlueprintType.
        FEdGraphPinType Type;

        // Default Value.
        FString DefaultValue;

        // When parsing a string, true if default was specified.
        bool DefaultSpecified = false;

        // Boolean flags.
        TSet<FName> Flags;

        // This is only populated if these variables are associated
        // with blueprint variables or local variables.
        FBPVariableDescription *BPVar = nullptr;

        // This is only populated if these variables are associated
        // with an editable pin base.
        TSharedPtr<FUserPinInfo> Pin = nullptr;
    };

public:
    // The actual list of variables.
    TArray<Var> Variables;

    // The list has a name, which is used for generating good messages.
    const TCHAR *ListName;

    // Constructor.
    WingVariableList(const TCHAR *MyListName) : ListName(MyListName) {}

    // Return true if the variables are empty.
    void Empty() { Variables.Empty(); }
    
    // Print the variables to a string builder.
    void Print(FStringBuilderBase &Out);

    // Print compact: "type name,type name,..."
    void PrintCompact(FStringBuilderBase &Out);
    
    // Clear the BPVar and Pin fields.
    void ClearLinks();

    // Check the sanity of the vars in the array.  If allow
    // is false, then no variables are allowed in the array.
    bool CheckSanity(const TSet<FName> &GoodFlags, bool Allow);

    // Parse variables from a string.
    bool ParseString(const FString &Input);

    // Parse variable names only from a string.
    bool ParseNamesString(const FString &Input);

private:
    bool ParseOneVariable(WingTokenizer &Tok, Var &V);
    bool ParseVariableFlags(WingTokenizer &Tok, TSet<FName> &Out);
};

class WingVariables
{
public:    
    using Var = WingVariableList::Var;
    WingVariables() {}

    // The backing store.  Only one of these should be set.

    UBlueprint *Blueprint = nullptr;
    UEdGraph *Graph = nullptr;
    UK2Node_CustomEvent *CustomEvent = nullptr;

    // The Workspace.  At any given time, these may or may not contain
    // the same data as the backing store.

    WingVariableList BlueprintVariables{TEXT("Blueprint Variables")};
    WingVariableList LocalVariables{TEXT("Local Variables")};
    WingVariableList InputVariables{TEXT("Input Variables")};
    WingVariableList OutputVariables{TEXT("Output Variables")};

    // Configure the backing store.  On failure,
    // prints a message and returns false.

    bool SetBackingStore(UObject *Obj);

    // Clear the workspace.  Doesn't affect the backing store.

    void Empty();

    // Clear the BPVar and Pin fields.

    void ClearLinks();

    // Print the contents of the workspace.

    void Print(FStringBuilderBase &Out);

    // Load: clear the workspace, then
    // copy everything from the backing store into the workspace.

    void Load();

    // Check: make sure the contents of the workspace makes sense
    // given the type of backing store.

    bool Check();

    // Use the variables in the workspace to modify the backing store.

    bool Modify();

    // Create every variable in the workspace in the backing store.

    bool Create();

    // Remove every variable mentioned in the workspace from the backing store.

    bool Remove();

private:
    void LoadBlueprint();
    void LoadGraph();
    void LoadLocalVariables(UK2Node_EditablePinBase *Node);
    Var LoadBlueprintVariableDescription(FBPVariableDescription &Desc, UObject *CDO);
    Var LoadLocalVariableDescription(FBPVariableDescription &Desc);
    void LoadEditablePinBase(UK2Node_EditablePinBase *Node, WingVariableList &List);

    bool CheckBlueprint();
    bool CheckGraph();

    bool ModifyBlueprint();
    bool LinkBlueprintVariables();
    void ModifyBlueprintVariableFlags(Var &Input);
    bool ModifyBlueprintDefaults();

    bool ModifyGraph();

    bool CreateBlueprint();

    bool CreateGraph();

    bool RemoveBlueprint();
    bool RemoveGraph();

    void LoadCustomEvent();
    bool CheckCustomEvent();
    bool ModifyCustomEvent();
    bool CreateCustomEvent();
    bool RemoveCustomEvent();

    bool GetGraphNodes(
        UK2Node_EditablePinBase *&InputNode,
        UK2Node_EditablePinBase *&OutputNode,
        UK2Node_FunctionEntry *&LocalNode);

    bool ModifyEditablePinBase(WingVariableList &List, UK2Node_EditablePinBase *Node);
    void AddUserPinInfo(const Var &V, EEdGraphPinDirection Dir, UK2Node_EditablePinBase *Node);

    bool ErrorNoBackingStore();
};

