// Copyright (c) 2023 Codemongous LLC. All Rights Reserved.

#pragma once

#include "AssetThumbnail.h"
#include "GraphEditor.h"
#include "Sourcify.h"
#include "Widgets/Input/SCheckBox.h"
#include "Widgets/SCanvas.h"
#include "Widgets/Views/SListView.h"

class FUICommandList;
class SComboButton;
class SReferenceViewerFilterBar;
class SSearchBox;
class UEdGraph_Sourcify;
class UEdGraphNode_Sourcify;

namespace ESelectInfo { enum Type : int; }
struct FAssetManagerEditorRegistrySource;
template <typename OptionType> class SComboBox;

class UEdGraph;
class SProgressBar;
class SThrobber;

/** Enum for source control status */
enum class ESourceControlCheckoutStatus : uint8
{
	Unknown,
	Loading,
	CheckedOutByYou,
	CheckedOutByOther,
	AvailableForCheckout,
	NotUnderSourceControl
};

/** Struct to hold cached source control status */
struct FSourceControlStatusCache
{
	ESourceControlCheckoutStatus Status = ESourceControlCheckoutStatus::Unknown;
	FString OtherUserName;
};

/** Struct to hold asset referencer information for the list view */
struct FAssetReferencer
{
	FName AssetPath;
	/** The asset path of the node in the main graph that this referencer references */
	FName ReferencedNodePath;
};

/**
 * 
 */
class SSourcifyGraph : public SCompoundWidget
{
	friend class SSourcifyNode;

public:
	SLATE_BEGIN_ARGS( SSourcifyGraph )
		{
			_OwnerTab = nullptr;
		}

	SLATE_ARGUMENT(SDockTab*, OwnerTab)

	SLATE_END_ARGS()

	~SSourcifyGraph();

	/** Constructs this widget with InArgs */
	void Construct( const FArguments& InArgs );

	void FromDependencies(const Sourcify::BlueprintDependencies& Deps);

	/** Gets graph editor */
	TSharedPtr<SGraphEditor> GetGraphEditor() const { return GraphEditorPtr; }

	/** Called when the current registry source changes */
	void SetCurrentRegistrySource(const FAssetManagerEditorRegistrySource* RegistrySource);

	void ClearSelectedNodes();
	/** SWidget interface **/
	virtual bool SupportsKeyboardFocus() const override { return true; }
	virtual FReply OnKeyDown( const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent ) override;
	virtual void Tick( const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime ) override;

	void RequestClose() const;

private:

	/** Call after a structural change is made that causes the graph to be recreated */
	void RebuildGraph();

	/** Called when the selection changes in the Graph */
	void OnSelectedNodesChanged(const TSet<UObject*>& NewSelection);

	/** Called when the user right-clicks an empty area in the Graph. We don't want this to spawn at all so this handler simply returns a FActionMenuContent */
	FActionMenuContent OnCreateActionMenu(UEdGraph* Graph, const FVector2D& NodeAddPosition, const TArray<UEdGraphPin*>& DraggedFromPins, bool AutoExpandActionMenu, SGraphEditor::FActionMenuClosed CloseActionMenuCallback) const;

	/** Refresh the current view */
	void RefreshClicked();

	/** Gets the text to be displayed in the address bar */
	FText GetAddressBarText() const;

	void OnSourcifyingComplete(Sourcify::Results Results) const;

	void RegisterActions();
	void ShowSelectionInContentBrowser();
	void OpenSelectedInAssetEditor();
	void ShowReferenceTree() const;
	void ZoomToFit();
	bool CanZoomToFit() const;

	/** Handlers for searching */
	void ReCenterGraphOnNodes(const TSet<UObject*>& Nodes) const;

	UObject* GetObjectFromSingleSelectedNode() const;
	void GetPackageNamesFromSelectedNodes(TSet<FName>& OutNames) const;
	bool HasExactlyOneNodeSelected() const;
	bool IsAnyNodeSelected() const;
	bool HasExactlyOnePackageNodeSelected() const;
	bool HasAtLeastOnePackageNodeSelected() const;
	bool HasAtLeastOneRealNodeSelected() const;

	void OnInitialAssetRegistrySearchComplete();
	EActiveTimerReturnType TriggerZoomToFit(double InCurrentTime, float InDeltaTime);
	FText GetOutputDirectoryForSelected() const;
	FText GetOutputFilenameForSelected() const;
	FText GetOutputTypeForSelected() const;
	FText GetPreviewTypeName() const;
	void SetOutputDirectoryForSelected(const FText& InDirectory);
	void SetOutputFilenameForSelected(const FText& InFilename) ;
	void SetOutputTypeForSelected(const FText& InType, bool CheckIsReadyForOutput = false);
	bool OpenPlatformDirectoryPicker(FString& OutDirectory, FString DefaultPath);
	FReply BrowseForDirectory();
	FReply OnSourcifyClicked();
	FReply OnSettingsClicked();

	EVisibility GetTypeNameErrorLabelVisibility();
	FText GetTypeNameErrorLabelText();

	void ValidateConversionSettingsForSelected();

	EVisibility CanResourcifySelectedNodes() const;

	enum class ReadyState : uint8
	{
		Ready = 0,
		NotReady,
		CanNeverBeReady
	};

	ReadyState GetSourcifyReadiness() const;

	/** Populates the referencer lists from the raw FName arrays */
	void PopulateReferencerLists();

	/** Generates a row for the referencer list view */
	TSharedRef<ITableRow> GenerateReferencerRow(TSharedPtr<FAssetReferencer> Item, const TSharedRef<STableViewBase>& OwnerTable);

	/** Creates an asset thumbnail for display from FAssetData (does not load the asset) */
	TSharedPtr<FAssetThumbnail> MakeAssetThumbnail(FLinearColor& OutAssetColor, FAssetThumbnailConfig& OutThumbnailConfig, const FAssetData& InAssetData);

	/** Gets FAssetData for a package path without loading the asset */
	FAssetData GetAssetDataForPath(const FName& PackagePath) const;

	/** Gets asset type color from FAssetData without loading the asset */
	FLinearColor GetAssetTypeColorFromAssetData(const FAssetData& AssetData) const;

private:

	TSharedRef<SWidget> MakeToolBar();
	TSharedPtr<SGraphEditor> GraphEditorPtr;

	TSharedPtr<FUICommandList> ReferenceViewerActions;
	TSharedPtr<SWidget> FilenameBox;
	TSharedPtr<SWidget> TypeNameBox;
	TSharedPtr<SWidget> PreviewTypeNameBox;
	TSharedPtr<SCheckBox> ResourcifyCheckBox;
	
	UEdGraph_Sourcify* GraphObj;

	SDockTab* ParentTab;

	/** The temporary copy of the path text when it is actively being edited. */
	FText TemporaryPathBeingEdited;

	/** List of collection filter options */
	TArray<TSharedPtr<FName>> CollectionsComboList;

	/** Used to delay graph rebuilding during spin box slider interaction */
	bool bNeedsGraphRebuild;
	double GraphRebuildSliderDelay = 0.25;

	/** Handle to know if dirty */
	FDelegateHandle AssetRefreshHandle;

	TArray<UEdGraphNode_Sourcify*> SelectedNodes;
	FString OutputDirectory;

	FText LastInputValidityErrorText;
	bool bLastInputValidityCheckSuccessful = true;

	/** True if the graph needs to display a warning about there being a BP class inheritance */
	bool BPInheritanceExists = false;
	/** The message showing the current fatal (i.e. Sourcification not allowed) error */
	FString FatalErrorMessage;

	/** List of asset referencers that are external to the graph */
	TArray<FName> Referencers;

	/** List of asset referencers that are also nodes in the main graph */
	TArray<FName> GraphReferencers;

	/** Set of asset paths that are in the main dependency graph (for filtering) */
	TSet<FName> MainGraphAssetPaths;
	
	/** List view data source for the references panel */
	TArray<TSharedPtr<FAssetReferencer>> ReferencersList;

	/** List view widget for the references panel */
	TSharedPtr<SListView<TSharedPtr<FAssetReferencer>>> ReferencersListView;

	/** Checkbox to control whether to show referencers that are also in the main graph */
	TSharedPtr<SCheckBox> ShowGraphReferencersCheckBox;

	/** Whether to include referencers that are also in the main graph */
	bool bShowGraphReferencers = false;

	/** The asset path of the currently hovered referencer in the panel */
	FName HoveredReferencerPath;

	/** The asset path of the currently hovered node in the graph */
	FName HoveredNodePath;

	/** Map from referencer path to the node paths it references (in the main graph) */
	TMap<FName, TArray<FName>> ReferencerToNodeMap;

	/** Map from node path to the referencer paths that reference it */
	TMap<FName, TArray<FName>> NodeToReferencerMap;

	/** Canvas overlay for highlighting nodes when hovering over referencers */
	TSharedPtr<SCanvas> HighlightOverlayCanvas;

	/** Set of currently highlighted node paths */
	TSet<FName> HighlightedNodePaths;

	/** Cached source control status for each referencer */
	TMap<FName, FSourceControlStatusCache> SourceControlStatusCache;

	/** Whether async source control status loading is in progress */
	bool bIsLoadingSourceControlStatus = false;

	/** Number of source control status items loaded so far */
	int32 SourceControlStatusLoadedCount = 0;

	/** Total number of source control status items to load */
	int32 SourceControlStatusTotalCount = 0;

	/** Throbber widget for the referencers panel */
	TSharedPtr<SThrobber> ReferencersThrobber;

	/** Progress bar widget for the referencers panel */
	TSharedPtr<SProgressBar> ReferencersProgressBar;

	/** Container for the loading overlay */
	TSharedPtr<SWidget> LoadingOverlay;

	/** Starts async loading of source control status for all referencers */
	void StartAsyncSourceControlStatusLoading();

	/** Called when a single source control status item has been loaded */
	void OnSourceControlStatusLoaded(FName AssetPath, FSourceControlStatusCache StatusCache);

	/** Returns the progress of source control status loading (0.0 to 1.0) */
	float GetSourceControlLoadingProgress() const;

	/** Updates the highlight overlay to show highlights around the specified nodes */
	void UpdateNodeHighlights(const TArray<FName>& NodePathsToHighlight);

	/** Clears all node highlights */
	void ClearNodeHighlights();

	/** Returns the source control checkout status text for the given asset path (uses FAssetData, does not load asset) */
	FText GetSourceControlCheckoutStatusText(const FName& AssetPath, const FAssetData& AssetData) const;

	/** Returns the source control checkout status color for the given asset path (uses FAssetData, does not load asset) */
	FSlateColor GetSourceControlCheckoutStatusColor(const FName& AssetPath, const FAssetData& AssetData) const;

	/** Called when mouse enters a referencer row */
	void OnReferencerMouseEnter(const FName& ReferencerPath);

	/** Called when mouse leaves a referencer row */
	void OnReferencerMouseLeave();

	/** Called when mouse enters a graph node */
	void OnGraphNodeMouseEnter(const FName& NodePath);

	/** Called when mouse leaves a graph node */
	void OnGraphNodeMouseLeave();

	/** Returns true if the given referencer should be highlighted */
	bool IsReferencerHighlighted(const FName& ReferencerPath) const;

	/** Returns true if the given graph node should be highlighted */
	bool IsGraphNodeHighlighted(const FName& NodePath) const;

	void GatherUpAssetReferencers(const Sourcify::BlueprintDependencies& Deps);
};

enum class EDependencyPinCategory
{
	LinkEndPassive = 0,
	LinkEndActive = 1,
	LinkEndMask = LinkEndActive,

	LinkTypeNone = 0,
	LinkTypeUsedInGame = 2,
	LinkTypeHard = 4,
	LinkTypeMask = LinkTypeHard | LinkTypeUsedInGame,
};
ENUM_CLASS_FLAGS(EDependencyPinCategory);

extern EDependencyPinCategory ParseDependencyPinCategory(FName PinCategory);
extern FLinearColor GetColor(EDependencyPinCategory Category);
extern FName GetName(EDependencyPinCategory Category);
