#undef UpdateResource

#include "OptiXCameraActor.h"

#include "OptiXModule.h"
#include "OptiXTextureSampler.h"
#include "OptiXLaserActor.h"

#include "Engine/EngineTypes.h"
#include <EngineGlobals.h>
#include "Runtime/Engine/Classes/Camera/PlayerCameraManager.h"
#include "Runtime/Engine/Classes/Kismet/GameplayStatics.h"
#include "Runtime/Engine/Classes/Engine/LocalPlayer.h"
#include "Runtime/Engine/Public/SceneView.h"
#include <Runtime/Engine/Classes/Engine/Engine.h>
#include "Runtime/Engine/Public/HighResScreenshot.h"
#include "Runtime/Engine/Classes/Engine/TextureRenderTarget2D.h"
#include "Runtime/Engine/Classes/Engine/TextureRenderTargetCube.h"
#include "Runtime/Engine/Classes/Materials/MaterialInstanceDynamic.h"
#include "Runtime/Engine/Classes/Engine/Light.h"
#include "Runtime/Engine/Classes/GameFramework/GameUserSettings.h"
#include "Runtime/Engine/Classes/GameFramework/PlayerStart.h"

// VR
#include "Runtime/HeadMountedDisplay/Public/IHeadMountedDisplay.h"
#include "Runtime/HeadMountedDisplay/Public/IXRTrackingSystem.h"
#include "Runtime/HeadMountedDisplay/Public/IXRCamera.h"

#include "Runtime/Engine/Classes/Camera/CameraActor.h"


DEFINE_LOG_CATEGORY(OptiXPluginCameraActor);


UOptiXBlendable::UOptiXBlendable(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
	BlendWeight = 1.0f;
}

void UOptiXBlendable::OverrideBlendableSettings(FSceneView & View, float InWeight) const
{
	float Weight = FMath::Clamp(BlendWeight, 0.0f, 1.0f) * InWeight;
}

AOptiXPlayerCameraManager::AOptiXPlayerCameraManager(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
	UE_LOG(OptiXPluginCameraActor, Display, TEXT("Optix Camera Manager Constructor!"));

	SceneCaptureComponent = ObjectInitializer.CreateDefaultSubobject<USceneCaptureComponent2D>(this, TEXT("SceneCaptureComponent0"));
	RootComponent = SceneCaptureComponent;
	SceneCaptureComponent->bCaptureEveryFrame = false;
	SceneCaptureComponent->bCaptureOnMovement = false;
	//SceneCaptureComponent->bEnableClipPlane = true; // don't want ourself in the capture
	SceneCaptureComponent->FOVAngle = 90.0f;
	SceneCaptureComponent->HideActorComponents(this);
	//SceneCaptureComponent->CaptureSource = SCS_SceneColorHDR;
	//SceneCaptureComponent->HideActorComponents(UGameplayStatics::GetPlayerController(GetWorld(), 0));
	//SceneCaptureComponent->HideActorComponents(UGameplayStatics::GetPlayerPawn(GetWorld(), 0));

	SceneCaptureCubeComponent = ObjectInitializer.CreateDefaultSubobject<USceneCaptureComponentCube>(this, TEXT("SceneCaptureComponentCube0"));
	SceneCaptureCubeComponent->SetupAttachment(SceneCaptureComponent);
	SceneCaptureCubeComponent->bCaptureEveryFrame = false;
	SceneCaptureCubeComponent->bCaptureOnMovement = false;
	SceneCaptureCubeComponent->HideActorComponents(this);


	PostProcessComponent = ObjectInitializer.CreateDefaultSubobject<UPostProcessComponent>(this, TEXT("PostProcessComponent0"));
	PostProcessComponent->SetupAttachment(SceneCaptureCubeComponent); // Doesn't matter anyway
	PostProcessComponent->bUnbound = true; // Should be default anyway

	PrimaryActorTick.bCanEverTick = true;
	//PrimaryActorTick.TickGroup = TG_PostUpdateWork;


	// TODO DEBUG CREATE IN C++
	//static ConstructorHelpers::FObjectFinder<UTextureRenderTargetCube> RT(TEXT("TextureRenderTarget2D'/OptiX/PPMaterials/RTCube.RTCube'"));

	//if (RT.Object != NULL)
	//{
	//	CubeRenderTarget = RT.Object;
	//	SceneCaptureCubeComponent->TextureTarget = CubeRenderTarget;

	//}


	CubeRenderTarget = NewObject<UTextureRenderTargetCube>();
	CubeRenderTarget->Init(1024, PF_B8G8R8A8);
	CubeRenderTarget->UpdateResource();
	SceneCaptureCubeComponent->TextureTarget = CubeRenderTarget;

	//FRotator Rotation = FRotationMatrix::MakeFromX(Directions[i]).Rotator();
	//FQuat Q = FQuat({ 1, 0, 0 });
	//SceneCaptureComponent->AddWorldRotation();

	for (uint8 i = 0; i < 6; i++)
	{
		UTextureRenderTarget2D* RenderTarget = NewObject<UTextureRenderTarget2D>();
		//RenderTarget->AddToRoot();
		RenderTarget->InitCustomFormat(CubemapSize, CubemapSize, PF_B8G8R8A8, true);
		//RenderTarget->CompressionSettings = TextureCompressionSettings::TC_VectorDisplacementmap;
		//RenderTarget->SRGB = 0;
		//RenderTarget->TargetGamma = 2.2;
		RenderTarget->NeverStream = 1;
		//RenderTarget->InitAutoFormat(CubemapSize, CubemapSize);
		RenderTarget->UpdateResourceImmediate(); //???
		//RenderTarget->AdjustBrightness = 1.0; // ??? TODO
		CubemapRenderTargets.Add(RenderTarget);
	}

}

void AOptiXPlayerCameraManager::BeginPlay()
{
	
	//UGameplayStatics::GetPlayerController(GetWorld(), 0)->SetViewTarget(this);
	UE_LOG(OptiXPluginCameraActor, Display, TEXT("OptiX Camera Manager Begin Play!"));
	Super::BeginPlay();

	Init();
}

void AOptiXPlayerCameraManager::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
	// Clean up again TODO
	Super::EndPlay(EndPlayReason);
}

void AOptiXPlayerCameraManager::Tick(float DeltaSeconds)
{
	Super::Tick(DeltaSeconds);
	if (C == 2)
	{
		CaptureCubemap();
		bCubemapCaptured.AtomicSet(true);
		//InitCubemap();
		SetActorTickEnabled(false);
		return; // no need to increase C further
	}
	C++;
}


void AOptiXPlayerCameraManager::Init()
{
	PostProcessComponent->AddOrUpdateBlendable(FOptiXModule::Get().GetOptiXContextManager()->GetOptiXMID(), 1.0f);
	FOptiXModule::Get().GetOptiXContextManager()->SetActiveCameraActor(this);
}



void AOptiXPlayerCameraManager::CaptureCubemap()
{

	// 2D
	TArray<FVector> Directions =
	{
		{1, 0, 0},
		{-1, 0, 0},
		{0, 1, 0},
		{0, -1, 0},
		{0, 0, -1},
		{0, 0, 1}
	};

	SceneCaptureComponent->HideActorComponents(UGameplayStatics::GetPlayerController(GetWorld(), 0));
	SceneCaptureComponent->HideActorComponents(UGameplayStatics::GetPlayerPawn(GetWorld(), 0));
	
	TArray<AActor*> FoundLaserActors;
	UGameplayStatics::GetAllActorsOfClass(GetWorld(), AOptiXLaserActor::StaticClass(), FoundLaserActors);

	for (AActor* Actor : FoundLaserActors)
	{
		SceneCaptureComponent->HideComponent(Cast<AOptiXLaserActor>(Actor)->LineInstancedStaticMeshComponent);
		SceneCaptureCubeComponent->HideComponent(Cast<AOptiXLaserActor>(Actor)->LineInstancedStaticMeshComponent);
	}

	// the actual camera position is still 0 here, so the capture would be at the origin and not the actual player start.
	// Let's move the capture "into" the first lens actor. 

	TArray<AActor*> FoundPlayerStarts;
	UGameplayStatics::GetAllActorsOfClass(GetWorld(), APlayerStart::StaticClass(), FoundPlayerStarts);


	FVector Loc = FoundPlayerStarts[0]->GetActorLocation(); // Just use the first one

	// CUBE

	SceneCaptureCubeComponent->HideActorComponents(UGameplayStatics::GetPlayerController(GetWorld(), 0));
	SceneCaptureCubeComponent->HideActorComponents(UGameplayStatics::GetPlayerPawn(GetWorld(), 0));
	//SceneCaptureCubeComponent->SetWorldRotation(FQuat(FVector(0, 1, 0), 180));
	SceneCaptureCubeComponent->SetWorldLocation(Loc);

	SceneCaptureCubeComponent->CaptureScene();
	

}