// Icon Studio - Mathew Simon

#include "SIconStudioTab.h"

#include "IconStudioPreset.h"
#include "IconStudioSession.h"
#include "IconStudioSubsystem.h"

#include "Editor.h"
#include "Engine/Texture2D.h"
#include "IDetailsView.h"
#include "Modules/ModuleManager.h"
#include "PropertyEditorModule.h"
#include "Styling/AppStyle.h"
#include "Widgets/Images/SImage.h"
#include "Widgets/Input/SButton.h"
#include "Widgets/Layout/SBorder.h"
#include "Widgets/Layout/SScaleBox.h"
#include "Widgets/Layout/SSeparator.h"
#include "Widgets/Layout/SSplitter.h"
#include "Widgets/SBoxPanel.h"
#include "Widgets/SOverlay.h"
#include "Widgets/Text/STextBlock.h"

#define LOCTEXT_NAMESPACE "IconStudio"

namespace IconStudioTabLocal
{
	/** Un bouton de la barre d'actions, tous construits pareil. */
	TSharedRef<SWidget> MakeButton(const FText& Label, const FText& Tooltip, FOnClicked OnClicked)
	{
		return SNew(SButton)
			.Text(Label)
			.ToolTipText(Tooltip)
			.HAlign(HAlign_Center)
			.ContentPadding(FMargin(10.f, 4.f))
			.OnClicked(OnClicked);
	}
}

UIconStudioSubsystem* SIconStudioTab::GetStudio()
{
	return GEditor ? GEditor->GetEditorSubsystem<UIconStudioSubsystem>() : nullptr;
}

void SIconStudioTab::Construct(const FArguments& InArgs)
{
	PreviewBrush.DrawAs = ESlateBrushDrawType::Image;
	PreviewBrush.ImageSize = FVector2D(256.f, 256.f);

	StatusMessage = LOCTEXT("Ready", "Pick a preset and a subject to start.");

	ChildSlot
	[
		SNew(SSplitter)
		.Orientation(Orient_Horizontal)

		+ SSplitter::Slot()
		.Value(0.62f)
		[
			BuildPreviewPanel()
		]

		+ SSplitter::Slot()
		.Value(0.38f)
		[
			BuildSettingsPanel()
		]
	];

	// L'abonnement vient apres la construction : les panneaux de details doivent
	// exister avant qu'un premier rendu ne cherche a les remplir.
	if (UIconStudioSubsystem* const Studio = GetStudio())
	{
		PreviewHandle = Studio->OnPreviewUpdated.AddSP(this, &SIconStudioTab::HandlePreviewUpdated);
		StatusHandle = Studio->OnStatus.AddSP(this, &SIconStudioTab::HandleStatus);

		SessionDetails->SetObject(Studio->GetSession());

		// Ouvrir la fenetre suffit a avoir un preset utilisable : celui du
		// projet s'il en existe un, un neuf sinon.
		Studio->EnsurePreset();
		Studio->RefreshPreview();
	}
}

SIconStudioTab::~SIconStudioTab()
{
	// Le sous-systeme survit a la fenetre : sans desabonnement il diffuserait
	// dans un widget detruit a la premiere generation suivante.
	if (UIconStudioSubsystem* const Studio = GetStudio())
	{
		Studio->OnPreviewUpdated.Remove(PreviewHandle);
		Studio->OnStatus.Remove(StatusHandle);
	}
}

TSharedRef<SWidget> SIconStudioTab::BuildPreviewPanel()
{
	// Le damier est le seul moyen de VOIR la transparence : sur un aplat, une
	// icone au fond noir et une icone au fond transparent sont identiques, et on
	// ne s'apercoit du probleme qu'une fois la texture dans le jeu.
	const FSlateBrush* Checkerboard = FAppStyle::Get().GetBrush(TEXT("Checkerboard"));

	const TAttribute<EVisibility> PlaceholderVisibility = TAttribute<EVisibility>::CreateLambda(
		[this]() { return PreviewBrush.GetResourceObject() ? EVisibility::Collapsed : EVisibility::Visible; });

	return SNew(SVerticalBox)

		+ SVerticalBox::Slot()
		.FillHeight(1.f)
		.Padding(6.f)
		[
			SNew(SBorder)
			.BorderImage(FAppStyle::Get().GetBrush(TEXT("ToolPanel.GroupBorder")))
			.Padding(8.f)
			[
				SNew(SOverlay)

				+ SOverlay::Slot()
				[
					SNew(SImage).Image(Checkerboard)
				]

				+ SOverlay::Slot()
				.HAlign(HAlign_Center)
				.VAlign(VAlign_Center)
				[
					SNew(SScaleBox)
					.Stretch(EStretch::ScaleToFit)
					// Jamais au-dela de la taille native : une icone de 256
					// pixels etiree sur tout le panneau parait floue et donne une
					// fausse idee de sa nettete reelle. On la juge a sa taille.
					.StretchDirection(EStretchDirection::DownOnly)
					[
						SNew(SImage)
						.Image(&PreviewBrush)
						.Visibility(this, &SIconStudioTab::GetPreviewVisibility)
					]
				]

				+ SOverlay::Slot()
				.HAlign(HAlign_Center)
				.VAlign(VAlign_Center)
				[
					SNew(STextBlock)
					.Text(LOCTEXT("NoPreview", "No preview.\nPick a preset, then select an asset and press Use Selection."))
					.Justification(ETextJustify::Center)
					.Visibility(PlaceholderVisibility)
				]
			]
		]

		+ SVerticalBox::Slot()
		.AutoHeight()
		.Padding(6.f, 0.f)
		[
			SNew(SHorizontalBox)

			+ SHorizontalBox::Slot()
			.AutoWidth()
			.Padding(0.f, 0.f, 4.f, 0.f)
			[
				IconStudioTabLocal::MakeButton(
					LOCTEXT("UseSelection", "Use Selection"),
					LOCTEXT("UseSelectionTip", "Preview the first renderable asset selected in the Content Browser."),
					FOnClicked::CreateSP(this, &SIconStudioTab::OnUseSelectionClicked))
			]

			+ SHorizontalBox::Slot()
			.AutoWidth()
			.Padding(0.f, 0.f, 4.f, 0.f)
			[
				IconStudioTabLocal::MakeButton(
					LOCTEXT("Refresh", "Refresh"),
					LOCTEXT("RefreshTip", "Render the preview again."),
					FOnClicked::CreateSP(this, &SIconStudioTab::OnRefreshClicked))
			]

			+ SHorizontalBox::Slot()
			.FillWidth(1.f)
			[
				IconStudioTabLocal::MakeButton(
					LOCTEXT("Generate", "Generate"),
					LOCTEXT("GenerateTip", "Write the icon of the previewed subject and assign it."),
					FOnClicked::CreateSP(this, &SIconStudioTab::OnGenerateClicked))
			]
		]

		+ SVerticalBox::Slot()
		.AutoHeight()
		.Padding(6.f)
		[
			SNew(STextBlock)
			.Text(this, &SIconStudioTab::GetStatusText)
			.AutoWrapText(true)
		];
}

TSharedRef<SWidget> SIconStudioTab::BuildSettingsPanel()
{
	FPropertyEditorModule& PropertyEditor = FModuleManager::LoadModuleChecked<FPropertyEditorModule>(TEXT("PropertyEditor"));

	FDetailsViewArgs SessionArgs;
	SessionArgs.bAllowSearch = false;
	SessionArgs.bHideSelectionTip = true;
	SessionArgs.bShowOptions = false;
	// Le bandeau de nom afficherait "IconStudioSession", ce qui n'apprend rien.
	SessionArgs.NameAreaSettings = FDetailsViewArgs::HideNameArea;
	SessionDetails = PropertyEditor.CreateDetailView(SessionArgs);

	FDetailsViewArgs PresetArgs = SessionArgs;
	// Le preset a beaucoup plus de champs : la recherche y devient utile.
	PresetArgs.bAllowSearch = true;
	PresetDetails = PropertyEditor.CreateDetailView(PresetArgs);

	return SNew(SVerticalBox)

		+ SVerticalBox::Slot()
		.AutoHeight()
		[
			SessionDetails.ToSharedRef()
		]

		+ SVerticalBox::Slot()
		.AutoHeight()
		.Padding(0.f, 4.f)
		[
			SNew(SSeparator)
		]

		+ SVerticalBox::Slot()
		.FillHeight(1.f)
		[
			PresetDetails.ToSharedRef()
		]

		+ SVerticalBox::Slot()
		.AutoHeight()
		.Padding(4.f)
		[
			SNew(SHorizontalBox)

			+ SHorizontalBox::Slot()
			.FillWidth(1.f)
			.Padding(0.f, 0.f, 4.f, 0.f)
			[
				IconStudioTabLocal::MakeButton(
					LOCTEXT("AddSelection", "Add Selection"),
					LOCTEXT("AddSelectionTip", "Add every renderable asset selected in the Content Browser to the batch."),
					FOnClicked::CreateSP(this, &SIconStudioTab::OnAddSelectionClicked))
			]

			+ SHorizontalBox::Slot()
			.FillWidth(1.f)
			.Padding(0.f, 0.f, 4.f, 0.f)
			[
				IconStudioTabLocal::MakeButton(
					LOCTEXT("GenerateBatch", "Generate Batch"),
					LOCTEXT("GenerateBatchTip", "Render every asset in the batch list and assign their icons."),
					FOnClicked::CreateSP(this, &SIconStudioTab::OnGenerateBatchClicked))
			]

			+ SHorizontalBox::Slot()
			.AutoWidth()
			[
				IconStudioTabLocal::MakeButton(
					LOCTEXT("ClearBatch", "Clear"),
					LOCTEXT("ClearBatchTip", "Empty the batch list."),
					FOnClicked::CreateSP(this, &SIconStudioTab::OnClearBatchClicked))
			]
		];
}

EVisibility SIconStudioTab::GetPreviewVisibility() const
{
	return PreviewBrush.GetResourceObject() ? EVisibility::Visible : EVisibility::Collapsed;
}

void SIconStudioTab::HandlePreviewUpdated(UTexture2D* PreviewTexture)
{
	if (PreviewTexture)
	{
		PreviewBrush.SetResourceObject(PreviewTexture);
		PreviewBrush.ImageSize = FVector2D(PreviewTexture->GetSizeX(), PreviewTexture->GetSizeY());
	}
	else
	{
		PreviewBrush.SetResourceObject(nullptr);
	}

	// Le panneau du bas suit le preset choisi dans celui du haut. SetObject sur
	// le meme objet ne fait rien, donc editer une valeur ne coupe pas la saisie
	// en cours malgre le rendu declenche a chaque frappe.
	if (PresetDetails.IsValid())
	{
		UIconStudioSubsystem* const Studio = GetStudio();
		UIconStudioPreset* const Preset = Studio ? Studio->GetSession()->Preset.Get() : nullptr;
		PresetDetails->SetObject(Preset);
	}
}

void SIconStudioTab::HandleStatus(const FString& Message)
{
	StatusMessage = FText::FromString(Message);
}

FReply SIconStudioTab::OnRefreshClicked()
{
	if (UIconStudioSubsystem* const Studio = GetStudio())
	{
		Studio->RefreshPreview();
	}
	return FReply::Handled();
}

FReply SIconStudioTab::OnGenerateClicked()
{
	if (UIconStudioSubsystem* const Studio = GetStudio())
	{
		Studio->GenerateCurrent();
	}
	return FReply::Handled();
}

FReply SIconStudioTab::OnUseSelectionClicked()
{
	if (UIconStudioSubsystem* const Studio = GetStudio())
	{
		Studio->SetSubjectFromContentBrowser();
	}
	return FReply::Handled();
}

FReply SIconStudioTab::OnAddSelectionClicked()
{
	if (UIconStudioSubsystem* const Studio = GetStudio())
	{
		Studio->AddContentBrowserSelectionToBatch();
	}
	return FReply::Handled();
}

FReply SIconStudioTab::OnGenerateBatchClicked()
{
	if (UIconStudioSubsystem* const Studio = GetStudio())
	{
		Studio->GenerateBatch();
	}
	return FReply::Handled();
}

FReply SIconStudioTab::OnClearBatchClicked()
{
	if (UIconStudioSubsystem* const Studio = GetStudio())
	{
		Studio->ClearBatch();
	}
	return FReply::Handled();
}

#undef LOCTEXT_NAMESPACE
