#pragma once

#include "CoreMinimal.h"

#include "UObject/ObjectMacros.h"
#include "VentuzActorTransformSync.h"

#include "IDetailCustomization.h"
#include "K2Node.h"
#include "VentuzBPEvent.h"
#include "VentuzPluginModule.h"
#include "VentuzDataGetterNode.generated.h"

class IDetailLayoutBuilder;

// needs to be in sync with UE4Interface.cs Ventuz.Kernel.E2E.FieldType and UE4ComCommon.h::E2EFieldType
UENUM()
enum class VentuzType : uint8
{
    Float       = Ventuz::E2EFieldType::Float        UMETA( DisplayName = "Float" ),
    Int         = Ventuz::E2EFieldType::Integer      UMETA( DisplayName = "Integer" ),
    Bool        = Ventuz::E2EFieldType::Boolean      UMETA( DisplayName = "Boolean" ),
    String      = Ventuz::E2EFieldType::String       UMETA( DisplayName = "String" ),
    FloatArray  = Ventuz::E2EFieldType::FloatArray   UMETA( DisplayName = "FloatArray" ),
    IntArray    = Ventuz::E2EFieldType::IntegerArray UMETA( DisplayName = "IntegerArray" ),
    BoolArray   = Ventuz::E2EFieldType::BooleanArray UMETA( DisplayName = "BooleanArray" ),
    StringArray = Ventuz::E2EFieldType::StringArray  UMETA( DisplayName = "StringArray" ),
};

USTRUCT()
struct FVentuzNodeField
{
    GENERATED_BODY()

    UPROPERTY( VisibleAnywhere )
    VentuzCommunicationDirection direction;

    UPROPERTY( EditAnywhere )
    VentuzType fieldType;

    UPROPERTY( EditAnywhere )
    FString fieldName;

    UEdGraphPin* pin = nullptr;
};

USTRUCT()
struct FVentuzNodeFieldToVentuz : public FVentuzNodeField
{
    GENERATED_BODY()
    FVentuzNodeFieldToVentuz()
    {
        direction = VentuzCommunicationDirection::ToVentuz;
    }
};

USTRUCT()
struct FVentuzNodeFieldFromVentuz : public FVentuzNodeField
{
    GENERATED_BODY()
    FVentuzNodeFieldFromVentuz()
    {
        direction = VentuzCommunicationDirection::FromVentuz;
    }
};

UCLASS()
class VENTUZEDITORPLUGIN_API UVentuzE2ESyncNodeBase : public UK2Node, public IVentuzNodeFieldSync
{
    GENERATED_BODY()

public:
    UPROPERTY( EditAnywhere, Category = "Custom Data Model Sync" )
    FString nodeInVentuzToSyncFrom;

    virtual void SyncFields() {};
    virtual void UpdateNodeFields( const std::vector<Ventuz::UE4::FieldDescriptor>& fieldList ) override {};

    static FString FixName(int index, const FVentuzNodeField& name);
};

class FVentuzE2ESyncNodeDetails : public IDetailCustomization
{
public:
    /** Makes a new instance of this detail layout class for a specific detail view requesting it */
    static TSharedRef<IDetailCustomization> MakeInstance();
private:

    /** IDetailCustomization interface */
    virtual void CustomizeDetails( IDetailLayoutBuilder& DetailLayout ) override;
    virtual void CustomizeDetails( const TSharedPtr<IDetailLayoutBuilder>& DetailBuilder ) override;

    FReply OnSyncNodeFields();

private:

    TWeakPtr<IDetailLayoutBuilder> cachedDetailBuilder;
    TWeakObjectPtr<UVentuzE2ESyncNodeBase> getDataNode;
};

//////////////////////////////////////////////////////////////////////////
// From Ventuz Multi Field Node

UCLASS( BlueprintType, Blueprintable )
class VENTUZEDITORPLUGIN_API UVentuzE2EGetData : public UVentuzE2ESyncNodeBase
{
    GENERATED_BODY()

    virtual FText GetNodeTitle( ENodeTitleType::Type TitleType ) const override;
    virtual FText GetTooltipText() const override;
    virtual FText GetMenuCategory() const override;
    virtual FSlateIcon GetIconAndTint( FLinearColor& OutColor ) const override;

    virtual void GetMenuActions( FBlueprintActionDatabaseRegistrar& ActionRegistrar ) const override;
    virtual void AllocateDefaultPins() override;

    virtual void ExpandNode( class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph ) override;
    virtual bool ShouldShowNodeProperties() const override;

    virtual void SyncFields() override;

    virtual void PreEditChange(FProperty* PropertyThatWillChange) override;

    virtual void PostEditChangeProperty( struct FPropertyChangedEvent& PropertyChangedEvent ) override;
    virtual void PostEditChangeChainProperty( struct FPropertyChangedChainEvent& PropertyChangedEvent ) override;

public:

    UPROPERTY( EditAnywhere, Category = "Custom Data Model Fields" )
    TArray<FVentuzNodeFieldFromVentuz> fromVentuzFields;

    virtual void UpdateNodeFields( const std::vector<Ventuz::UE4::FieldDescriptor>& fieldList ) override;

private:

    TArray<FVentuzNodeFieldFromVentuz> fieldsBackup;
    FNodeTextCache CachedTooltip;
    FNodeTextCache CachedNodeTitle;
};

//////////////////////////////////////////////////////////////////////////
// To Ventuz Multi Field Node

UCLASS( BlueprintType, Blueprintable )
class VENTUZEDITORPLUGIN_API UVentuzE2ESendData : public UVentuzE2ESyncNodeBase
{
    GENERATED_BODY()

    virtual FText GetNodeTitle( ENodeTitleType::Type TitleType ) const override;
    virtual FText GetTooltipText() const override;
    virtual FText GetMenuCategory() const override;
    virtual FSlateIcon GetIconAndTint( FLinearColor& OutColor ) const override;

    virtual void GetMenuActions( FBlueprintActionDatabaseRegistrar& ActionRegistrar ) const override;
    virtual void AllocateDefaultPins() override;

    virtual void ExpandNode( class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph ) override;
    virtual bool ShouldShowNodeProperties() const override;

    virtual void SyncFields() override;

    virtual void PreEditChange( FProperty* PropertyThatWillChange ) override;

    virtual void PostEditChangeProperty( struct FPropertyChangedEvent& PropertyChangedEvent ) override;
    virtual void PostEditChangeChainProperty( struct FPropertyChangedChainEvent& PropertyChangedEvent ) override;

public:

    UPROPERTY( EditAnywhere, Category = "Custom Data Model Fields" )
    TArray<FVentuzNodeFieldToVentuz> toVentuzFields;

    virtual void UpdateNodeFields( const std::vector<Ventuz::UE4::FieldDescriptor>& fieldList ) override;

private:

    TArray<FVentuzNodeFieldToVentuz> fieldsBackup;
    FNodeTextCache CachedTooltip;
    FNodeTextCache CachedNodeTitle;
};

//////////////////////////////////////////////////////////////////////////
// From Ventuz Single Field Node

UCLASS( BlueprintType, Blueprintable )
class VENTUZEDITORPLUGIN_API UVentuzGetValue : public UK2Node
{
    GENERATED_BODY()

    virtual FText GetNodeTitle( ENodeTitleType::Type TitleType ) const override;
    virtual FText GetTooltipText() const override;
    virtual FText GetMenuCategory() const override;
    virtual FSlateIcon GetIconAndTint( FLinearColor& OutColor ) const override;

    virtual void GetMenuActions( FBlueprintActionDatabaseRegistrar& ActionRegistrar ) const override;
    virtual void AllocateDefaultPins() override;

    virtual void ExpandNode( class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph ) override;

    virtual void PostEditChangeProperty( struct FPropertyChangedEvent& PropertyChangedEvent ) override;

    virtual bool ShouldShowNodeProperties() const override;

public:

    UPROPERTY( EditAnywhere, BlueprintReadWrite )
    VentuzType type;

private:

    FNodeTextCache CachedTooltip;
    FNodeTextCache CachedNodeTitle;

};

//////////////////////////////////////////////////////////////////////////
// To Ventuz Single Field Node

UCLASS( BlueprintType, Blueprintable )
class VENTUZEDITORPLUGIN_API UVentuzSendValue : public UK2Node
{
    GENERATED_BODY()

    virtual FText GetNodeTitle( ENodeTitleType::Type TitleType ) const override;
    virtual FText GetTooltipText() const override;
    virtual FText GetMenuCategory() const override;
    virtual FSlateIcon GetIconAndTint( FLinearColor& OutColor ) const override;

    virtual void GetMenuActions( FBlueprintActionDatabaseRegistrar& ActionRegistrar ) const override;
    virtual void AllocateDefaultPins() override;

    virtual void ExpandNode( class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph ) override;

    virtual void PostEditChangeProperty( struct FPropertyChangedEvent& PropertyChangedEvent ) override;

    virtual bool ShouldShowNodeProperties() const override;

public:

    UPROPERTY( EditAnywhere, BlueprintReadWrite )
    VentuzType type;

private:

    FNodeTextCache CachedTooltip;
    FNodeTextCache CachedNodeTitle;
};
