// Project Lab - NHTV IGAD
//////////////////////////////////////////
// Author: Yoshi van Belkom - 130118
//////////////////////////////////////////

#include "SkillTreeEditorPrivatePCH.h"
#include "SkillTreeEditorViewport.h"
#include "SceneViewport.h"

#include "GraphEditor.h"
#include "SDockTab.h"
#include "SEditorViewport.h"
#include "SCommonEditorViewportToolbarBase.h"
#include "ISkillTree.h"
#include "SkillTreeEditorViewportClient.h"
#include "SSkillTreeEditorViewportToolbar.h"
#include "SkillTreeObject.h"
#include "SkillObject.h"
#include "WorkspaceMenuStructureModule.h"

#include "SkillTreeEditorCommands.h"
#include "SSingleObjectDetailsPanel.h"
#include "SkillTreeDetailsCustomization.h"

#define LOCTEXT_NAMESPACE "SkillTreeEditor"

const FName skillTreeEditorAppName = FName( TEXT( "SkillTreeEditorApp" ) );

// Tab identifiers.
struct FSkillTreeEditorTabs
{
	static const FName skillTreeViewportID;
	static const FName skillTreeDetailViewportID;
	static const FName skillViewportID;
	static const FName skillDetailViewportID;
};

const FName FSkillTreeEditorTabs::skillTreeViewportID( TEXT( "STViewport" ) );
const FName FSkillTreeEditorTabs::skillViewportID( TEXT( "SViewport" ) );
const FName FSkillTreeEditorTabs::skillTreeDetailViewportID( TEXT( "STDViewport" ) );
const FName FSkillTreeEditorTabs::skillDetailViewportID( TEXT( "SDViewport" ) );

//TODO: Make separate headers for these classes.
/////////////////////////////////////////////////////
// SSkillTreePropertiesTabBody

class SSkillTreePropertiesTabBody : public SSingleObjectDetailsPanel
{
public:
	SLATE_BEGIN_ARGS( SSkillTreePropertiesTabBody ) {}
	SLATE_END_ARGS()

private:
	// Pointer back to owning skill tree editor instance.
	TWeakPtr<class FSkillTreeEditorViewport> m_skillTreeEditorPtr;

public:
	void Construct( const FArguments& a_args, TSharedPtr<FSkillTreeEditorViewport> a_skillTreeEditor )
	{
		m_skillTreeEditorPtr = a_skillTreeEditor;

		SSingleObjectDetailsPanel::Construct( SSingleObjectDetailsPanel::FArguments().HostCommandList( a_skillTreeEditor->GetToolkitCommands() ), true, true );

		FOnGetDetailCustomizationInstance customizeSkillTreeForEditor = FOnGetDetailCustomizationInstance::CreateStatic( &FSkillTreeDetailsCustomization::MakeInstanceForSkillTreeEditor );
		PropertyView->RegisterInstancedCustomPropertyLayout( USkillTreeObject::StaticClass(), customizeSkillTreeForEditor );
	}

	// SSingleObjectDetailsPanel interface
	virtual UObject* GetObjectToObserve() const override
	{
		return m_skillTreeEditorPtr.Pin()->GetSkillTreeBeingEdited();
	}

	virtual TSharedRef<SWidget> PopulateSlot( TSharedRef<SWidget> a_propertyEditorWidget ) override
	{
		return SNew( SVerticalBox )
			+ SVerticalBox::Slot()
			.FillHeight( 1 )
			[
				a_propertyEditorWidget
			];
	}
	// End of SSingleObjectDetailsPanel interface
};

/////////////////////////////////////////////////////
// SSkillPropertiesTabBody

class SSkillPropertiesTabBody : public SSingleObjectDetailsPanel
{
public:
	SLATE_BEGIN_ARGS( SSkillPropertiesTabBody ) {}
	SLATE_END_ARGS()

private:
	// Pointer back to owning sprite editor instance (the keeper of state)
	TWeakPtr<class FSkillTreeEditorViewport> m_skillTreeEditorPtr;

public:
	void Construct( const FArguments& InArgs, TSharedPtr<FSkillTreeEditorViewport> a_skillTreeEditor )
	{
		m_skillTreeEditorPtr = a_skillTreeEditor;

		SSingleObjectDetailsPanel::Construct( SSingleObjectDetailsPanel::FArguments().HostCommandList( a_skillTreeEditor->GetToolkitCommands() ), true, true );

		FOnGetDetailCustomizationInstance customizeSkillTreeForEditor = FOnGetDetailCustomizationInstance::CreateStatic( &FSkillDetailsCustomization::MakeInstanceForSkillTreeEditor );
		PropertyView->RegisterInstancedCustomPropertyLayout( USkillObject::StaticClass(), customizeSkillTreeForEditor );
	}

	// SSingleObjectDetailsPanel interface
	virtual UObject* GetObjectToObserve() const override
	{
		return m_skillTreeEditorPtr.Pin()->GetSkillBeingEdited();
	}

	virtual TSharedRef<SWidget> PopulateSlot( TSharedRef<SWidget> a_propertyEditorWidget ) override
	{
		return SNew( SVerticalBox )
			+ SVerticalBox::Slot()
			.FillHeight( 1 )
			[
				a_propertyEditorWidget
			];
	}
	// End of SSingleObjectDetailsPanel interface
};

/////////////////////////////////////////////////////
// SSkillTreeEditorViewport

class SSkillTreeEditorViewport : public SEditorViewport, public ICommonEditorViewportToolbarInfoProvider
{
public:
	SLATE_BEGIN_ARGS( SSkillTreeEditorViewport ) {}
	SLATE_END_ARGS()

	void Construct( const FArguments& a_args, TSharedPtr<FSkillTreeEditorViewport> a_spriteEditor );

	// SEditorViewport interface
	virtual void BindCommands() override;
	virtual TSharedRef<FEditorViewportClient> MakeEditorViewportClient() override;
	virtual TSharedPtr<SWidget> MakeViewportToolbar() override;
	virtual EVisibility GetTransformToolbarVisibility() const override;
	virtual void OnFloatingButtonClicked() override;
	// End of SEditorViewport interface

	// ICommonEditorViewportToolbarInfoProvider interface
	virtual TSharedRef<class SEditorViewport> GetViewportWidget() override;
	virtual TSharedPtr<FExtender> GetExtenders() const override;
	// End of ICommonEditorViewportToolbarInfoProvider interface

	void ActivateEditMode()
	{
		m_editorViewportClient->ActivateEditMode();
	}

	void IsSkill( bool a_state )
	{
		m_editorViewportClient->IsSkill( a_state );
	}

private:
	// Pointer back to owning Skill Tree editor instance (the keeper of state).
	TWeakPtr<class FSkillTreeEditorViewport> m_skillTreeEditorPtr;

	// Viewport client
	TSharedPtr<FSkillTreeEditorViewportClient> m_editorViewportClient;
};

void SSkillTreeEditorViewport::Construct( const FArguments& a_args, TSharedPtr<FSkillTreeEditorViewport> a_skillTreeEditor )
{
	m_skillTreeEditorPtr = a_skillTreeEditor;

	SEditorViewport::Construct( SEditorViewport::FArguments() );
}

void SSkillTreeEditorViewport::BindCommands()
{
	SEditorViewport::BindCommands();

	const FSkillTreeEditorCommands& commands = FSkillTreeEditorCommands::Get();

	TSharedRef<FSkillTreeEditorViewportClient> editorViewportClientRef = m_editorViewportClient.ToSharedRef();

	// Show toggles
	//TODO: Remove.
	CommandList->MapAction(
		commands.setShowGrid,
		FExecuteAction::CreateSP( editorViewportClientRef, &FEditorViewportClient::SetShowGrid ),
		FCanExecuteAction(),
		FIsActionChecked::CreateSP( editorViewportClientRef, &FEditorViewportClient::IsSetShowGridChecked ) );


	// Editing modes
	//TODO: Remove.
	CommandList->MapAction(
		commands.enterViewMode,
		FExecuteAction::CreateSP( editorViewportClientRef, &FSkillTreeEditorViewportClient::EnterViewMode ),
		FCanExecuteAction(),
		FIsActionChecked::CreateSP( editorViewportClientRef, &FSkillTreeEditorViewportClient::IsInViewMode ) );
}

// Make a new client for the viewport.
TSharedRef<FEditorViewportClient> SSkillTreeEditorViewport::MakeEditorViewportClient()
{
	m_editorViewportClient = MakeShareable( new FSkillTreeEditorViewportClient( m_skillTreeEditorPtr, SharedThis( this ) ) );

	return m_editorViewportClient.ToSharedRef();
}

TSharedPtr<SWidget> SSkillTreeEditorViewport::MakeViewportToolbar()
{
	return SNew( SSkillTreeEditorViewportToolbar, SharedThis( this ) );
}

EVisibility SSkillTreeEditorViewport::GetTransformToolbarVisibility() const
{
	return EVisibility::Visible;
}

TSharedRef<class SEditorViewport> SSkillTreeEditorViewport::GetViewportWidget()
{
	return SharedThis( this );
}

TSharedPtr<FExtender> SSkillTreeEditorViewport::GetExtenders() const
{
	TSharedPtr<FExtender> Result( MakeShareable( new FExtender ) );
	return Result;
}

//Needed to be implemented but not used.
void SSkillTreeEditorViewport::OnFloatingButtonClicked()
{
}

/////////////////////////////////////////////////////
// FSkillTreeEditorViewport

TSharedRef<SDockTab> FSkillTreeEditorViewport::SpawnTab_SKViewport( const FSpawnTabArgs& a_args )
{
	return SNew( SDockTab )
		.Label( LOCTEXT( "SKViewportTab_Title", "Skill Tree" ) )
		[
			SNew( SOverlay )

			// The Skill Tree editor viewport
			+ SOverlay::Slot()
			[
				m_skillTreeViewport.ToSharedRef()
			]

			// Bottom-right corner text indicating the preview nature of the viewport.
			+ SOverlay::Slot()
				.Padding( 10 )
				.VAlign( VAlign_Bottom )
				.HAlign( HAlign_Right )
				[
					SNew( STextBlock )
					.Visibility( EVisibility::HitTestInvisible )
					.TextStyle( FEditorStyle::Get(), "Graph.CornerText" )
					.Text( LOCTEXT( "SKViewportTab_CornerText", "Edit Skill Tree" ) )
				]
		];
}

TSharedRef<SDockTab> FSkillTreeEditorViewport::SpawnTab_SViewport( const FSpawnTabArgs& a_args )
{
	return SNew( SDockTab )
		.Label( LOCTEXT( "SViewportTab_Title", "Skill" ) )
		[
			SNew( SOverlay )

			// The sprite editor viewport
			+ SOverlay::Slot()
			[
				m_skillViewport.ToSharedRef()
			]

			// Bottom-right corner text indicating the preview nature of the viewport.
			+ SOverlay::Slot()
				.Padding( 10 )
				.VAlign( VAlign_Bottom )
				.HAlign( HAlign_Right )
				[
					SNew( STextBlock )
					.Visibility( EVisibility::HitTestInvisible )
					.TextStyle( FEditorStyle::Get(), "Graph.CornerText" )
					.Text( LOCTEXT("SViewportTab_CornerText", "Edit Skill") )
				]
		];
}

TSharedRef<SDockTab> FSkillTreeEditorViewport::SpawnTab_SKDViewport( const FSpawnTabArgs& a_args )
{
	TSharedPtr<FSkillTreeEditorViewport> editorPtr = SharedThis( this );

	// Spawn the tab
	return SNew( SDockTab )
		.Label( LOCTEXT( "SKDViewportTab_Title", "Skill Tree Details" ) )
		[
			SNew( SSkillTreePropertiesTabBody, editorPtr )
		];
}

TSharedRef<SDockTab> FSkillTreeEditorViewport::SpawnTab_SDViewport( const FSpawnTabArgs& a_args )
{
	TSharedPtr<FSkillTreeEditorViewport> editorPtr = SharedThis( this );

	// Spawn the tab
	return SNew( SDockTab )
		.Label( LOCTEXT( "SDViewportTab_Title", "Skill Details" ) )
		[
			SNew( SSkillPropertiesTabBody, editorPtr )
		];
}

void FSkillTreeEditorViewport::RegisterTabSpawners( const TSharedRef<class FTabManager>& a_tabManager )
{
	WorkspaceMenuCategory = a_tabManager->AddLocalWorkspaceMenuCategory( LOCTEXT( "WorkspaceMenu_SpriteEditor", "Skill Tree Editor" ) );
	auto workspaceMenuCategoryRef = WorkspaceMenuCategory.ToSharedRef();

	FAssetEditorToolkit::RegisterTabSpawners( a_tabManager );

	a_tabManager->RegisterTabSpawner( FSkillTreeEditorTabs::skillTreeViewportID, FOnSpawnTab::CreateSP( this, &FSkillTreeEditorViewport::SpawnTab_SKViewport ) )
		.SetDisplayName( LOCTEXT( "SKViewportTab", "Skill Tree" ) )
		.SetGroup( workspaceMenuCategoryRef )
		.SetIcon( FSlateIcon( FEditorStyle::GetStyleSetName(), "LevelEditor.Tabs.Viewports" ) );

	a_tabManager->RegisterTabSpawner( FSkillTreeEditorTabs::skillTreeDetailViewportID, FOnSpawnTab::CreateSP( this, &FSkillTreeEditorViewport::SpawnTab_SKDViewport ) )
		.SetDisplayName( LOCTEXT( "SKDViewportTab", "Skill Tree Details" ) )
		.SetGroup( workspaceMenuCategoryRef )
		.SetIcon( FSlateIcon( FEditorStyle::GetStyleSetName(), "LevelEditor.Tabs.Viewports" ) );

	a_tabManager->RegisterTabSpawner( FSkillTreeEditorTabs::skillViewportID, FOnSpawnTab::CreateSP( this, &FSkillTreeEditorViewport::SpawnTab_SViewport ) )
		.SetDisplayName( LOCTEXT( "SViewportTab", "Skill" ) )
		.SetGroup( workspaceMenuCategoryRef )
		.SetIcon( FSlateIcon( FEditorStyle::GetStyleSetName(), "LevelEditor.Tabs.Viewports" ) );

	a_tabManager->RegisterTabSpawner( FSkillTreeEditorTabs::skillDetailViewportID, FOnSpawnTab::CreateSP( this, &FSkillTreeEditorViewport::SpawnTab_SDViewport ) )
		.SetDisplayName( LOCTEXT( "SDViewportTab", "Skill Details" ) )
		.SetGroup( workspaceMenuCategoryRef )
		.SetIcon( FSlateIcon( FEditorStyle::GetStyleSetName(), "LevelEditor.Tabs.Viewports" ) );
}

void FSkillTreeEditorViewport::UnregisterTabSpawners( const TSharedRef<class FTabManager>& a_tabManager )
{
	FAssetEditorToolkit::UnregisterTabSpawners( a_tabManager );

	a_tabManager->UnregisterTabSpawner( FSkillTreeEditorTabs::skillTreeViewportID );
	a_tabManager->UnregisterTabSpawner( FSkillTreeEditorTabs::skillTreeDetailViewportID );
	a_tabManager->UnregisterTabSpawner( FSkillTreeEditorTabs::skillViewportID );
	a_tabManager->UnregisterTabSpawner( FSkillTreeEditorTabs::skillDetailViewportID );
}

FName FSkillTreeEditorViewport::GetToolkitFName() const
{
	return FName( "SkillTreeEditor" );
}

void FSkillTreeEditorViewport::SetSkillBeingEdited( USkillObject* a_skill )
{
	if ( (a_skill != m_skillBeingEdited) && (a_skill != nullptr) )
	{
		USkillObject* oldSkill = m_skillBeingEdited;
		m_skillBeingEdited = a_skill;

		//No longer needed.
		/*
		RemoveEditingObject( oldSkill );
		AddEditingObject( a_skill );
		*/
	}
}

FText FSkillTreeEditorViewport::GetBaseToolkitName() const
{
	return LOCTEXT( "SkillTreeEditorAppLabel", "Skill Tree" );
}

FText FSkillTreeEditorViewport::GetToolkitName() const
{
	bool dirtyState = (m_skillTreeBeingEdited->GetOutermost()->IsDirty() || m_skillBeingEdited->GetOutermost()->IsDirty());

	FFormatNamedArguments args;
	args.Add( TEXT( "SkillTreeName" ), FText::FromString( m_skillTreeBeingEdited->GetName() ) );
	args.Add( TEXT( "DirtyState" ), dirtyState ? FText::FromString( TEXT( "*" ) ) : FText::GetEmpty() );
	return FText::Format( LOCTEXT( "SkillTreeEditorAppLabel", "{SkillTreeName}{DirtyState}" ), args );
}

FText FSkillTreeEditorViewport::GetToolkitToolTipText() const
{
	return LOCTEXT( "SkillTreeToolTip", "Test tooltip text" );
}

FString FSkillTreeEditorViewport::GetWorldCentricTabPrefix() const
{
	return TEXT( "SkillTreeEditor" );
}

FString FSkillTreeEditorViewport::GetDocumentationLink() const
{
	return TEXT( "http://iamfromtheinternet.nl/wiki/index.php/Skill_Tree" );
}

FLinearColor FSkillTreeEditorViewport::GetWorldCentricTabColorScale() const
{
	return FLinearColor::White;
}

void FSkillTreeEditorViewport::CreateModeToolbarWidgets( FToolBarBuilder& a_ignoredBuilder )
{
	FToolBarBuilder toolbarBuilder( m_skillTreeViewport->GetCommandList(), FMultiBoxCustomization::None );
	toolbarBuilder.AddToolBarButton( FSkillTreeEditorCommands::Get().enterViewMode );
	AddToolbarWidget( toolbarBuilder.MakeWidget() );
}

void FSkillTreeEditorViewport::InitSkillTreeEditor( const EToolkitMode::Type a_mode, const TSharedPtr< class IToolkitHost >& a_toolkitHost, class USkillTreeObject* a_skillTree, class USkillObject* a_skill )
{
	m_skillTreeBeingEdited = a_skillTree;
	m_tmpSkills = m_skillTreeBeingEdited->skills;
	if ( !a_skill )
	{
		m_skillBeingEdited = NewObject<USkillObject>();
		m_skillBeingEdited->hexMap.width = 0;
		m_skillBeingEdited->hexMap.height = 0;
	}
	else
	{
		m_skillBeingEdited = a_skill;
		selectedY = m_tmpSkills.Find( TSubclassOf<USkillObject>((UClass*)m_skillBeingEdited) );
	}

	FSkillTreeEditorCommands::Register();

	TSharedPtr<FSkillTreeEditorViewport> skillTreeEditorPtr = SharedThis( this );
	m_skillTreeViewport = SNew( SSkillTreeEditorViewport, skillTreeEditorPtr );
	m_skillViewport = SNew( SSkillTreeEditorViewport, skillTreeEditorPtr );
	m_skillViewport->IsSkill( true );

	const TSharedRef<FTabManager::FLayout> defaultLayout = FTabManager::NewLayout( "SkillTreeEditor_Layout_v2" )
		->AddArea
		(
			FTabManager::NewPrimaryArea()
			->SetOrientation( Orient_Vertical )
			->Split
			(
				FTabManager::NewStack()
				->SetSizeCoefficient( 0.1f )
				->SetHideTabWell( true )
				->AddTab( GetToolbarTabId(), ETabState::OpenedTab )
			)
			->Split
			(
				FTabManager::NewSplitter()
				->SetOrientation( Orient_Horizontal )
				->SetSizeCoefficient( 0.9f )
				->Split
				(
					FTabManager::NewStack()
					->SetSizeCoefficient( 0.35f )
					->SetHideTabWell( true )
					->AddTab( FSkillTreeEditorTabs::skillTreeViewportID, ETabState::OpenedTab )
				)
				->Split
				(
					FTabManager::NewStack()
					->SetSizeCoefficient( 0.15f )
					->SetHideTabWell( true )
					->AddTab( FSkillTreeEditorTabs::skillTreeDetailViewportID, ETabState::OpenedTab )
				)
				->Split
				(
					FTabManager::NewStack()
					->SetSizeCoefficient( 0.35f )
					->SetHideTabWell( true )
					->AddTab( FSkillTreeEditorTabs::skillViewportID, ETabState::OpenedTab )
				)
				->Split
				(
					FTabManager::NewStack()
					->SetSizeCoefficient( 0.15f )
					->SetHideTabWell( true )
					->AddTab( FSkillTreeEditorTabs::skillDetailViewportID, ETabState::OpenedTab )
				)
			)
		);

	// Initialize the asset editor
	InitAssetEditor( a_mode, a_toolkitHost, skillTreeEditorAppName, defaultLayout, true, true, a_skillTree );

	m_skillTreeViewport->ActivateEditMode();
	m_skillViewport->ActivateEditMode();

	// Extend things
	RegenerateMenusAndToolbars();
}

void FSkillTreeEditorViewport::UpdateSkills()
{
	if ( m_skillTreeBeingEdited->skills != m_tmpSkills )
	{
		for ( int i = 0; i < m_tmpSkills.Num(); i++ )
		{
			if ( m_tmpSkills[i] != NULL && !m_skillTreeBeingEdited->skills.Contains( m_tmpSkills[i] ) )
			{
				USkillObject* tmpSkill = (USkillObject*)*m_tmpSkills[i];//m_tmpSkills[i]->GetDefaultObject<USkillObject>();
				if ( tmpSkill != NULL ) tmpSkill->skillTree = NULL;
			}
		}
		for ( int i = 0; i < m_skillTreeBeingEdited->skills.Num(); i++ )
		{
			if ( m_skillTreeBeingEdited->skills[i] != NULL && !m_tmpSkills.Contains( m_skillTreeBeingEdited->skills[i] ) )
			{
				USkillObject* tmpSkill = (USkillObject*)*m_skillTreeBeingEdited->skills[i]; //m_skillTreeBeingEdited->skills[i]->GetDefaultObject<USkillObject>();
				if ( tmpSkill != NULL ) tmpSkill->skillTree = m_skillTreeBeingEdited;
			}
		}
		m_tmpSkills = m_skillTreeBeingEdited->skills;
	}
}

void FSkillTreeEditorViewport::SaveAsset_Execute()
{
	TArray< UPackage* > PackagesToSave;
	PackagesToSave.Add( m_skillTreeBeingEdited->GetOutermost() );
	PackagesToSave.Add( m_skillBeingEdited->GetOutermost() );

	FEditorFileUtils::PromptForCheckoutAndSave( PackagesToSave, bCheckDirtyOnAssetSave, /*bPromptToSave=*/ false );
}

#undef LOCTEXT_NAMESPACE