#include "pch.h"
#include "TLine.h"

#include "TTableLayer.h"


TLine::TLine(TCollisionComponent* collCmp, char* activeFlag, unsigned int collisionGroup, float x0, float y0, float x1,
             float y1): TEdgeSegment(collCmp, activeFlag, collisionGroup)
{
	X0 = x0;
	Y0 = y0;
	X1 = x1;
	Y1 = y1;
	maths::line_init(Line, x0, y0, x1, y1);
}

TLine::TLine(TCollisionComponent* collCmp, char* activeFlag, unsigned int collisionGroup, const vector2& start,
             const vector2& end) : TEdgeSegment(collCmp, activeFlag, collisionGroup)
{
	X0 = start.X;
	Y0 = start.Y;
	X1 = end.X;
	Y1 = end.Y;
	maths::line_init(Line, X0, Y0, X1, Y1);
}

void TLine::Offset(float offset)
{
	float offX = offset * Line.PerpendicularC.X;
	float offY = offset * Line.PerpendicularC.Y;

	X0 += offX;
	Y0 += offY;
	X1 += offX;
	Y1 += offY;
	maths::line_init(Line, X0, Y0, X1, Y1);
}

float TLine::FindCollisionDistance(const ray_type& ray)
{
	return maths::ray_intersect_line(ray, Line);
}

void TLine::EdgeCollision(TBall* ball, float distance)
{
	CollisionComponent->Collision(
		ball,
		&Line.RayIntersect,
		&Line.PerpendicularC,
		distance,
		this);
}

void TLine::place_in_grid(RectF* aabb)
{
	if (aabb)
	{
		aabb->Merge({
			std::max(X0, X1), std::max(Y0, Y1),
			std::min(X0, X1), std::min(Y0, Y1)
		});
	}

	auto edgeMan = TTableLayer::edge_manager;
	auto xBox0 = edgeMan->box_x(X0);
	auto yBox0 = edgeMan->box_y(Y0);
	auto xBox1 = edgeMan->box_x(X1);
	auto yBox1 = edgeMan->box_y(Y1);

	int dirX = X0 >= X1 ? -1 : 1;
	int dirY = Y0 >= Y1 ? -1 : 1;

	if (yBox0 == yBox1)
	{
		if (dirX == 1)
		{
			while (xBox0 <= xBox1)
				edgeMan->add_edge_to_box(xBox0++, yBox0, this);
		}
		else
		{
			while (xBox0 >= xBox1)
				edgeMan->add_edge_to_box(xBox0--, yBox0, this);
		}
	}
	else if (xBox0 == xBox1)
	{
		if (dirY == 1)
		{
			while (yBox0 <= yBox1)
				edgeMan->add_edge_to_box(xBox0, yBox0++, this);
		}
		else
		{
			while (yBox0 >= yBox1)
				edgeMan->add_edge_to_box(xBox0, yBox0--, this);
		}
	}
	else
	{
		edgeMan->add_edge_to_box(xBox0, yBox0, this);

		// Bresenham line formula: y = dYdX * (x - x0) + y0; dYdX = (y0 - y1) / (x0 - x1)
		auto dyDx = (Y0 - Y1) / (X0 - X1);
		// Precompute constant part: dYdX * (-x0) + y0
		auto precomp = -X0 * dyDx + Y0;
		// X and Y indexes are offset by one when going forwards, not sure why
		auto xBias = dirX == 1 ? 1 : 0, yBias = dirY == 1 ? 1 : 0;

		for (auto indexX = xBox0, indexY = yBox0; indexX != xBox1 || indexY != yBox1;)
		{
			// Calculate y from indexY and from line formula
			auto yDiscrete = (indexY + yBias) * edgeMan->AdvanceY + edgeMan->MinY;
			auto ylinear = ((indexX + xBias) * edgeMan->AdvanceX + edgeMan->MinX) * dyDx + precomp;
			if (dirY == 1 ? ylinear >= yDiscrete : ylinear <= yDiscrete)
			{
				// Advance indexY when discrete value is ahead/behind
				// Advance indexX when discrete value matches linear value
				indexY += dirY;
				if (ylinear == yDiscrete)
					indexX += dirX;
			}
			else
			{
				// Advance indexX otherwise
				indexX += dirX;
			}
			edgeMan->add_edge_to_box(indexX, indexY, this);
		}
	}
}
