#include "proof_mode.h"

#include "editor.h"

#include <engine/graphics.h>

#include <game/client/components/menu_background.h>

void CProofMode::CState::Reset()
{
	m_ProofBorders = EProofBorder::OFF;
	m_CurrentMenuProofIndex = 0;
	m_vMenuBackgroundPositions.clear();
	m_vvMenuBackgroundCollisions.clear();
}

void CProofMode::OnInit(CEditor *pEditor)
{
	CEditorComponent::OnInit(pEditor);
	InitMenuBackgroundPositionNames();
}

void CProofMode::OnMapLoad()
{
	InitMenuBackgroundPositions();
}

void CProofMode::InitMenuBackgroundPositionNames()
{
	m_vpMenuBackgroundPositionNames.resize(CMenuBackground::NUM_POS);
	m_vpMenuBackgroundPositionNames[CMenuBackground::POS_START] = "start";
	m_vpMenuBackgroundPositionNames[CMenuBackground::POS_BROWSER_INTERNET] = "browser(internet)";
	m_vpMenuBackgroundPositionNames[CMenuBackground::POS_BROWSER_LAN] = "browser(lan)";
	m_vpMenuBackgroundPositionNames[CMenuBackground::POS_DEMOS] = "demos";
	m_vpMenuBackgroundPositionNames[CMenuBackground::POS_NEWS] = "news";
	m_vpMenuBackgroundPositionNames[CMenuBackground::POS_BROWSER_FAVORITES] = "favorites";
	m_vpMenuBackgroundPositionNames[CMenuBackground::POS_SETTINGS_LANGUAGE] = "settings(language)";
	m_vpMenuBackgroundPositionNames[CMenuBackground::POS_SETTINGS_GENERAL] = "settings(general)";
	m_vpMenuBackgroundPositionNames[CMenuBackground::POS_SETTINGS_PLAYER] = "settings(player)";
	m_vpMenuBackgroundPositionNames[CMenuBackground::POS_SETTINGS_TEE] = "settings(tee)";
	m_vpMenuBackgroundPositionNames[CMenuBackground::POS_SETTINGS_APPEARANCE] = "settings(appearance)";
	m_vpMenuBackgroundPositionNames[CMenuBackground::POS_SETTINGS_CONTROLS] = "settings(controls)";
	m_vpMenuBackgroundPositionNames[CMenuBackground::POS_SETTINGS_GRAPHICS] = "settings(graphics)";
	m_vpMenuBackgroundPositionNames[CMenuBackground::POS_SETTINGS_SOUND] = "settings(sound)";
	m_vpMenuBackgroundPositionNames[CMenuBackground::POS_SETTINGS_DDNET] = "settings(ddnet)";
	m_vpMenuBackgroundPositionNames[CMenuBackground::POS_SETTINGS_ASSETS] = "settings(assets)";
	m_vpMenuBackgroundPositionNames[CMenuBackground::POS_SETTINGS_CREDITS] = "settings(credits)";
	m_vpMenuBackgroundPositionNames[CMenuBackground::POS_BROWSER_CUSTOM0] = "custom(1)";
	m_vpMenuBackgroundPositionNames[CMenuBackground::POS_BROWSER_CUSTOM1] = "custom(2)";
	m_vpMenuBackgroundPositionNames[CMenuBackground::POS_BROWSER_CUSTOM2] = "custom(3)";
	m_vpMenuBackgroundPositionNames[CMenuBackground::POS_BROWSER_CUSTOM3] = "custom(4)";
	m_vpMenuBackgroundPositionNames[CMenuBackground::POS_BROWSER_CUSTOM4] = "custom(5)";
	m_vpMenuBackgroundPositionNames[CMenuBackground::POS_SETTINGS_RESERVED0] = "reserved settings(1)";
	m_vpMenuBackgroundPositionNames[CMenuBackground::POS_RESERVED0] = "reserved(1)";
	m_vpMenuBackgroundPositionNames[CMenuBackground::POS_RESERVED1] = "reserved(2)";
	m_vpMenuBackgroundPositionNames[CMenuBackground::POS_RESERVED2] = "reserved(3)";
}

void CProofMode::InitMenuBackgroundPositions()
{
	CState &State = Map()->m_ProofModeState;
	std::array<vec2, CMenuBackground::NUM_POS> aBackgroundPositions = GenerateMenuBackgroundPositions();
	State.m_vMenuBackgroundPositions.assign(aBackgroundPositions.begin(), aBackgroundPositions.end());

	if(Map()->m_pGameLayer)
	{
		for(int y = 0; y < Map()->m_pGameLayer->m_Height; ++y)
		{
			for(int x = 0; x < Map()->m_pGameLayer->m_Width; ++x)
			{
				CTile Tile = Map()->m_pGameLayer->GetTile(x, y);
				if(Tile.m_Index >= TILE_TIME_CHECKPOINT_FIRST && Tile.m_Index <= TILE_TIME_CHECKPOINT_LAST)
				{
					int ArrayIndex = std::clamp<int>((Tile.m_Index - TILE_TIME_CHECKPOINT_FIRST), 0, CMenuBackground::NUM_POS);
					State.m_vMenuBackgroundPositions[ArrayIndex] = vec2(x * 32.0f + 16.0f, y * 32.0f + 16.0f);
				}

				x += Tile.m_Skip;
			}
		}
	}

	State.m_vvMenuBackgroundCollisions.clear();
	State.m_vvMenuBackgroundCollisions.resize(State.m_vMenuBackgroundPositions.size());
	for(size_t i = 0; i < State.m_vMenuBackgroundPositions.size(); i++)
	{
		for(size_t j = i + 1; j < State.m_vMenuBackgroundPositions.size(); j++)
		{
			if(i != j && distance(State.m_vMenuBackgroundPositions[i], State.m_vMenuBackgroundPositions[j]) < 0.001f)
				State.m_vvMenuBackgroundCollisions.at(i).push_back(j);
		}
	}
}

void CProofMode::RenderScreenSizes()
{
	const vec2 WorldOffset = Editor()->MapView()->GetWorldOffset();

	// render screen sizes
	if(IsEnabled())
	{
		std::shared_ptr<CLayerGroup> pGameGroup = Map()->m_pGameGroup;
		pGameGroup->MapScreen();

		Graphics()->TextureClear();
		Graphics()->LinesBegin();

		// possible screen sizes (white border)
		CScreenRect LastRect(0, 0, 0, 0);
		float Start = 1.0f; // 9.0f/16.0f;
		float End = 16.0f / 9.0f;
		const int NumSteps = 20;
		for(int i = 0; i <= NumSteps; i++)
		{
			float Aspect = Start + (End - Start) * (i / (float)NumSteps);

			float Zoom = IsModeMenu() ? 0.7f : 1.0f;
			CScreenRect ScreenRect = Graphics()->MapScreenToWorld(
				WorldOffset.x, WorldOffset.y,
				100.0f, 100.0f, 100.0f, 0.0f, 0.0f, Aspect, Zoom);

			if(i == 0)
			{
				IGraphics::CLineItem aArray[] = {
					IGraphics::CLineItem(ScreenRect.m_TopLeft.x, ScreenRect.m_TopLeft.y, ScreenRect.m_BottomRight.x, ScreenRect.m_TopLeft.y),
					IGraphics::CLineItem(ScreenRect.m_TopLeft.x, ScreenRect.m_BottomRight.y, ScreenRect.m_BottomRight.x, ScreenRect.m_BottomRight.y)};
				Graphics()->LinesDraw(aArray, std::size(aArray));
			}

			if(i != 0)
			{
				IGraphics::CLineItem aArray[] = {
					IGraphics::CLineItem(ScreenRect.m_TopLeft.x, ScreenRect.m_TopLeft.y, LastRect.m_TopLeft.x, LastRect.m_TopLeft.y),
					IGraphics::CLineItem(ScreenRect.m_BottomRight.x, ScreenRect.m_TopLeft.y, LastRect.m_BottomRight.x, LastRect.m_TopLeft.y),
					IGraphics::CLineItem(ScreenRect.m_TopLeft.x, ScreenRect.m_BottomRight.y, LastRect.m_TopLeft.x, LastRect.m_BottomRight.y),
					IGraphics::CLineItem(ScreenRect.m_BottomRight.x, ScreenRect.m_BottomRight.y, LastRect.m_BottomRight.x, LastRect.m_BottomRight.y)};
				Graphics()->LinesDraw(aArray, std::size(aArray));
			}

			if(i == NumSteps)
			{
				IGraphics::CLineItem aArray[] = {
					IGraphics::CLineItem(ScreenRect.m_TopLeft.x, ScreenRect.m_TopLeft.y, ScreenRect.m_TopLeft.x, ScreenRect.m_BottomRight.y),
					IGraphics::CLineItem(ScreenRect.m_BottomRight.x, ScreenRect.m_TopLeft.y, ScreenRect.m_BottomRight.x, ScreenRect.m_BottomRight.y)};
				Graphics()->LinesDraw(aArray, std::size(aArray));
			}

			LastRect = ScreenRect;
		}
		Graphics()->LinesEnd();

		// two screen sizes (green and red border)
		{
			Graphics()->SetColor(1, 0, 0, 1);
			for(int Pass = 0; Pass < 2; Pass++)
			{
				const float aAspects[] = {4.0f / 3.0f, 16.0f / 10.0f, 5.0f / 4.0f, 16.0f / 9.0f};
				const ColorRGBA aColors[] = {ColorRGBA(1.0f, 0.0f, 0.0f, 1.0f), ColorRGBA(0.0f, 1.0f, 0.0f, 1.0f)};
				float Zoom = IsModeMenu() ? 0.7f : 1.0f;
				CScreenRect ScreenRect = Graphics()->MapScreenToWorld(
					WorldOffset.x, WorldOffset.y,
					100.0f, 100.0f, 100.0f, 0.0f, 0.0f, aAspects[Pass], Zoom);

				CUIRect Rect;
				Rect.x = ScreenRect.m_TopLeft.x;
				Rect.y = ScreenRect.m_TopLeft.y;
				Rect.w = ScreenRect.Width();
				Rect.h = ScreenRect.Height();
				Rect.DrawOutline(aColors[Pass]);
			}
		}

		// tee position (blue circle) and other screen positions
		{
			Graphics()->TextureClear();
			Graphics()->QuadsBegin();
			Graphics()->SetColor(0, 0, 1, 0.3f);
			Graphics()->DrawCircle(WorldOffset.x, WorldOffset.y - 3.0f, 20.0f, 32);

			if(IsModeMenu())
			{
				Graphics()->SetColor(0, 1, 0, 0.3f);

				const std::vector<vec2> &Positions = MenuBackgroundPositions();
				std::set<int> Indices;
				for(int i = 0; i < (int)Positions.size(); i++)
					Indices.insert(i);

				while(!Indices.empty())
				{
					int i = *Indices.begin();
					Indices.erase(i);
					for(int k : MenuBackgroundCollisions(i))
						Indices.erase(k);

					vec2 Pos = Positions[i];
					Pos += WorldOffset - Positions[CurrentMenuProofIndex()];

					if(Pos == WorldOffset)
						continue;

					Graphics()->DrawCircle(Pos.x, Pos.y - 3.0f, 20.0f, 32);
				}
			}

			Graphics()->QuadsEnd();
		}
	}
}

bool CProofMode::IsEnabled() const
{
	return Map()->m_ProofModeState.m_ProofBorders != EProofBorder::OFF;
}

bool CProofMode::IsModeMenu() const
{
	return Map()->m_ProofModeState.m_ProofBorders == EProofBorder::MENU;
}

bool CProofMode::IsModeIngame() const
{
	return Map()->m_ProofModeState.m_ProofBorders == EProofBorder::INGAME;
}

void CProofMode::Toggle()
{
	Map()->m_ProofModeState.m_ProofBorders = Map()->m_ProofModeState.m_ProofBorders == EProofBorder::OFF ? EProofBorder::INGAME : EProofBorder::OFF;
}

void CProofMode::SetModeIngame()
{
	Map()->m_ProofModeState.m_ProofBorders = EProofBorder::INGAME;
}

void CProofMode::SetModeMenu()
{
	Map()->m_ProofModeState.m_ProofBorders = EProofBorder::MENU;
}

int CProofMode::CurrentMenuProofIndex() const
{
	return Map()->m_ProofModeState.m_CurrentMenuProofIndex;
}

void CProofMode::SetCurrentMenuProofIndex(int MenuProofIndex)
{
	Map()->m_ProofModeState.m_CurrentMenuProofIndex = MenuProofIndex;
}

const std::vector<vec2> &CProofMode::MenuBackgroundPositions() const
{
	return Map()->m_ProofModeState.m_vMenuBackgroundPositions;
}

vec2 CProofMode::CurrentMenuBackgroundPosition() const
{
	return Map()->m_ProofModeState.m_vMenuBackgroundPositions[CurrentMenuProofIndex()];
}

const char *CProofMode::MenuBackgroundPositionName(int MenuProofIndex) const
{
	return m_vpMenuBackgroundPositionNames[MenuProofIndex];
}

const std::vector<int> &CProofMode::MenuBackgroundCollisions(int MenuProofIndex) const
{
	return Map()->m_ProofModeState.m_vvMenuBackgroundCollisions[MenuProofIndex];
}
