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

//---------------------------
// Includes
//---------------------------
#include "CollisionList.h"

//---------------------------
// Defines
//---------------------------
#define GAME_ENGINE (GameEngine::GetSingleton())
#define MARIO		(Mario::GetSingleton())
#define ENEMY_LIST	(EnemyList::GetSingleton())
#define BLOCK_LIST	(BlockList::GetSingleton())
#define ENTITY_LIST	(EntityList::GetSingleton())
#define HUD			(HUD::GetSingleton())
#define SOUND		(Sound::GetSingleton())
#define HANDY		(Handy::GetSingleton())
#define LEVEL		(Level::GetSingleton())

CollisionList* CollisionList::m_CollisionListPtr=nullptr;

//---------------------------
// Constructor & Destructor
//---------------------------
CollisionList::CollisionList()
{
	// nothing
}

CollisionList::~CollisionList()
{
	// nothing
}

CollisionList* CollisionList::GetSingleton()
{
	if (m_CollisionListPtr == nullptr)
	{
		m_CollisionListPtr = new CollisionList();
	}
	return m_CollisionListPtr;
}

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

void CollisionList::Tick(double deltaTime)
{
	if (!MARIO->HasNoCollisionRects())
	{
		// mario collisions
		MarioVToEnemiesCollision();
		MarioHToEnemiesCollision();

		// mario - blocks
		MarioVToBlocksCollision();
		MarioHToBlocksCollision();

		// mario - entities
		MarioToEntityCollision();

		// mario - pipes
		MarioToPipeEnterance();
		MarioToPipeExit();

		// mario - flagpole (level)
		MarioToFlagPole();

		// troopa - goomba
		TroopaToGoomba();
	}

	// enemie - blocks
	EnemiesToBlocksCollision();

	// entities - blocks
	EntitiyToBlockCollision();
}

void CollisionList::Paint(MATRIX3X2 matView)
{
	//RECT2 test = m_MarioPtr->GetHRectOfMario();
	//GAME_ENGINE->SetColor(COLOR(255,0,0,150));
	//GAME_ENGINE->FillRect(test);

}

//bool CollisionList::RectIntersectTest(RECT2 r1, RECT2 r2)
//{
//	bool noIntersect =
//		r1.right < r2.left || r1.left > r2.right ||
//		r1.top > r2.bottom || r1.bottom < r2.top;
//
//	return !noIntersect;
//}
//
//RECT2 CollisionList::RectIntersect(RECT2 r1, RECT2 r2)
//{
//	RECT2 r;
//	if (RectIntersectTest(r1, r2))
//	{
//		r.top = max(r1.top, r2.top);
//		r.bottom = min(r1.bottom, r2.bottom);
//		r.left = max(r1.left, r2.left);
//		r.right = min(r1.right, r2.right);
//	}
//	return r;
//}

void CollisionList::MarioHToBlocksCollision()
{
	RECT2 marioH = MARIO->GetHRectOfMario();
	for (int i = 0; i < BlockList::NR_OF_BLOCKS; ++i)
	{
		RECT2 blockRect = BLOCK_LIST->GetAABBRect(i);
		if (HANDY->RectIntersectTest(marioH, blockRect))
		{
			OutputDebugString("AABB - Mario + Block - Horizontal \n");
			RECT2 r = HANDY->RectIntersect(marioH, blockRect);

			//come from left
			if (marioH.left < r.left && marioH.left < r.right &&
				marioH.right > r.left && marioH.right == r.right)
			{
				DOUBLE2 move ( -(r.right - r.left), 0);
				MARIO->Move( move );
			}

			//come from right
			if (marioH.left == r.left && marioH.left < r.right &&
				marioH.right > r.left && marioH.right > r.right)
			{
				DOUBLE2 move ( (r.right - r.left), 0);
				MARIO->Move( move );
			}
		}
	}
}

void CollisionList::MarioVToBlocksCollision()
{
	RECT2 marioV = MARIO->GetVRectOfMario();
	for (int i = 0; i < BLOCK_LIST->NR_OF_BLOCKS; ++i)
	{
		RECT2 blockRect = BLOCK_LIST->GetAABBRect(i);
		if (HANDY->RectIntersectTest(marioV, blockRect))
		{
			OutputDebugString("AABB - Mario + Block - Vertical \n");
			RECT2 r = HANDY->RectIntersect(marioV, blockRect);

			//come from top
			if (marioV.top < r.top && marioV.top < r.bottom &&
				marioV.bottom > r.top && marioV.bottom == r.bottom)
			{
				double depth = r.bottom - r.top;
				MARIO->SetVelocityToZero(false, true);
				DOUBLE2 move ( 0, -depth );		
				MARIO->Move( move );
			}

			//come from bottom
			if (marioV.top == r.top && marioV.top < r.bottom &&
				marioV.bottom > r.top && marioV.bottom > r.bottom)
			{
				DOUBLE2 move ( 0, (r.bottom - r.top) );
				MARIO->ReverseVerticalVelocity();
				//MARIO->SetVelocityToZero(false, true);
				MARIO->Move( move );

				// block bounce
				if( MARIO->GetFormState() != 0 || BLOCK_LIST->GetBlockType(i) == 1 || BLOCK_LIST->GetBlockType(i) == 5)	// take CND damage if mario is big or if its a ?-block
					BLOCK_LIST->TakeDamageToCondition(i);																		// hidden block gets damaged by all mario states!
				//BLOCK_LIST->CheckBlockIfDestroyed(i);			// bug fix
				BLOCK_LIST->BounceUp(i);
				SOUND->SFX(2);
			}
		}
	}
}

void CollisionList::MarioHToEnemiesCollision()
{
	RECT2 marioH = MARIO->GetHRectOfMario();
	for (int i = 0; i < ENEMY_LIST->NR_OF_ENEMIES; ++i)
	{
		RECT2 enemyRect = ENEMY_LIST->GetAABBRect(i);
		if (HANDY->RectIntersectTest(marioH, enemyRect))
		{
			OutputDebugString("AABB - Mario + Enemy - Horizontal \n");
			if (!MARIO->IsStarman())	// if NOT starman
			{
				RECT2 r = HANDY->RectIntersect(marioH, enemyRect);

				//come from left
				if (marioH.left < r.left && marioH.left < r.right &&
					marioH.right > r.left && marioH.right == r.right)
				{
					MARIO->IsHit();
				}

				//come from right
				if (marioH.left == r.left && marioH.left < r.right &&
					marioH.right > r.left && marioH.right > r.right)
				{
					MARIO->IsHit();
				}
			}
			else	// if starman
			{
				ENEMY_LIST->IsFiredOn(i);
				int type = ENEMY_LIST->GetTypeOfEnemy(i);
				if (type == 0)	HUD->AddScore(SCORE_KILL_GOOMBA);
				if (type == 1)	HUD->AddScore(SCORE_KILL_TROOPA);
			}
		}
	}
}

void CollisionList::MarioVToEnemiesCollision()
{
	RECT2 marioV = MARIO->GetVRectOfMario();
	for (int i = 0; i < ENEMY_LIST->NR_OF_ENEMIES; ++i)
	{
		RECT2 enemyRect = ENEMY_LIST->GetAABBRect(i);
		if (HANDY->RectIntersectTest(marioV, enemyRect))
		{
			OutputDebugString("AABB - Mario + Enemy - Vertical \n");
			RECT2 r = HANDY->RectIntersect(marioV, enemyRect);

			//come from top
			if (marioV.top < r.top && marioV.top < r.bottom &&
				marioV.bottom > r.top && marioV.bottom == r.bottom)
			{
				DOUBLE2 move ( 0, -(r.bottom - r.top));			
				MARIO->SetVelocityToZero(false, true);
				MARIO->Move( move );
				MARIO->KillJump();
				SOUND->SFX(7,1,-1);
				ENEMY_LIST->IsJumpedOn(i);
				// give score depending on enemy
				switch (ENEMY_LIST->GetTypeOfEnemy(i))
				{
				case 0:
					HUD->AddScore(SCORE_KILL_GOOMBA);
					break;
				case 1:
					HUD->AddScore(SCORE_KILL_TROOPA);
					ENEMY_LIST->SetDirection(i, !MARIO->GetDirection());	// '!' because mario's dir true == enemy's dir false. true is the original direction of the character.
					break;
				default:
					break;
				}
			}

			//come from bottom (enemy lands on mario)
			if (marioV.top == r.top && marioV.top < r.bottom &&
				marioV.bottom > r.top && marioV.bottom > r.bottom)
			{
				DOUBLE2 move ( 0, (r.bottom - r.top) );
				MARIO->SetVelocityToZero(false, true);
				//MARIO->Move( move );
				MARIO->SetLife(false);
				//SOUND->SFX(8);
			}
		}
	}
}

void CollisionList::EnemiesToBlocksCollision()
{
	for (int i = 0; i < ENEMY_LIST->NR_OF_ENEMIES; ++i)
	{
		RECT2 enemyRect = ENEMY_LIST->GetAABBRect(i);
		for (int b = 0; b < BLOCK_LIST->NR_OF_BLOCKS; ++b)
		{
			RECT2 blockRect = BLOCK_LIST->GetAABBRect(b);
			if (HANDY->RectIntersectTest(enemyRect, blockRect))
			{
				RECT2 r = HANDY->RectIntersect(enemyRect, blockRect);
				DOUBLE2 move ( 0, -(r.bottom - r.top) );
				ENEMY_LIST->Move(i, move);
				ENEMY_LIST->SetVelocityToZero(i, false, true);
			}
		}
	}
}

bool CollisionList::MarioToEntityCollision()
{
	bool doesCollide = false;
	RECT2 mario = MARIO->GetHRectOfMario();		// only the horizontal aabb is enough for this collision

	for (int i = 0; i < ENTITY_LIST->GetSize(); ++i)
	{
		DOUBLE2 entityPos = ENTITY_LIST->GetPosition(i);

		if (HANDY->CheckIfInRange(MARIO->GetPosition(), entityPos))
		{
			RECT2 entity = ENTITY_LIST->GetAABBRect(i);
			if (HANDY->RectIntersectTest(mario, entity))
			{
				doesCollide = true;
				OutputDebugString("AABB - Mario + Entity \n");

				// check what type of entity and which reward/soundFX
				int type= ENTITY_LIST->GetType(i);
				switch (type)
				{
				case 1:			// coin
					HUD->AddCoin();
					SOUND->SFX(4,1,50);
					break;

				case 2:			// flower
					if (MARIO->GetFormState() == 1)		// turn into fire if mario is big
						MARIO->Grow();
					HUD->AddScore(SCORE_PICKUP_FLOWER);
					SOUND->SFX(5);
					ENTITY_LIST->SetStage(i,3);
					break;

				case 3:			// mushroom
					if (MARIO->GetFormState() == 0 && ENTITY_LIST->GetMushroomType(i) == 0)		// only grow when mario is small (but not into fire)
						MARIO->Grow();
					HUD->AddScore(SCORE_PICKUP_MUSHROOM);
					SOUND->SFX(5);
					ENTITY_LIST->SetStage(i,3);
					break;

				case 4:			// star
					HUD->AddScore(SCORE_PICKUP_STAR);
					ENTITY_LIST->SetStage(i,3);
					MARIO->ActivateStarman(true);
					break;

				default:
					break;
				}

				// delete the hit entity
				ENTITY_LIST->DeleteThisEntity(i);
			}
		}
	}

	return doesCollide;
}

void CollisionList::MarioToPipeEnterance()
{
	RECT2 mario = MARIO->GetVRectOfMario();
	RECT2 pipe = LEVEL->GetEnterancePipeAABB();
	if (HANDY->RectIntersectTest(mario, pipe))
	{
		OutputDebugString("AABB - Mario + Pipe Enterance\n");
		if (GAME_ENGINE->IsKeyDown(VK_DOWN))
		{
			MARIO->StartPipeAnimation(0);
			SOUND->SFX(3);
		}
	}
}

void CollisionList::MarioToPipeExit()
{
	RECT2 mario = MARIO->GetVRectOfMario();
	RECT2 pipe = LEVEL->GetExitPipeAABB();
	if (HANDY->RectIntersectTest(mario, pipe))
	{
		OutputDebugString("AABB - Mario + Pipe Exit\n");
		if (GAME_ENGINE->IsKeyDown(VK_RIGHT))
		{
			MARIO->StartPipeAnimation(1);
			SOUND->SFX(3);
		}
	}
}

void CollisionList::EntitiyToBlockCollision()
{
	for (int i = 0; i < ENTITY_LIST->GetSize(); ++i)
	{
		RECT2 ent = ENTITY_LIST->GetAABBRect(i);
		DOUBLE2 entPos = ENTITY_LIST->GetPosition(i);

		for (int j = 0; j < BLOCK_LIST->NR_OF_BLOCKS; ++j)
		{
			// check if nearby
			DOUBLE2 blockPos = BLOCK_LIST->GetPosition(j);
			if (HANDY->CheckIfInRange(entPos,blockPos))
			{
				RECT2 block = BLOCK_LIST->GetAABBRect(j);
				if (HANDY->RectIntersectTest(ent, block))
				{
					RECT2 coll = HANDY->RectIntersect(ent, block);
					DOUBLE2 dir (0, -(coll.bottom - coll.top));

					ENTITY_LIST->Move(i, dir);
					ENTITY_LIST->ZeroVelocity(i, false, true);
				}
			}
		}
	}
}

void CollisionList::MarioToFlagPole()
{
	DOUBLE2 marioPos ( MARIO->GetPosition() );
	DOUBLE2 polePos ( LEVEL->GetFlagPoleAABB().left, marioPos.y) ;		// same Y as mario, because the height does not matter, same height gives more accurate range (in a way).

	if ( HANDY->CheckIfInRange(marioPos, polePos) )
	{
		if ( HANDY->RectIntersectTest(MARIO->GetHRectOfMario(), LEVEL->GetFlagPoleAABB()) )
		{
			// mario goes in animated state

			// flag goes down
			LEVEL->FlagCanDrop();
			if (!LEVEL->FlagIsDown())
				SOUND->SFX(12);
		}
	}
}

void CollisionList::TroopaToGoomba()
{
	DOUBLE2 troopaPos = ENEMY_LIST->GetPos(ENEMY_LIST->NR_OF_GOOMAS);
	RECT2 troopa = ENEMY_LIST->GetAABBRect(ENEMY_LIST->NR_OF_GOOMAS);

	if (troopaPos.y > 50 && troopaPos.y < 224)
	{
		for (int i = 0; i < ENEMY_LIST->NR_OF_GOOMAS; ++i)
		{
			DOUBLE2 goombaPos = ENEMY_LIST->GetPos(i);
			if (HANDY->CheckIfInRange(troopaPos, goombaPos))
			{
				RECT2 goomba = ENEMY_LIST->GetAABBRect(i);
				if (HANDY->RectIntersectTest(troopa, goomba))
				{
					ENEMY_LIST->IsFiredOn(i);
					HUD->AddScore(SCORE_KILL_GOOMBA*2);
					SOUND->SFX(7);
				}
			}
		}
	}
}



