/*
 * Copyright 2019,2020.2021 Sony Corporation
 */

// <-- Plugin Header -->
#include "Window/SRDisplayWindow.h"
#include "Window/SRDisplaySceneViewport.h"

// <-- Engine Header -->
#include <Engine/Engine.h>
#include <Engine/GameViewportClient.h>
#include <UnrealEngine.h>
#include <Framework/Application/SlateApplication.h>

// <-- Standard Library -->
#include <windef.h>
#include <WinUser.h>

namespace sr_display
{
	TSharedPtr<FSRDisplayWindow> FSRDisplayWindow::PlayWindow = nullptr;

	FSRDisplayWindow::FSRDisplayWindow(FVector2D Resolution)
	{

		// Hide and disable engine window
#if PLATFORM_WINDOWS
		HWND WindowHandle = (HWND)GEngine->GameViewport->GetWindow()->GetNativeWindow()->GetOSWindowHandle();
		if (WindowHandle)
		{
			::EnableWindow(WindowHandle, false);
			::SetParent(WindowHandle, HWND_MESSAGE);
		}
#endif

		// Create original window
		Window = SNew(SWindow)
			.Type(EWindowType::GameWindow)
			.Title(FText::FromString("SR Display Window"))
			.AutoCenter(EAutoCenter::PrimaryWorkArea)
			.ScreenPosition(FVector2D(0, 0))
			.ClientSize(Resolution)
			.SizingRule(ESizingRule::UserSized)
			.IsTopmostWindow(true)
			.ActivationPolicy(EWindowActivationPolicy::FirstShown)
			.FocusWhenFirstShown(true)
			.UseOSWindowBorder(true)
			.SaneWindowPlacement(false);

		InDelegate.BindRaw(this, &FSRDisplayWindow::OnWindowClosed);
		Window->SetOnWindowClosed(InDelegate);

		PlaySceneViewport = SNew(SSRDisplaySceneViewport);
		PlaySceneViewport->Init(Resolution.X, Resolution.Y);
		Window->SetContent(PlaySceneViewport.ToSharedRef());
	}

	FSRDisplayWindow::~FSRDisplayWindow()
	{
		InDelegate.Unbind();
	}

	TSharedPtr<SWindow> FSRDisplayWindow::GetWindow()
	{
		if (Window.IsValid())
		{
			return Window;
		}
		return nullptr;
	}

	UTextureRenderTarget2D* FSRDisplayWindow::GetRenderTarget()
	{
		if (PlaySceneViewport.IsValid())
		{
			return PlaySceneViewport->GetRenderTarget();
		}
		return nullptr;
	}

	void FSRDisplayWindow::RenderTexture_RenderThread(FRHICommandListImmediate& RHICmdList)
	{
		if (PlaySceneViewport.IsValid())
		{
			PlaySceneViewport->RenderTexture_RenderThread(RHICmdList);
		}
	}

	void FSRDisplayWindow::Initialize(FVector2D Resolution, FIntRect ScreenPosition)
	{
		if (!PlayWindow.IsValid())
		{
			FSRDisplayWindow::PlayWindow = MakeShareable(new FSRDisplayWindow(Resolution));
		}

		if (GEngine && FSRDisplayWindow::PlayWindow && FSRDisplayWindow::PlayWindow->GetWindow())
		{
			const float WindowPosX = static_cast<float>(ScreenPosition.Min.X);
			const float WindowPosY = static_cast<float>(ScreenPosition.Min.Y);
			const FVector2D Position(WindowPosX, WindowPosY);
			const float WindowWidth = static_cast<float>(ScreenPosition.Width());
			const float WindowHeight = static_cast<float>(ScreenPosition.Height());
			const FVector2D Size(WindowWidth, 1);

			// Don't move and resize window after AddWindowAsNativeChild and before SetWindowMode. If do so, it will make window Vsync ON.
			FSRDisplayWindow::GetPlayWindow()->GetWindow()->MoveWindowTo(Position);
			FSRDisplayWindow::GetPlayWindow()->GetWindow()->Resize(Size);

			FSlateApplication::Get().AddWindowAsNativeChild(
				FSRDisplayWindow::GetPlayWindow()->GetWindow().ToSharedRef(), GEngine->GameViewport->GetWindow().ToSharedRef());
			FSRDisplayWindow::GetPlayWindow()->GetWindow()->SetWindowMode(EWindowMode::WindowedFullscreen);
		}

		if (GEngine)
		{
			UGameUserSettings* UserSettings = GEngine->GetGameUserSettings();
			UserSettings->SetFullscreenMode(EWindowMode::Windowed);
			UserSettings->ApplySettings(false);
			UserSettings->SetFullscreenMode(EWindowMode::WindowedFullscreen);
			UserSettings->ApplySettings(false);
		}
	}

	void FSRDisplayWindow::Finalize()
	{
		if (FSRDisplayWindow::PlayWindow.IsValid())
		{
			if (FSRDisplayWindow::PlayWindow->PlaySceneViewport.IsValid())
			{
				FSRDisplayWindow::PlayWindow->PlaySceneViewport->Shutdown();
				FSRDisplayWindow::PlayWindow->PlaySceneViewport.Reset();
				FSRDisplayWindow::PlayWindow->PlaySceneViewport = nullptr;
			}

			FSRDisplayWindow::PlayWindow.Reset();
			FSRDisplayWindow::PlayWindow = nullptr;

			if (GEngine && GEngine->GameViewport)
			{
				TSharedPtr<SWindow> EngineWindow = GEngine->GameViewport->GetWindow();
				EngineWindow->RequestDestroyWindow();
			}
		}
	}

	FSRDisplayWindow* FSRDisplayWindow::GetPlayWindow()
	{
		if (PlayWindow.IsValid())
		{
			return PlayWindow.Get();
		}
		return nullptr;
	}

	void FSRDisplayWindow::OnWindowClosed(const TSharedRef<SWindow>& InWindow)
	{
		FSRDisplayWindow::Finalize();
	}

	bool FSRDisplayWindow::GetMousePosition(FVector2D& MousePosition) const
	{
		if (PlaySceneViewport.IsValid())
		{
			return PlaySceneViewport->GetMousePosition(MousePosition);
		}
		return false;
	}

	FViewport* FSRDisplayWindow::GetViewport()
	{
		if (PlaySceneViewport.IsValid())
		{
			return PlaySceneViewport->GetViewport();
		}
		return nullptr;
	}

} // namespace sr_display
