// Oleg Petruny staff
#include "VideoSettings.h"
#include "Engine.h"
#include "Engine/Engine.h"
#include "Engine/RendererSettings.h"
#include <Runtime\Engine\Public\UnrealEngine.h>
#include "GameFramework/GameUserSettings.h"
#include "GeneralEngineSettings.h"
#include "RHI.h"
#include "Math/Range.h"


//UE_LOG(LogTemp, Warning, TEXT("Some warning message") );
//GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor::Yellow, TEXT("Some debug message!"));
void UVideoSettings::GetPrimaryMonitorId(int &monitorId) {
	TArray<FMonitorInformation> Monitors;
	UVideoSettings::GetAvailableMonitors(Monitors);
	for (int i = 0; i < Monitors.Num(); i++) {
		if (Monitors[i].IsPrimary) {
			monitorId = i;
			break;
		}
	}
}
void UVideoSettings::GetCurrentMonitorId(int &monitorId) {
	TArray<FMonitorInformation> Monitors;
	UVideoSettings::GetAvailableMonitors(Monitors);
	int32 CurrentWidth = GEngine->GameViewport->GetWindow()->GetPositionInScreen().X;
	for (int i = 0; i < Monitors.Num(); i++) {
		if (CurrentWidth < Monitors[i].NativeWidth) {
			monitorId = i;
			break;
		} else {
			CurrentWidth -= Monitors[i].NativeWidth;
		}
	}
}
void UVideoSettings::GetAvailableMonitors(TArray<FMonitorInformation> &monitors) {
	FDisplayMetrics FDM;
	FDisplayMetrics::RebuildDisplayMetrics(FDM);
	TArray<FMonitorInformation> buffer;
	for (const FMonitorInfo &i : FDM.MonitorInfo) {
		buffer.Add(FMonitorInformation(i.Name, i.ID, i.NativeWidth, i.NativeHeight, 
			FPlatformRectangle(i.DisplayRect.Left, i.DisplayRect.Top, i.DisplayRect.Right, i.DisplayRect.Bottom), 
			FPlatformRectangle(i.WorkArea.Left, i.WorkArea.Top, i.WorkArea.Right, i.WorkArea.Bottom),
			i.bIsPrimary, i.DPI));
	}
	monitors = buffer;
}
void UVideoSettings::SetMonitor(int monitorId) {
	int CurrentMonitorID = 0;
	UVideoSettings::GetCurrentMonitorId(CurrentMonitorID);
	if (monitorId != CurrentMonitorID) {
		TArray<FMonitorInformation> Monitors;
		UVideoSettings::GetAvailableMonitors(Monitors);
		if (monitorId < Monitors.Num()) {
			float anotherMonitor = 0.0f;
			while (CurrentMonitorID < monitorId) {
				anotherMonitor += Monitors[CurrentMonitorID].NativeWidth;
				CurrentMonitorID++;
			}
			while (CurrentMonitorID - monitorId < monitorId - 1) {
				anotherMonitor += Monitors[CurrentMonitorID].NativeWidth;
				CurrentMonitorID++;
			}
			FIntPoint currentResolution = GEngine->GameViewport->Viewport->GetSizeXY();
			FIntPoint nativeResolution = FIntPoint { Monitors[monitorId].NativeWidth, Monitors[monitorId].NativeHeight };
			GEngine->GameViewport->GetWindow()->MoveWindowTo(FVector2D { ((float)((nativeResolution.X - currentResolution.X) / 2) + anotherMonitor), (float)((nativeResolution.Y - currentResolution.Y) / 2) });
		}
	}
}

void UVideoSettings::IntPointToString(FIntPoint in, FString &out) {
	out = FString::FromInt(in.X) + "x" + FString::FromInt(in.Y);
}
void UVideoSettings::StringToIntPoint(FString in, FIntPoint &out) {
	const FRegexPattern rgxP(FRegexPattern(TEXT("[0-9]+")));
	FRegexMatcher rgx = FRegexMatcher(rgxP, in);
	if (rgx.FindNext()) {
		out.X = FCString::Atoi(*in.Mid(rgx.GetMatchBeginning(), rgx.GetMatchEnding() - rgx.GetMatchBeginning()));
	}
	if (rgx.FindNext()) {
		out.Y = FCString::Atoi(*in.Mid(rgx.GetMatchBeginning(), rgx.GetMatchEnding() - rgx.GetMatchBeginning()));
	}
}
void UVideoSettings::StringAnyToInt(FString in, int32 &out) {
	const FRegexPattern rgxP(FRegexPattern(TEXT("[0-9]+")));
	FRegexMatcher rgx = FRegexMatcher(rgxP, in);
	if (rgx.FindNext()) {
		out = FCString::Atoi(*in.Mid(rgx.GetMatchBeginning(), rgx.GetMatchEnding() - rgx.GetMatchBeginning()));
	}
}
void UVideoSettings::GetNativeResolutionByMonitorId(int monitorId, FIntPoint &nativeResolution) {
	TArray<FMonitorInformation> Monitors;
	UVideoSettings::GetAvailableMonitors(Monitors);
	nativeResolution = FIntPoint { Monitors[monitorId].NativeWidth, Monitors[monitorId].NativeHeight };
}
void UVideoSettings::GetAllAvailableResolutionsAndRefreshRates(TArray<FResolutionAndRefreshRate> &availableResolutionsAndRefreshRates) {
	FScreenResolutionArray ResolutionsAndRefreshRates;
	TArray<FResolutionAndRefreshRate> buffer;
	if (RHIGetAvailableResolutions(ResolutionsAndRefreshRates, false)) {
		for (const FScreenResolutionRHI &i : ResolutionsAndRefreshRates) {
			buffer.Add(FResolutionAndRefreshRate(i.Width, i.Height, i.RefreshRate));
		}
	}
	availableResolutionsAndRefreshRates = buffer;
}
void UVideoSettings::GetAvailableResolutionsByMonitorId(int monitorId, TArray<FIntPoint> &availableResolutions) {
	TArray<FResolutionAndRefreshRate> ResolutionsAndRates;
	UVideoSettings::GetAllAvailableResolutionsAndRefreshRates(ResolutionsAndRates);
	TArray<FIntPoint> buffer;
	FIntPoint NativeResolution;
	UVideoSettings::GetNativeResolutionByMonitorId(monitorId, NativeResolution);
	for (FResolutionAndRefreshRate i : ResolutionsAndRates) {
		if (i.Width <= NativeResolution.X && i.Height <= NativeResolution.Y)
			if (!buffer.Contains(FIntPoint { i.Width, i.Height }))
				buffer.Add(FIntPoint { i.Width, i.Height });
	}
	availableResolutions = buffer;
}
void UVideoSettings::FilterResolutionsViaAspectRatio(TArray<FIntPoint> resolutionsIn, FIntPoint aspectRatio, TArray<FIntPoint> &resolutionsOut) {
	TArray <FIntPoint> buffer;
	if (aspectRatio.X != 0 && aspectRatio.Y != 0) {
		for (FIntPoint i : resolutionsIn) {
			if (i.X / aspectRatio.X == i.Y / aspectRatio.Y) {
				buffer.Add(i);
			}
		}
	}
	resolutionsOut = buffer;
}
void UVideoSettings::GetCurrentResolution(FIntPoint &currentResolution) {
	currentResolution = GEngine->GameViewport->Viewport->GetSizeXY();
}
EDisplayMode ConvertModeWindowToDisplay(EWindowMode::Type mode) {
	switch (mode) {
		case EWindowMode::Type::Fullscreen:
		{
			return EDisplayMode::Fullscreen;
		}
		case EWindowMode::Type::WindowedFullscreen:
		{
			return EDisplayMode::Borderless;
		}
		case EWindowMode::Type::Windowed:
		{
			return EDisplayMode::Windowed;
		}
		default:
			return EDisplayMode::Windowed;
	}
}
EWindowMode::Type ConvertModeDisplayToWindow(EDisplayMode mode) {
	switch (mode) {
		case EDisplayMode::Fullscreen:
		{
			return EWindowMode::Type::Fullscreen;
		}
		case EDisplayMode::Borderless:
		{
			return EWindowMode::Type::WindowedFullscreen;
		}
		case EDisplayMode::Windowed:
		{
			return EWindowMode::Type::Windowed;
		}
		default:
			return EWindowMode::Type::Windowed;
	}
}
void UVideoSettings::SetResolution(FIntPoint resolution) {
	//GEngine->GameViewport->Viewport->SetInitialSize(resolution);
	UGameUserSettings *settings = GEngine->GetGameUserSettings();
	settings->bUseDynamicResolution = true;
	//settings->RequestResolutionChange(resolution.X, resolution.Y, ConvertModeDisplayToWindow(mode), false);
	settings->SetScreenResolution(resolution);
}
void UVideoSettings::SetResolutionScale(float resolutionScale) {
	UGameUserSettings *settings = GEngine->GetGameUserSettings();
	settings->SetResolutionScaleValueEx(resolutionScale);
}

void UVideoSettings::GetAvailableRefreshRates(FIntPoint resolution, TArray<int32> &availableRefreshRates) {
	TArray<FResolutionAndRefreshRate> ResolutionsAndRates;
	UVideoSettings::GetAllAvailableResolutionsAndRefreshRates(ResolutionsAndRates);
	if (!resolution.Size())
		UVideoSettings::GetCurrentResolution(resolution);
	TArray<int32> rates;
	for (FResolutionAndRefreshRate i : ResolutionsAndRates) {
		if (i.Width == resolution.X && i.Height == resolution.Y)
			if (!rates.Contains(i.RefreshRate))
				rates.Add(i.RefreshRate);
	}
	availableRefreshRates = rates;
}
void UVideoSettings::GetCurrentRefreshRate(int32 &currentRefreshRate) {
	UEngine *engine = GetMutableDefault<UEngine>();
	currentRefreshRate = engine->MinDesiredFrameRate;
}
void UVideoSettings::SetRefreshRate(int32 refreshRate) {
	UEngine *engine = GetMutableDefault<UEngine>();
	engine->MinDesiredFrameRate = refreshRate;
	engine->SmoothedFrameRateRange = FFloatRange { (float)(refreshRate - 10), (float)(refreshRate + 10) };
}

void UVideoSettings::AspectRatioParse(FIntPoint resolution, FIntPoint &aspectRatio) {
	if (resolution.X / 16 == resolution.Y / 9) {
		aspectRatio = FIntPoint { 16, 9 };
	} else if (resolution.X / 16 == resolution.Y / 10) {
		aspectRatio = FIntPoint { 16, 10 };
	} else if (resolution.X / 4 == resolution.Y / 3) {
		aspectRatio = FIntPoint { 4, 3 };
	} else if (resolution.X / 21 == resolution.Y / 9) {
		aspectRatio = FIntPoint { 21, 9 };
	} else if (resolution.X == resolution.Y) {
		aspectRatio = FIntPoint { 1, 1 };
	}
}
void UVideoSettings::GetNativeAspectRatioByMonitorId(int monitorId, FIntPoint &nativeAspectRatio) {
	FIntPoint res;
	UVideoSettings::GetNativeResolutionByMonitorId(monitorId, res);
	UVideoSettings::AspectRatioParse(res, nativeAspectRatio);
}
void UVideoSettings::GetAvailableAspectRatiousByMonitorId(int monitorId, TArray<FIntPoint> &availableAspectRatious) {
	TArray<FIntPoint> Resolutions;
	UVideoSettings::GetAvailableResolutionsByMonitorId(monitorId, Resolutions);
	TArray<FIntPoint> buffer;
	FIntPoint x;
	for (FIntPoint i : Resolutions) {
		UVideoSettings::AspectRatioParse(i, x);
		if (!buffer.Contains(x)) {
			buffer.Add(x);
		}
	}
	availableAspectRatious = buffer;
}
void UVideoSettings::GetCurrentAspectRatio(FIntPoint &currentAspectRatio) {
	FIntPoint res;
	UVideoSettings::GetCurrentResolution(res);
	UVideoSettings::AspectRatioParse(res, currentAspectRatio);
}
void UVideoSettings::SetAspectRatio(FIntPoint aspectRatio) {
	//NoWay!
}

void UVideoSettings::StringToDisplayMode(FString in, EDisplayMode &mode) {
	if (in.Contains("Fullscreen")) {
		mode = EDisplayMode::Fullscreen;
	} else if (in.Contains("Borderless")) {
		mode = EDisplayMode::Borderless;
	} else {
		mode = EDisplayMode::Windowed;
	}
}
void UVideoSettings::DisplayModeToString(EDisplayMode mode, FString &out) {
	switch (mode) {
		case EDisplayMode::Fullscreen:
		{
			out = "Fullscreen";
			break;
		}
		case EDisplayMode::Borderless:
		{
			out = "Borderless";
			break;
		}
		default:
		{
			out = "Windowed";
			break;
		}
	}
}
void UVideoSettings::IntToDisplayMode(int in, EDisplayMode &mode) {
	switch (in) {
		case 0:
		{
			mode = EDisplayMode::Fullscreen;
			break;
		}
		case 1:
		{
			mode = EDisplayMode::Borderless;
			break;
		}
		default:
		{
			mode = EDisplayMode::Windowed;
			break;
		}
	}
}
void UVideoSettings::DisplayModeToInt(EDisplayMode mode, int &out) {
	switch (mode) {
		case EDisplayMode::Fullscreen:
		{
			out = 0;
			break;
		}
		case EDisplayMode::Borderless:
		{
			out = 1;
			break;
		}
		default:
		{
			out = 2;
			break;
		}
	}
}
void UVideoSettings::GetCurrentDisplayMode(EDisplayMode &mode) {
	UGameUserSettings *settings = GEngine->GetGameUserSettings();
	EWindowMode::Type buffer = settings->GetFullscreenMode();
	mode = ConvertModeWindowToDisplay(buffer);
}
void UVideoSettings::SetDisplayMode(EDisplayMode mode) {
	UGameUserSettings *settings = GEngine->GetGameUserSettings();
	settings->SetFullscreenMode(ConvertModeDisplayToWindow(mode));
}

void UVideoSettings::WindowToCenter() {
	EDisplayMode currentMode;
	UVideoSettings::GetCurrentDisplayMode(currentMode);
	if (currentMode == EDisplayMode::Windowed) {
		float anotherMonitor = 0.0f;
		int MonitorID;
		UVideoSettings::GetCurrentMonitorId(MonitorID); 
		if (MonitorID > 0) {
			TArray<FMonitorInformation> Monitors;
			UVideoSettings::GetAvailableMonitors(Monitors);
			for (int i = 0; i < MonitorID; i++) {
				anotherMonitor += Monitors[i].NativeWidth;
			}
		}
		FIntPoint currentResolution;
		UVideoSettings::GetCurrentResolution(currentResolution);
		FIntPoint nativeResolution;
		int CurrentMonitor;
		UVideoSettings::GetCurrentMonitorId(CurrentMonitor);
		UVideoSettings::GetNativeResolutionByMonitorId(CurrentMonitor, nativeResolution);
		GEngine->GameViewport->GetWindow()->MoveWindowTo(FVector2D { ((float)((nativeResolution.X - currentResolution.X) / 2) + anotherMonitor), (float)((nativeResolution.Y - currentResolution.Y) / 2) });
	}
}

void UVideoSettings::ApplyVideoSettings() {
	UGameUserSettings *settings = GEngine->GetGameUserSettings();
	//settings->ApplyResolutionSettings(false);
	settings->ApplySettings(false);
	settings->SaveSettings();
	settings->LoadSettings();
	UEngine *engine = GetMutableDefault<UEngine>();
	engine->SaveConfig();
	engine->LoadConfig();
	
}
void UVideoSettings::GetDefaultVideoSettings(FIntPoint &resolution, EDisplayMode &mode) {
	UGameUserSettings *settings = GEngine->GetGameUserSettings();
	resolution = settings->GetDefaultResolution();
	mode = ConvertModeWindowToDisplay(settings->GetDefaultWindowMode());
}
void UVideoSettings::GetLastVideoSettings(FIntPoint &resolution, EDisplayMode &mode) {
	UGameUserSettings *settings = GEngine->GetGameUserSettings();
	resolution = settings->GetLastConfirmedScreenResolution();
	mode = ConvertModeWindowToDisplay(settings->GetLastConfirmedFullscreenMode());
}
void UVideoSettings::SetLastVideoSettings(FIntPoint resolution, EDisplayMode mode) {
	UGameUserSettings *settings = GEngine->GetGameUserSettings();
	settings->ConfirmVideoMode();
}

void UVideoSettings::GetGamma(float &gamma) {
	UEngine *engine = GetMutableDefault<UEngine>();
	gamma = engine->GetDisplayGamma();
}
void UVideoSettings::SetGamma(float gamma) {
	//0.5-5
	UEngine *engine = GetMutableDefault<UEngine>();
	engine->DisplayGamma = gamma;
	engine->SaveConfig();
	engine->ReloadConfig();
}
