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

#include "SkillTreeEditorPrivatePCH.h"
#include "SkillTreeEditorViewportClient.h"
#include "SceneViewport.h"

#include "PreviewScene.h"
#include "ScopedTransaction.h"
#include "Runtime/Engine/Public/ComponentReregisterContext.h"

#include "AssetToolsModule.h"
#include "AssetRegistryModule.h"
#include "CanvasTypes.h"
#include "CanvasItem.h"

#include "PhysicsEngine/BodySetup2D.h"
#include "SEditorViewport.h"

#include "SNotificationList.h"
#include "NotificationManager.h"

#include "SkillTreeFactory.h"
#include "SkillTreeObject.h"
#include "SkillObject.h"

#define LOCTEXT_NAMESPACE "SkillTreeEditor"

FSkillTreeEditorViewportClient::FSkillTreeEditorViewportClient( TWeakPtr<FSkillTreeEditorViewport> a_skillTreeEditor, TWeakPtr<class SEditorViewport> a_skillTreeEditorViewportPtr )
	: FEditorViewportClient( new FAssetEditorModeManager(), nullptr, a_skillTreeEditorViewportPtr )
	, m_skillTreeEditorPtr( a_skillTreeEditor )
	, m_skillTreeEditorViewportPtr( a_skillTreeEditorViewportPtr )
{
	check( m_skillTreeEditorPtr.IsValid() && m_skillTreeEditorViewportPtr.IsValid() );

	// The tile map editor fully supports mode tools and isn't doing any incompatible stuff with the Widget
	Widget->SetUsesEditorModeTools( ModeTools );

	PreviewScene = &m_ownedPreviewScene;
	((FAssetEditorModeManager*)ModeTools)->SetPreviewScene( PreviewScene );

	SetRealtime( true );

	DrawHelper.bDrawGrid = false;

	EngineShowFlags.DisableAdvancedFeatures();
	EngineShowFlags.CompositeEditorPrimitives = false; //REMINDER: Turned off.
}

void FSkillTreeEditorViewportClient::Draw( const FSceneView* a_view, FPrimitiveDrawInterface* a_drawInterface )
{
	FEditorViewportClient::Draw( a_view, a_drawInterface );
}

void FSkillTreeEditorViewportClient::DrawCanvas( FViewport& a_viewport, FSceneView& a_view, FCanvas& a_canvas )
{
	const bool isHitTesting = a_canvas.IsHitTesting();
	if ( !isHitTesting )
	{
		a_canvas.SetHitProxy( nullptr );
	}

	if ( !m_skillTreeEditorPtr.IsValid() )
	{
		return;
	}

	m_screenSize = a_viewport.GetSizeXY();

	USkillTreeObject* skillTree = m_skillTreeEditorPtr.Pin()->GetSkillTreeBeingEdited();
	USkillObject* skill = m_skillTreeEditorPtr.Pin()->GetSkillBeingEdited();

	int offsetY = -m_hexSize;
	int height;
	int maxY;
	int width;

	if ( m_state )
	{
		height = skill->hexMap.height;
		if ( height % 2 != 0 ) offsetY = m_hexSize;
		width = skill->hexMap.width;
		maxY = height - 1;
	}
	else
	{
		height = 16;
		width = 13;
		maxY = 15;
	}

	FLinearColor enabledColor = FLinearColor( 0.43f, 0.43f, 0.43f );
	FLinearColor disabledColor = FLinearColor( 0.10f, 0.10f, 0.10f );

	for ( float x = 0; x < width; x++ )
	{
		maxY = (maxY == (height - 1) ? height : (height - 1));
		offsetY = (offsetY == -m_hexSize ? 0 : -m_hexSize);
		for ( int y = 0; y < maxY; y++ )
		{
			bool test;
			if ( m_state )
			{
				test = skill->hexMap.Get( x, y );
			}
			else
			{
				test = skillTree->hexMap.Get( x, y );
			} 
			FLinearColor hexColor = enabledColor;
			if ( !test )
			{
				hexColor = disabledColor;
			}
			float X = (x - (((float)width) / 2.0f)) * m_hexSize * 2;
			float Y = ((y - (maxY / 2)) * m_hexSize * 2) + offsetY;
			FCanvasNGonItem hexagonBorder( FVector2D( ( m_screenSize.X / 2 ) + X, ( m_screenSize.Y / 2 ) + Y ), FVector2D( m_hexSize, m_hexSize ), 6, hexColor );
			hexagonBorder.Draw( &a_canvas );

			if ( m_state )
			{
				FCanvasTextItem TextItem( FVector2D( ( m_screenSize.X / 2 ) + X, ( m_screenSize.Y / 2 ) + Y ), FText::Format( LOCTEXT( "PositionStr", "{0},{1}" ), FText::AsNumber( x ), FText::AsNumber( y ) ), GEngine->GetSmallFont(), FLinearColor::White );
				TextItem.EnableShadow( FLinearColor::Black );
				TextItem.bCentreX = true;

				TextItem.Draw( &a_canvas );
			}
		}
	}

	float hexmapWidth = ( m_screenSize.X / 2 ) + ( ( ( width - 1 ) - ( ( (float)width ) / 2.0f ) ) * m_hexSize * 2 );
	hexmapWidth += m_hexSize;
	int boxWidth = ( ( m_screenSize.X - hexmapWidth ) );
	boxWidth = FMath::Clamp( boxWidth, 0, 150 );
	if ( !m_state )
	{
		for ( int i = 0, y = 50; i < skillTree->skills.Num(); i++, y += 20 )
		{
			if ( !skillTree->skills[i] ) continue;
			if ( m_hoverY == i || m_skillTreeEditorPtr.Pin()->selectedY == i )
			{
				int minboxX = m_screenSize.X - boxWidth;
				int minboxY = y;
				FCanvasBoxItem boxItem( FVector2D( minboxX, minboxY ), FVector2D( boxWidth, 20) );
				if ( m_hoverY == i ) boxItem.SetColor( FLinearColor::Black );
				if ( m_skillTreeEditorPtr.Pin()->selectedY == i ) boxItem.SetColor( FLinearColor::White );
				boxItem.Draw( &a_canvas );
			}
			FFormatNamedArguments args;
			args.Add( TEXT( "DirtyState" ), skillTree->skills[i]->GetOutermost()->IsDirty() ? FText::FromString( TEXT( "*" ) ) : FText::GetEmpty() );
			args.Add( TEXT( "SkillName" ), FText::AsCultureInvariant( skillTree->skills[i]->GetName() ) );
			const FText assetName = FText::Format( LOCTEXT( "SkillName", "{SkillName}{DirtyState}" ), args );
			FCanvasTextItem TextItem( FVector2D( m_screenSize.X - ( ( m_screenSize.X - hexmapWidth ) / 2 ), y ), assetName, GEngine->GetSmallFont(), FLinearColor::White );
			TextItem.EnableShadow( FLinearColor::Black );
			TextItem.bCentreX = true;

			TextItem.Draw( &a_canvas );
		}
	}

	int minboxX = 0;
	int minboxY = 50;
	FCanvasTileItem enabledColorSquare( FVector2D( 0, 50 ), FVector2D( boxWidth, 20 ), enabledColor );
	FCanvasTileItem disabledColorSquare( FVector2D( 0, 70 ), FVector2D( boxWidth, 20 ), disabledColor );
	enabledColorSquare.Draw( &a_canvas );
	disabledColorSquare.Draw( &a_canvas );

	FCanvasTextItem enabledTextItem( FVector2D( boxWidth / 2, 50 ), FText::FromString( TEXT( "Solid" ) ), GEngine->GetSmallFont(), FLinearColor::White );
	enabledTextItem.EnableShadow( FLinearColor::Black );
	enabledTextItem.bCentreX = true;
	enabledTextItem.Draw( &a_canvas );

	FCanvasTextItem disabledTextItem( FVector2D( boxWidth / 2, 70 ), FText::FromString( TEXT( "Empty" ) ), GEngine->GetSmallFont(), FLinearColor::White );
	disabledTextItem.EnableShadow( FLinearColor::Black );
	disabledTextItem.bCentreX = true;
	disabledTextItem.Draw( &a_canvas );

	FEditorViewportClient::DrawCanvas( a_viewport, a_view, a_canvas );
}

void FSkillTreeEditorViewportClient::MouseMove( FViewport* a_viewport, int32 a_mouseX, int32 a_mouseY )
{
	if ( m_state ) return;
	USkillTreeObject* skillTree = m_skillTreeEditorPtr.Pin()->GetSkillTreeBeingEdited();
	int width = 13;

	float hexmapWidth = ( m_screenSize.X / 2 ) + ( ( ( width - 1 ) - ( ( (float)width ) / 2.0f ) ) * m_hexSize * 2 );
	hexmapWidth += m_hexSize;

	int minboxX = m_screenSize.X - ( ( m_screenSize.X - hexmapWidth ) );
	int maxboxX = m_screenSize.X;
	for ( int i = 0, y = 50; i < skillTree->skills.Num(); i++, y += 20 )
	{
		int minboxY = y;
		int maxboxY = y + 20;
		if ( !skillTree->skills[i] ) continue;
		if ( a_mouseX >= minboxX && a_mouseX <= maxboxX && a_mouseY >= minboxY && a_mouseY <= maxboxY )
		{
			m_hoverY = i;
			FEditorViewportClient::MouseMove( a_viewport, a_mouseX, a_mouseY );
			return;
		}
	}
	m_hoverY = -1;
	
	FEditorViewportClient::MouseMove( a_viewport, a_mouseX, a_mouseY );
}

void FSkillTreeEditorViewportClient::InternalActivateNewMode( )
{
	//m_currentMode = a_newMode;
	Viewport->InvalidateHitProxy();
}

FLinearColor FSkillTreeEditorViewportClient::GetBackgroundColor() const
{
	return FLinearColor( 0.20f, 0.22f, 0.22f );
}

void FSkillTreeEditorViewportClient::ActivateEditMode()
{
	// Activate the skill tree edit mode
	ModeTools->SetToolkitHost( m_skillTreeEditorPtr.Pin()->GetToolkitHost() );
	ModeTools->ActivateDefaultMode();
}

void FSkillTreeEditorViewportClient::Tick( float a_deltaSeconds )
{
	FEditorViewportClient::Tick( a_deltaSeconds );

	m_skillTreeEditorPtr.Pin()->UpdateSkills();

	if ( !GIntraFrameDebuggingGameThread )
	{
		m_ownedPreviewScene.GetWorld()->Tick( LEVELTICK_All, a_deltaSeconds );
	}
}

void FSkillTreeEditorViewportClient::ProcessClick( FSceneView& a_view, HHitProxy* a_hitProxy, FKey a_key, EInputEvent a_event, uint32 a_hitX, uint32 a_hitY )
{
	bool handled = false;
	USkillTreeObject* skillTree = m_skillTreeEditorPtr.Pin()->GetSkillTreeBeingEdited();
	USkillObject* skill = m_skillTreeEditorPtr.Pin()->GetSkillBeingEdited();

	int offsetY = -m_hexSize;
	int height;
	int maxY;
	int width;

	if ( m_state )
	{
		height = skill->hexMap.height;
		width = skill->hexMap.width;
		if ( height % 2 != 0 ) offsetY = m_hexSize;
		maxY = height-1;
	}
	else
	{
		height = 16;
		width = 13;
		maxY = 15;
	}

	FVector2D mpos( a_hitX, a_hitY );

	for ( float x = 0; x < width; x++ )
	{
		maxY = (maxY == (height - 1) ? height : (height - 1));
		offsetY = ( offsetY == -m_hexSize ? 0 : -m_hexSize );
		for ( int y = 0; y < maxY; y++ )
		{
			float X = ( x - ( ( (float)width ) / 2.0f ) ) * m_hexSize * 2;
			float Y = ( ( y - ( maxY / 2 ) ) * m_hexSize * 2 ) + offsetY;
			FVector2D pos( ( m_screenSize.X / 2 ) + X, ( m_screenSize.Y / 2 ) + Y );
			float dist = FVector2D::Distance( pos, mpos );
			if ( dist <= m_hexSize )
			{
				if ( !m_state )
				{
					skillTree->hexMap.Invert( x, y );
					skillTree->Modify();
					skillTree->MarkPackageDirty();
					skillTree->PostEditChange();
				}
				else
				{
					skill->hexMap.Invert( x, y );
					skill->Modify();
					skill->MarkPackageDirty();
					skill->PostEditChange();
				}
				handled = true;
				Invalidate();
				break;
			}
		}
		if ( handled )
		{
			break;
		}
	}

	if ( !handled && !m_state )
	{
		float hexmapWidth = ( m_screenSize.X / 2 ) + ( ( ( width - 1 ) - ( ( (float)width ) / 2.0f ) ) * m_hexSize * 2 );
		hexmapWidth += m_hexSize;

		uint32 minboxX = m_screenSize.X - ( ( m_screenSize.X - hexmapWidth ) );
		uint32 maxboxX = m_screenSize.X;
		for ( int i = 0, y = 50; i < skillTree->skills.Num(); i++, y += 20 )
		{
			uint32 minboxY = y;
			uint32 maxboxY = y + 20;
			if ( !skillTree->skills[i] ) continue;
			if ( a_hitX >= minboxX && a_hitX <= maxboxX && a_hitY >= minboxY && a_hitY <= maxboxY )
			{
				m_skillTreeEditorPtr.Pin()->selectedY = i;
				handled = true;
				USkillObject* tmpCast = skillTree->skills[i]->GetDefaultObject<USkillObject>();
				if ( tmpCast != NULL ) m_skillTreeEditorPtr.Pin()->SetSkillBeingEdited( tmpCast );
			}
		}
	}

	if ( !handled )
	{
		FEditorViewportClient::ProcessClick( a_view, a_hitProxy, a_key, a_event, a_hitX, a_hitY );
	}
}

#undef LOCTEXT_NAMESPACE