// Copyright (c) Microsoft Corporation. 
// Licensed under the MIT license.

#include "LODRecipeToolbarExtenders.h"
#include "LODRecipe.h"
#include "LODRecipe/Private/LODRecipeRegistry.h"
#include "LODRecipeManager.h"
#include "SimplygonStyle.h"

#include "Templates/SharedPointer.h"

#include "IStaticMeshEditor.h"
#include "Engine/StaticMesh.h"
#include "ISkeletalMeshEditor.h"
#include "Engine/SkeletalMesh.h"
#include "IPersonaToolkit.h"
#include "IPersonaPreviewScene.h"
#include "Animation/DebugSkelMeshComponent.h"

#include "Widgets/Text/STextBlock.h"
#include "Widgets/Layout/SBorder.h"
#include "FrameWork/MultiBox/MultiBoxBuilder.h"

#include "IContentBrowserSingleton.h"
#include "ContentBrowserModule.h"
#include "AssetRegistryModule.h"
#include "PropertyCustomizationHelpers.h"
#include "Framework/Application/SlateApplication.h"

DEFINE_LOG_CATEGORY_STATIC(LogLODRecipeSMEExtender, Verbose, All);
#define LOCTEXT_NAMESPACE "LODRecipeSMEExtender"

//////////////////////////////////////////////////////////////
// FEditorExtenderCommon


void FEditorExtenderCommon::CreateToolbarButton(FToolBarBuilder& ToolbarBuilder)
{
	struct local
	{
		static void GenerateLODs(UObject* MeshObject, FSimplygonPostBuildFromEditor* PostBuildDelegate)
		{
			FLODRecipeManager::Get().Build(MeshObject);
			PostBuildDelegate->ExecuteIfBound();
		}
		static bool CanGenerateLODs(UObject* MeshObject)
		{
			TSoftObjectPtr<ULODRecipe> LODRecipe = FLODRecipeRegistry::Get().FindLODRecipe(MeshObject);
			LODRecipe.LoadSynchronous();
			return LODRecipe.IsValid();
		}
	};

	ToolbarBuilder.AddSeparator();
	ToolbarBuilder.AddToolBarButton(
		FUIAction(FExecuteAction::CreateStatic(&local::GenerateLODs, MeshObject, &PostBuildDelegate), FCanExecuteAction::CreateStatic(local::CanGenerateLODs, MeshObject)),
		NAME_None,
		LOCTEXT("SimplygonCombo_Label2", "Generate LODs"),
		LOCTEXT("SimplygonComboToolTip", "Generate LODs for this mesh using the attached LOD recipe."),
		FSlateIcon(FSimplygonStyle::GetStyleSetName(), "SimplygonIcon.LogoIcon"));

	ToolbarBuilder.AddComboButton(
		FUIAction(),
		FOnGetContent::CreateRaw(this, &FEditorExtenderCommon::CreateToolbarMenu),
		LOCTEXT("SimplygonCombo_Label1", ""),
		FText(),
		FSlateIcon(/*FEditorStyle::GetStyleSetName(), "SimplygonIcon.Enabled"*/),
		false);

	ToolbarBuilder.AddSeparator();
}

TSharedRef<SWidget> FEditorExtenderCommon::CreateToolbarMenu()
{
	TSharedPtr<FExtender> MenuExtender = MakeShareable(new FExtender);

	const bool bShouldCloseWindowAfterMenuSelection = true;
	FMenuBuilder MenuBuilder(bShouldCloseWindowAfterMenuSelection, nullptr, MenuExtender);

	FUIAction OpenLODRecipeAction(FExecuteAction::CreateRaw(this, &FEditorExtenderCommon::OpenLODRecipe));
	FUIAction RemoveLODRecipeAction(FExecuteAction::CreateRaw(this, &FEditorExtenderCommon::RemoveLODRecipe));

	MenuBuilder.BeginSection("LOD Recipe", LOCTEXT("Simplygon_SME_Toolbar_LODSettings", "LOD Recipe"));
	{
		MenuBuilder.AddSubMenu(
			LOCTEXT("Simplygon_SME_Toolbar_Settings", "Create/Choose"),
			FText(),
			FNewMenuDelegate::CreateRaw(this, &FEditorExtenderCommon::OpenLODRecipePicker));

		bool bHasLODRecipe = FLODRecipeRegistry::Get().FindLODRecipe(MeshObject).IsValid();
		if (bHasLODRecipe)
		{
			MenuBuilder.AddMenuEntry(
				LOCTEXT("SME_Extension_EditLODSettings", "Edit"),
				LOCTEXT("SME_Extension_EditLODSettings_Tooltip", "Open attached LOD Setting object"),
				FSlateIcon(),
				OpenLODRecipeAction,
				NAME_None,
				EUserInterfaceActionType::Button);

			MenuBuilder.AddMenuEntry(
				LOCTEXT("SME_Extension_RemoveLODRecipe", "Remove"),
				LOCTEXT("SME_Extension_RemoveLODRecipe_Tooltip", "Remove LOD recipe from this asset"),
				FSlateIcon(),
				RemoveLODRecipeAction,
				NAME_None,
				EUserInterfaceActionType::Button);
		}
	}
	MenuBuilder.EndSection();

	return MenuBuilder.MakeWidget();
}

void FEditorExtenderCommon::OpenLODRecipe()
{
	TSoftObjectPtr<ULODRecipe> LODRecipe = FLODRecipeRegistry::Get().FindLODRecipe(MeshObject);
	if (!LODRecipe.IsValid())
	{
		UE_LOG(LogLODRecipeSMEExtender, Warning, TEXT("Static mesh (%s) does not contian a LOD recipe"), *MeshObject->GetName());
		return;
	}
	GEditor->GetEditorSubsystem<UAssetEditorSubsystem>()->OpenEditorForAsset(LODRecipe.Get());
}

void FEditorExtenderCommon::RemoveLODRecipe()
{
 	TSoftObjectPtr<ULODRecipe> LODRecipe = FLODRecipeRegistry::Get().FindLODRecipe(MeshObject);
	if (!LODRecipe.IsValid())
		return;

	LODRecipe->RemoveMesh(MeshObject);
}

void FEditorExtenderCommon::OpenLODRecipePicker(FMenuBuilder& MenuBuilder)
{
	const bool bAllowClear = false;

	TArray<const UClass*> AllowedClasses;
	AllowedClasses.Add(ULODRecipe::StaticClass());

	FAssetData CurrentAssetData;
	TSoftObjectPtr<ULODRecipe> LODRecipe = FLODRecipeRegistry::Get().FindLODRecipe(MeshObject);
	if (LODRecipe.IsValid())
	{
		CurrentAssetData = FAssetData(LODRecipe.Get());
	}

	TSharedRef<SWidget> AssetPickerWidget = PropertyCustomizationHelpers::MakeAssetPickerWithMenu(CurrentAssetData,
		/*bAllowClear*/ false,
		/*bAllowCopyPaste*/ false,
		AllowedClasses,
		PropertyCustomizationHelpers::GetNewAssetFactoriesForClasses(AllowedClasses),
		FOnShouldFilterAsset(),
		FOnAssetSelected::CreateRaw(this, &FEditorExtenderCommon::OnAssetSelected),
		FSimpleDelegate::CreateRaw(this, &FEditorExtenderCommon::CloseMenu));

	MenuBuilder.AddWidget(AssetPickerWidget, FText::GetEmpty(), /*bNoIndent=*/ true);
}

void FEditorExtenderCommon::CloseMenu()
{
	FSlateApplication::Get().DismissAllMenus();
}

//////////////////////////////////////////////////////////////
// FStaticMeshEditorExtender

FStaticMeshEditorExtender::FStaticMeshEditorExtender(TWeakPtr<IStaticMeshEditor> InStaticMeshEditor)
{
	StaticMeshEditor = InStaticMeshEditor;
	MeshObject = InStaticMeshEditor.Pin()->GetStaticMesh();

	TSharedRef<FUICommandList> DummyList = MakeShareable(new FUICommandList);
	this->AddToolBarExtension("Asset",
		EExtensionHook::After,
		DummyList,
		FToolBarExtensionDelegate::CreateRaw(this, &FStaticMeshEditorExtender::CreateToolbarButton));

	PostBuildDelegate.BindLambda([Editor = &StaticMeshEditor]()
		{
			if (Editor->IsValid())
			{
				Editor->Pin()->RefreshTool();
			}
		});
}

void FStaticMeshEditorExtender::OnAssetSelected(const FAssetData& AssetData)
{
	ULODRecipe* LODRecipe = Cast<ULODRecipe>(AssetData.GetAsset());
	if (!LODRecipe)
	{
		return;
	}

	LODRecipe->AddMesh(StaticMeshEditor.Pin()->GetStaticMesh());
}

//////////////////////////////////////////////////////////////
// FSkeletaMeshEditorExtender 

FSkeletaMeshEditorExtender::FSkeletaMeshEditorExtender(const TSharedRef<FUICommandList> CommandList, TSharedRef<ISkeletalMeshEditor> InSkeletalMeshEditor)
{
	SkeletalMeshEditor = InSkeletalMeshEditor;
	MeshObject = SkeletalMeshEditor.Pin()->GetPersonaToolkit()->GetMesh();

	this->AddToolBarExtension("Asset",
		EExtensionHook::After,
		CommandList,
		FToolBarExtensionDelegate::CreateRaw(this, &FSkeletaMeshEditorExtender::CreateToolbarButton));

	PostBuildDelegate.BindLambda([Editor = &SkeletalMeshEditor]()
		{
			if (Editor->IsValid())
			{
				Editor->Pin()->GetPersonaToolkit()->GetPreviewMeshComponent()->MarkRenderStateDirty();
			}
		});
}

void FSkeletaMeshEditorExtender::OnAssetSelected(const FAssetData& AssetData)
{
	ULODRecipe* LODRecipe = Cast<ULODRecipe>(AssetData.GetAsset());
	if (!LODRecipe)
	{
		return;
	}

	LODRecipe->AddMesh(SkeletalMeshEditor.Pin()->GetPersonaToolkit()->GetMesh());
}

#undef LOCTEXT_NAMESPACE


