#include "BreakoutBall.h"
#include "AABB.h"
#include "Vector2.h"
#include <math.h>

#include "BreakoutBox.h"
#include "BreakoutPaddle.h"
#include "BreakoutWorldBoundaries.h"

#include "GameConfig.h"

#include "GameException.h"

BreakoutBall::BreakoutBall(const int &radius, const ColourRGB &innerColour, const ColourRGB &outerColour)
: m_radius(radius), m_spriteID(0)
{
	m_spriteID = Visualisation::getSingleton()->CreateSpriteBall((int)radius, innerColour, outerColour);
}

BreakoutBall::~BreakoutBall()
{
}

void BreakoutBall::Draw()
{
	int halfRadius = m_radius / 2;
	Visualisation::getSingleton()->DrawSprite(m_spriteID, m_position.x - halfRadius, m_position.y - halfRadius);
}

void BreakoutBall::Update()
{
	if(m_velocity.x > BALL_MAX_SPEED_X)
		m_velocity.x = BALL_MAX_SPEED_X;
	if(m_velocity.y > BALL_MAX_SPEED_Y)
		m_velocity.y = BALL_MAX_SPEED_Y;

	m_previousPosition = m_position;
	m_position += m_velocity;
}

bool BreakoutBall::HandleCollisionWithBox(BreakoutBox* pBox)
{
	if(!pBox)
	{
		throw GameException(eMinorError, "BreakoutBall: HandleCollisionWithBox() failed due to non-existant box passed to it.");
		return false;
	}

	AABBi bbox = pBox->GetAABB();
	Vector2i closestPoint;
	if(AABBi::AABBvsCircleCollisionTest(bbox, m_position, m_radius, closestPoint))
	{
		eAABBSideQueryResult boxSide = AABBi::GetSideFromClosestPoint(bbox, closestPoint);
		if(boxSide == eInsideAABB)
		{
			AABBi::AABBvsCircleCollisionTest(bbox, m_previousPosition, m_radius, closestPoint);
			boxSide = AABBi::GetSideFromClosestPoint(bbox, closestPoint);
		}

		switch(boxSide)
		{
			case eTopSide:
				m_position.y = bbox.Top + m_radius;
				m_velocity.y = -m_velocity.y;
				break;
			case eBottomSide:
				m_position.y = bbox.Bottom - m_radius;
				m_velocity.y = -m_velocity.y;
				break;
			case eLeftSide:
				m_position.x = bbox.Left - m_radius;
				m_velocity.x = -m_velocity.x;
				break;
			case eRightSide:
				m_position.x = bbox.Right + m_radius;
				m_velocity.x = -m_velocity.x;
				break;
		}

		return true;
	}

	return false;
}

bool BreakoutBall::HandleCollisionWithPaddle(BreakoutPaddle* pPaddle)
{
	if(!pPaddle)
	{
		throw GameException(eMinorError, "BreakoutBall: HandleCollisionWithPaddle() failed due to non-existant paddle passed to it.");
		return false;
	}

	//With the paddle, any collision will result in an upward reflection
	AABBi paddleBox = pPaddle->GetAABB();

	int minCircleX = m_position.x - m_radius;
	int maxCircleX = m_position.x + m_radius;

	if(minCircleX > paddleBox.Right)
		return false;
	else if(maxCircleX < paddleBox.Left)
		return false;

	int minCircleY = m_position.y - m_radius;
	int maxCircleY = m_position.y + m_radius;
	if(minCircleY > paddleBox.Top)
		return false;
	if(maxCircleY < paddleBox.Bottom)
		return false;

	int yDiff = paddleBox.Top - minCircleY;
	m_position.y -= yDiff;
	m_velocity.y = -m_velocity.y;

	int xDiff = m_position.x - (paddleBox.Left + ((paddleBox.Right - paddleBox.Left) / 2));
// 	if(xDiff < 0)
// 		xDiff = -xDiff;
	m_velocity.x += xDiff / PADDLE_SPIN_PRECISION;
// 	if(m_velocity.x < 0)
// 		m_velocity.x -= xDiff / 6;
// 	else
// 		m_velocity.x += xDiff / 6;
	
	return true;
}

bool BreakoutBall::HandleCollisionWithWorldBoundaries(BreakoutWorldBoundaries* pBounds)
{
	if(!pBounds)
	{
		throw GameException(eMinorError, "BreakoutBall: HandleCollisionWithWorldBoundaries() failed due to non-existant world boundaries passed to it.");
		return false;
	}

	bool bCollides = false;

	Vector2i min = pBounds->GetMinBounds(), max = pBounds->GetMaxBounds();

	int posDiff = (m_position.x - m_radius) - min.x;
	if(posDiff < 0)
	{
		bCollides = true;
		m_position.x -= posDiff; //The same as doing += -posDiff 
		m_velocity.x = -m_velocity.x;
	}

	posDiff = (m_position.x + m_radius) - max.x;
	if(posDiff > 0)
	{
		bCollides = true;
		m_position.x -= posDiff;
		m_velocity.x = -m_velocity.x;
	}

	posDiff = (m_position.y - m_radius) - min.y;
	if(posDiff < 0)
	{
		bCollides = true;
		m_position.y -= posDiff; //The same as doing += -posDiff 
		m_velocity.y = -m_velocity.y;
	}

	posDiff = (m_position.y + m_radius) - max.y;
	if(posDiff > 0)
	{
		bCollides = true;
		m_position.y -= posDiff;
		m_velocity.y = -m_velocity.y;
	}

	return bCollides;
}
