#include "pch.h"
#include "DebugOverlay.h"

#include "maths.h"
#include "proj.h"
#include "winmain.h"
#include "TFlipperEdge.h"
#include "TFlipper.h"
#include "pb.h"
#include "TLine.h"
#include "TCircle.h"
#include "TPinballTable.h"
#include "TEdgeBox.h"
#include "TTableLayer.h"
#include "TBall.h"
#include "render.h"
#include "options.h"


gdrv_bitmap8* DebugOverlay::dbScreen = nullptr;

static int SDL_RenderDrawCircle(SDL_Renderer* renderer, int x, int y, int radius)
{
	SDL_Point points[256];
	int pointCount = 0;
	int offsetx, offsety, d;
	int status;

	offsetx = 0;
	offsety = radius;
	d = radius - 1;
	status = 0;

	while (offsety >= offsetx)
	{
		if (pointCount + 8 > 256)
		{
			status = SDL_RenderDrawPoints(renderer, points, pointCount);
			pointCount = 0;

			if (status < 0) {
				status = -1;
				break;
			}
		}

		points[pointCount++] = { x + offsetx, y + offsety };
		points[pointCount++] = { x + offsety, y + offsetx };
		points[pointCount++] = { x - offsetx, y + offsety };
		points[pointCount++] = { x - offsety, y + offsetx };
		points[pointCount++] = { x + offsetx, y - offsety };
		points[pointCount++] = { x + offsety, y - offsetx };
		points[pointCount++] = { x - offsetx, y - offsety };
		points[pointCount++] = { x - offsety, y - offsetx };

		if (d >= 2 * offsetx) {
			d -= 2 * offsetx + 1;
			offsetx += 1;
		}
		else if (d < 2 * (radius - offsety)) {
			d += 2 * offsety - 1;
			offsety -= 1;
		}
		else {
			d += 2 * (offsety - offsetx - 1);
			offsety -= 1;
			offsetx += 1;
		}
	}

	if (pointCount > 0)
		status = SDL_RenderDrawPoints(renderer, points, pointCount);

	return status;
}

void DebugOverlay::UnInit()
{
	delete dbScreen;
	dbScreen = nullptr;
}

void DebugOverlay::DrawOverlay()
{
	if (dbScreen == nullptr)
	{
		dbScreen = new gdrv_bitmap8(render::vscreen->Width, render::vscreen->Height, false, false);
		dbScreen->CreateTexture("nearest", SDL_TEXTUREACCESS_TARGET);
		SDL_SetTextureBlendMode(dbScreen->Texture, SDL_BLENDMODE_BLEND);
	}

	// Setup overlay rendering
	Uint8 initialR, initialG, initialB, initialA;
	auto initialRenderTarget = SDL_GetRenderTarget(winmain::Renderer);
	SDL_GetRenderDrawColor(winmain::Renderer, &initialR, &initialG, &initialB, &initialA);
	SDL_SetRenderTarget(winmain::Renderer, dbScreen->Texture);
	SDL_SetRenderDrawColor(winmain::Renderer, 0, 0, 0, 0);
	SDL_RenderClear(winmain::Renderer);

	// Draw EdgeManager box grid
	if (options::Options.DebugOverlayGrid)
		DrawBoxGrid();

	// Draw all edges registered in TCollisionComponent.EdgeList + flippers
	if (options::Options.DebugOverlayAllEdges)
		DrawAllEdges();

	// Draw ball collision
	if (options::Options.DebugOverlayBallPosition || options::Options.DebugOverlayBallEdges)
		DrawBallInfo();

	// Restore render target
	SDL_SetRenderTarget(winmain::Renderer, initialRenderTarget);
	SDL_SetRenderDrawColor(winmain::Renderer,
		initialR, initialG, initialB, initialA);

	// Copy overlay with alpha blending
	SDL_BlendMode blendMode;
	SDL_GetRenderDrawBlendMode(winmain::Renderer, &blendMode);
	SDL_SetRenderDrawBlendMode(winmain::Renderer, SDL_BLENDMODE_BLEND);
	SDL_RenderCopy(winmain::Renderer, dbScreen->Texture, nullptr, &render::DestinationRect);
	SDL_SetRenderDrawBlendMode(winmain::Renderer, blendMode);
}

void DebugOverlay::DrawBoxGrid()
{
	auto& edgeMan = *TTableLayer::edge_manager;

	SDL_SetRenderDrawColor(winmain::Renderer, 0, 255, 0, 255);
	for (int x = 0; x <= edgeMan.MaxBoxX; x++)
	{
		vector2 boxPt{ x * edgeMan.AdvanceX + edgeMan.X , edgeMan.Y };
		auto pt1 = proj::xform_to_2d(boxPt);
		boxPt.Y = edgeMan.MaxBoxY * edgeMan.AdvanceY + edgeMan.Y;
		auto pt2 = proj::xform_to_2d(boxPt);

		SDL_RenderDrawLine(winmain::Renderer, pt1.X, pt1.Y, pt2.X, pt2.Y);
	}
	for (int y = 0; y <= edgeMan.MaxBoxY; y++)
	{
		vector2 boxPt{ edgeMan.X, y * edgeMan.AdvanceY + edgeMan.Y };
		auto pt1 = proj::xform_to_2d(boxPt);
		boxPt.X = edgeMan.MaxBoxX * edgeMan.AdvanceX + edgeMan.X;
		auto pt2 = proj::xform_to_2d(boxPt);

		SDL_RenderDrawLine(winmain::Renderer, pt1.X, pt1.Y, pt2.X, pt2.Y);
	}
}

void DebugOverlay::DrawAllEdges()
{
	SDL_SetRenderDrawColor(winmain::Renderer, 0, 200, 200, 255);
	for (auto cmp : pb::MainTable->ComponentList)
	{
		auto collCmp = dynamic_cast<TCollisionComponent*>(cmp);
		if (collCmp)
		{
			for (auto edge : collCmp->EdgeList)
			{
				DrawEdge(edge);
			}
		}
		auto flip = dynamic_cast<TFlipper*>(cmp);
		if (flip)
		{
			DrawEdge(flip->FlipperEdge);
		}
	}
}

void DebugOverlay::DrawBallInfo()
{
	auto& edgeMan = *TTableLayer::edge_manager;
	for (auto ball : pb::MainTable->BallList)
	{
		if (ball->ActiveFlag)
		{
			vector2 ballPosition = { ball->Position.X, ball->Position.Y };

			if (options::Options.DebugOverlayBallEdges)
			{
				SDL_SetRenderDrawColor(winmain::Renderer, 255, 0, 0, 255);
				auto x = edgeMan.box_x(ballPosition.X), y = edgeMan.box_y(ballPosition.Y);
				auto& box = edgeMan.BoxArray[x + y * edgeMan.MaxBoxX];
				for (auto edge : box.EdgeList)
				{
					DrawEdge(edge);
				}
			}

			if (options::Options.DebugOverlayBallPosition)
			{
				SDL_SetRenderDrawColor(winmain::Renderer, 0, 0, 255, 255);

				auto pt1 = proj::xform_to_2d(ballPosition);
				SDL_RenderDrawCircle(winmain::Renderer, pt1.X, pt1.Y, 10);

				auto nextPos = ballPosition;
				maths::vector_add(nextPos, maths::vector_mul(ball->Direction, ball->Speed / 10.0f));
				auto pt2 = proj::xform_to_2d(nextPos);
				SDL_RenderDrawLine(winmain::Renderer, pt1.X, pt1.Y, pt2.X, pt2.Y);
			}
		}
	}
}

void DebugOverlay::DrawCicleType(circle_type& circle)
{
	vector2 linePt{ circle.Center.X + sqrt(circle.RadiusSq), circle.Center.Y };
	auto pt1 = proj::xform_to_2d(circle.Center);
	auto pt2 = proj::xform_to_2d(linePt);
	auto radius = abs(pt2.X - pt1.X);

	SDL_RenderDrawCircle(winmain::Renderer, pt1.X, pt1.Y, radius);
}

void DebugOverlay::DrawLineType(line_type& line)
{
	auto pt1 = proj::xform_to_2d(line.Origin);
	auto pt2 = proj::xform_to_2d(line.End);

	SDL_RenderDrawLine(winmain::Renderer, pt1.X, pt1.Y, pt2.X, pt2.Y);
}

void DebugOverlay::DrawEdge(TEdgeSegment* edge)
{
	if (options::Options.DebugOverlayCollisionMask)
	{
		TBall* refBall = nullptr;
		for (auto ball : pb::MainTable->BallList)
		{
			if (ball->ActiveFlag)
			{
				refBall = ball;
				break;
			}
		}
		if (refBall != nullptr && (refBall->CollisionMask & edge->CollisionGroup) == 0)
			return;
	}

	auto line = dynamic_cast<TLine*>(edge);
	if (line)
	{
		DrawLineType(line->Line);
		return;
	}

	auto circle = dynamic_cast<TCircle*>(edge);
	if (circle)
	{
		DrawCicleType(circle->Circle);
		return;
	}

	auto flip = dynamic_cast<TFlipperEdge*>(edge);
	if (flip)
	{
		flip->set_control_points(pb::time_now);
		flip->build_edges_in_motion();

		DrawLineType(TFlipperEdge::lineA);
		DrawLineType(TFlipperEdge::lineB);
		DrawCicleType(TFlipperEdge::circlebase);
		DrawCicleType(TFlipperEdge::circleT1);
	}
}
