//-----------------------------------------------------
// Name, first name: Hoet, Guntran
// Group: 1DAE3
//-----------------------------------------------------

//---------------------------
// Includes
//---------------------------
#include "Fireball.h"
#include "Mario.h"
#include "HUD.h"
//---------------------------
// Defines
//---------------------------
#define GAME_ENGINE (GameEngine::GetSingleton())
#define MARIO		(Mario::GetSingleton())
#define LEVEL		(Level::GetSingleton())
#define ENEMYLIST	(EnemyList::GetSingleton())
#define HANDY		(Handy::GetSingleton())
#define HUD			(HUD::GetSingleton())
#define SOUND		(Sound::GetSingleton())
#define BLOCK_LIST	(BlockList::GetSingleton())
//---------------------------
// Constructor & Destructor
//---------------------------
Fireball::Fireball(DOUBLE2 position, Bitmap* bmpFireballPtr, Bitmap* bmpExplosionPtr)
	//bools
	:m_EnemyIsHit(false)
	,m_CanDestroy(false)
	// int - double
	,m_TickCount()
	,m_AnimationStep()
	,m_Speed(170)
	,m_Direction()
	,m_ShotEnemyScore()
	// bmps
	,m_BmpFireballPtr(bmpFireballPtr)
	,m_BmpExplosionPtr(bmpExplosionPtr)
	// others
	,m_Stage(Stage::FIREBALL)
{
	// DOUBLE2's
	m_Position = position;
	m_Gravity = DOUBLE2 (0,13);

	// rect
	m_Rect.top = m_Position.y - m_BmpFireballPtr->GetHeight()/2;
	m_Rect.bottom = m_Rect.top + m_BmpFireballPtr->GetHeight();
	m_Rect.left = m_Position.x - m_BmpFireballPtr->GetWidth()/2;
	m_Rect.right = m_Rect.left + m_BmpFireballPtr->GetWidth();

	// direction
	if (MARIO->GetDirection())
		m_Direction = 1;
	else 
		m_Direction = -1;

	// when created (and thus fired) play soundfx
	SOUND->SFX(11);
}

Fireball::~Fireball()
{
	// nothing to destroy
}

//---------------------------
// Methods - Member functions
//---------------------------

void Fireball::Tick(double deltaTime)
{
	++m_TickCount;
	HitRegion* hitLevelPtr = LEVEL->GetHitRegion();

	// check destroy timer
	TimedDestroy();

	switch (m_Stage)
	{
	case Fireball::Stage::FIREBALL:
		if (m_TickCount%INTERVAL == 0)
		{
			++m_AnimationStep;
			m_AnimationStep %= FIREBALL_STEPS;
		}

		// fireball collision with level
		VerticalLevelCollision(hitLevelPtr);
		HorizontalLevelCollision(hitLevelPtr);
		EnemyCollision();
		BlockCollision();

		// fireball tick
		DoFireballTick(deltaTime);
		break;

	case Fireball::Stage::EXPLOSION:
		if (m_TickCount%INTERVAL == 0)
		{
			++m_AnimationStep;
			if (m_AnimationStep >= EXPLOSION_STEPS)
			{
				m_Stage = Stage::DESTROY;
				break;
			}
		}
		break;

	case Fireball::Stage::DESTROY:
		m_CanDestroy = true;
		break;

	default:
		break;
	}
}

void Fireball::Paint(MATRIX3X2 matView)
{
	MATRIX3X2 matCenter, matTranslate, matTransform;
	RECT r;
	int bmpWidth, bmpHeight;

	switch (m_Stage)
	{
	case Fireball::Stage::FIREBALL:
		bmpWidth = m_BmpFireballPtr->GetWidth() / FIREBALL_STEPS;
		bmpHeight = m_BmpFireballPtr->GetHeight();

		r.top = 0;
		r.bottom = r.top + bmpHeight;
		r.left = bmpWidth * m_AnimationStep;
		r.right = r.left + bmpWidth;

		matCenter.SetAsTranslate(-bmpWidth/2, -bmpHeight/2);
		matTranslate.SetAsTranslate(m_Position);
		matTransform = matCenter * matTranslate * matView;
		GAME_ENGINE->SetTransformMatrix(matTransform);
		GAME_ENGINE->DrawBitmap(m_BmpFireballPtr,0,0,r);
		break;

	case Fireball::Stage::EXPLOSION:
		bmpWidth = m_BmpExplosionPtr->GetWidth() / EXPLOSION_STEPS;
		bmpHeight = m_BmpExplosionPtr->GetHeight();

		r.top = 0;
		r.bottom = r.top + bmpHeight;
		r.left = bmpWidth * m_AnimationStep;
		r.right = r.left + bmpWidth;

		matCenter.SetAsTranslate(-bmpWidth/2, -bmpHeight/2);
		matTranslate.SetAsTranslate(m_Position);
		matTransform = matCenter * matTranslate * matView;
		GAME_ENGINE->SetTransformMatrix(matTransform);
		GAME_ENGINE->DrawBitmap(m_BmpExplosionPtr,0,0,r);
		break;

	case Fireball::Stage::DESTROY:
		// draw nothing in delete stage...
		break;

	default:
		break;
	}
}

bool Fireball::CanDestroy()
{
	if (m_Stage == Stage::DESTROY)
		return true;
	else 
		return false;
}

void Fireball::DoFireballTick(double deltaTime)
{
	// rect
	m_Rect.top = m_Position.y - m_BmpFireballPtr->GetHeight()/2;
	m_Rect.bottom = m_Rect.top + m_BmpFireballPtr->GetHeight();
	m_Rect.left = m_Position.x - m_BmpFireballPtr->GetWidth()/2;
	m_Rect.right = m_Rect.left + m_BmpFireballPtr->GetWidth();

	// positioning
	m_Velocity.x = 0;
	m_Velocity.x += m_Speed * m_Direction * deltaTime;

	m_Velocity += m_Gravity * deltaTime;
	m_Position += m_Velocity;
}

RECT2 Fireball::GetAABB()
{
	return m_Rect;
}

void Fireball::VerticalLevelCollision(HitRegion* hitLevelPtr)
{
	const double jumpValue = 3.;
	int height = m_BmpFireballPtr->GetHeight();
	int width = m_BmpFireballPtr->GetWidth()/FIREBALL_STEPS;

	DOUBLE2 start (m_Position.x, m_Position.y-width/2);
	DOUBLE2 vector (0,height);
	const int nrRaycasts = 1;
	HIT hitArr[nrRaycasts]={};

	int nrHits = hitLevelPtr->Raycast(start, vector, hitArr, nrRaycasts, -1);

	if (nrHits > 0)
	{
		double lambda = hitArr[0].lambda;
		double depth = (1-lambda) * vector.Length();

		m_Velocity.y -= jumpValue;	
		m_Position.y -= depth;
	}

}

void Fireball::HorizontalLevelCollision(HitRegion* hitLevelPtr)
{
	int height = m_BmpFireballPtr->GetHeight();
	int width = m_BmpFireballPtr->GetWidth()/FIREBALL_STEPS;

	DOUBLE2 start (m_Position.x-width/2, m_Position.y);
	DOUBLE2 vector (width,0);
	const int nrRaycasts = 1;
	HIT hitArr[nrRaycasts]={};

	int nrHits = hitLevelPtr->Raycast(start, vector, hitArr, nrRaycasts, 0);

	if (nrHits > 0)
	{
		m_AnimationStep = 0;
		m_Stage = Stage::EXPLOSION;
	}

}

void Fireball::EnemyCollision()
{
	for (int i = 0; i < ENEMYLIST->NR_OF_ENEMIES; ++i)
	{
		DOUBLE2 enemyPos = ENEMYLIST->GetPos(i);
		if ( HANDY->CheckIfInRange(m_Position, enemyPos) )
		{
			RECT2 enemyRect = ENEMYLIST->GetAABBRect(i);
			if (HANDY->RectIntersectTest(m_Rect, enemyRect))
			{
				SOUND->SFX(7);
				ENEMYLIST->IsFiredOn(i);
				int type = ENEMYLIST->GetTypeOfEnemy(i);
				if (type == 0)	HUD->AddScore(CollisionList::SCORE_KILL_GOOMBA);
				if (type == 1)	HUD->AddScore(CollisionList::SCORE_KILL_TROOPA);
				m_EnemyIsHit = true;

				// go to explotion stage
				m_AnimationStep = 0;
				m_Stage = Stage::EXPLOSION;
				break;
			}
		}
	}
}

void Fireball::TimedDestroy()
{
	DOUBLE2 marioPos = MARIO->GetPosition();
	double range = 200;

	if (m_TickCount >= 300 || m_Position.y >= range*2 || !(m_Position.x >= marioPos.x - range && m_Position.x <= marioPos.x + range))		// about 5 sec.
	{
		m_Stage = Stage::DESTROY;
	}
}

//bool Fireball::HitEnemy()
//{
//	return m_EnemyIsHit;
//}
//
//int Fireball::GetScoreShotEnemy()
//{
//	return m_ShotEnemyScore;
//}

void Fireball::BlockCollision()
{
	for (int i = 0; i < BLOCK_LIST->NR_OF_BLOCKS; ++i)
	{
		DOUBLE2 blockPos = BLOCK_LIST->GetPosition(i);
		if (HANDY->CheckIfInRange(m_Position, blockPos))
		{
			RECT2 block = BLOCK_LIST->GetAABBRect(i);
			if (HANDY->RectIntersectTest(m_Rect, block) )
			{
				const double jumpValue = 8.;
				m_Velocity.y = 0;
				
				m_Position.y = blockPos.y - m_BmpFireballPtr->GetHeight()/2;
				//m_Velocity.y -= jumpValue;
			}
		}
	}
}




