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

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

//---------------------------
// Defines
//---------------------------
#define GAME_ENGINE (GameEngine::GetSingleton())
#define LEVEL		(Level::GetSingleton())
#define MARIO		(Mario::GetSingleton())

//---------------------------
// Constructor & Destructor
//---------------------------
Goomba::Goomba(int x, int y, Bitmap *bmpBitmapPtr) 
	:EnemyMaster(x, y, bmpBitmapPtr)
	//bool
	,m_IsGoingLeft(true)
	,m_IsActive(false)
	//int
	,m_Width()
	,m_Height()
	,m_TickCount()
	,m_AnimationStep()
	,m_StompedTimer(STOMPED_ANIMATION_TIME)
	//Bitmap
	,m_BmpBitmapPtr(bmpBitmapPtr)
	//Enum
	,m_BehaviourState(BehaviourState::WALKING)
{
	// pos
	m_Position = DOUBLE2 (x, y);

	//data
	m_Width = m_BmpBitmapPtr->GetWidth() / TOTAL_STEPS;
	m_Height = m_BmpBitmapPtr->GetHeight();

	m_Center.x = -m_Width/2;
	m_Center.y = -m_Height/2;

}

Goomba::~Goomba()
{
	// delete is done in the EnemyList! :)
}

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

void Goomba::Tick(double deltaTime)
{
	// first look if mario was close enough to start the tick
	DOUBLE2 marioPos = MARIO->GetPosition();
	if (marioPos.x > m_Position.x - AGRO_RANGE && marioPos.x < m_Position.x + AGRO_RANGE)
		m_IsActive = true;		// when in range, the enemy will keep ticking

	// second if they are activated they keep being active
	if (m_IsActive)
	{
		m_Velocity.x = 0;

		switch (m_BehaviourState)
		{
		case Goomba::BehaviourState::WALKING:
			WalkingTick(deltaTime);
			LevelCollision();
			break;

		case Goomba::BehaviourState::STOMPED:
			StompedTick(deltaTime);
			--m_StompedTimer;
			break;

		case Goomba::BehaviourState::BURNED:
			BurnedTick(deltaTime);
			break;
		}

	}
}

void Goomba::Paint(MATRIX3X2 matView)
{
	switch (m_BehaviourState)
	{
	case Goomba::BehaviourState::WALKING:
		WalkingPaint(matView);
		break;

	case Goomba::BehaviourState::STOMPED:
		StompedPaint(matView);
		break;

	case Goomba::BehaviourState::BURNED:
		BurnedPaint(matView);
		break;
	}
}

void Goomba::WalkingTick(double deltaTime)
{
	++m_TickCount;
	if ((m_TickCount % ANIMATION_INTERVAL) == 0)
	{
		m_TickCount = 0;

		++m_AnimationStep;
		m_AnimationStep %= WALKING_STEPS;
	}

	// det clip rect
	m_Rect.top = 0;
	m_Rect.bottom = m_Rect.top + m_Height;
	m_Rect.left = m_Width * m_AnimationStep;
	m_Rect.right = m_Rect.left + m_Width;

	// movement
	Movement(deltaTime);
}

void Goomba::StompedTick(double deltaTime)
{
	m_TickCount = 0;
	m_AnimationStep = 2;

	// det clip rect
	m_Rect.top = 0;
	m_Rect.bottom = m_Rect.top + m_Height;
	m_Rect.left = m_Width * m_AnimationStep;
	m_Rect.right = m_Rect.left + m_Width;
}

void Goomba::BurnedTick(double deltaTime)
{
	m_TickCount = 0;
	m_AnimationStep = 0;

	// det clip rect
	m_Rect.top = 0;
	m_Rect.bottom = m_Rect.top + m_Height;
	m_Rect.left = m_Width * m_AnimationStep;
	m_Rect.right = m_Rect.left + m_Width;

	// falling
	double extraFallingPower = 2.;		// to make them disapear quicker
	m_Velocity.x = 0;
	m_Velocity.y += FORCE_GRAVITY * extraFallingPower * deltaTime;
	m_Position += m_Velocity * deltaTime;
}

void Goomba::WalkingPaint(MATRIX3X2 matView)
{
	DOUBLE2 scale;
	if (m_IsGoingLeft)
		scale = DOUBLE2 (1,1);
	else
		scale = DOUBLE2 (-1,1);

	MATRIX3X2 matCenter, matTranslate, matScale, matTransform;
	matCenter.SetAsTranslate( m_Center );
	matScale.SetAsScale(scale.x, scale.y);
	matTranslate.SetAsTranslate( m_Position );
	matTransform = matCenter * matScale * matTranslate * matView;

	GAME_ENGINE->SetTransformMatrix(matTransform);
	DOUBLE2 zero (0,0);
	GAME_ENGINE->DrawBitmap(m_BmpBitmapPtr, zero, m_Rect);
}

void Goomba::StompedPaint(MATRIX3X2 matView)
{
	MATRIX3X2 matCenter, matTranslate,matScale, matTransform;
	matCenter.SetAsTranslate( m_Center );
	matScale.SetAsScale(1);
	matTranslate.SetAsTranslate( m_Position );
	matTransform = matCenter * matScale * matTranslate * matView;

	GAME_ENGINE->SetTransformMatrix(matTransform);
	DOUBLE2 zero (0,0);
	GAME_ENGINE->DrawBitmap(m_BmpBitmapPtr, zero, m_Rect);
}

void Goomba::BurnedPaint(MATRIX3X2 matView)
{
	MATRIX3X2 matCenter, matTranslate, matScale, matTransform;
	matCenter.SetAsTranslate( m_Center );
	matScale.SetAsScale(1,-1);
	matTranslate.SetAsTranslate( m_Position );
	matTransform = matCenter * matScale * matTranslate * matView;

	GAME_ENGINE->SetTransformMatrix(matTransform);
	DOUBLE2 zero (0,0);
	GAME_ENGINE->DrawBitmap(m_BmpBitmapPtr, zero, m_Rect);
}

void Goomba::DeleteHitRegion()
{
}

bool Goomba::IsJumpedOn()
{
	m_BehaviourState = BehaviourState::STOMPED;
	return true;
}

bool Goomba::IsFiredOn()
{
	m_BehaviourState = BehaviourState::BURNED;
	m_Velocity.y -= KILLED_JUMP;
	return true;
}

DOUBLE2 Goomba::GetPos()
{
	return m_Position;
}

void Goomba::DrawDebug(MATRIX3X2 matView)
{
	GAME_ENGINE->SetTransformMatrix(matView);

	GAME_ENGINE->SetColor(COLOR(50,50,50,150));	// check if hitregion is on place
	GAME_ENGINE->FillRect(GetAABBRect());
	GAME_ENGINE->SetColor(COLOR(0,0,0));
}

RECT2 Goomba::GetAABBRect()
{
	RECT2 rect;

	if (m_BehaviourState == BehaviourState::WALKING)
	{
		rect.top = m_Position.y - m_Height/2;
		rect.bottom = rect.top + m_Height;
		rect.left = m_Position.x - m_Width/2;
		rect.right = rect.left + m_Width;
		return rect;
	}
	else
		return rect;
}

void Goomba::Move(DOUBLE2 moveValue)
{
	m_Position += moveValue;
}

void Goomba::Movement(double deltaTime)
{
	// left - right
	(m_IsGoingLeft) ?m_Velocity.x -= SPEED*deltaTime :m_Velocity.x += SPEED*deltaTime;

	// gravity
	m_Velocity.y += FORCE_GRAVITY * deltaTime;

	m_Position += m_Velocity * deltaTime;
}

void Goomba::LevelCollision()
{
	HitRegion* hitLevelPtr = LEVEL->GetHitRegion();

	// vertical raycast
	const int nrRaycasts = 1;
	DOUBLE2 heightVector ( 0, m_Height);
	DOUBLE2 rayStart ( m_Position.x, m_Position.y - m_Height/2 );

	HIT hitVertArr[nrRaycasts]={};
	int nrHits = hitLevelPtr->Raycast(rayStart, heightVector, hitVertArr, nrRaycasts, 0);

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

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

	// horizontal raycast
	DOUBLE2 widthVector ( m_Width, 0 );
	rayStart = DOUBLE2 ( m_Position.x - m_Width/2, m_Position.y );
	HIT hitHorizArr[nrRaycasts]={};
	nrHits = hitLevelPtr->Raycast(rayStart, widthVector, hitHorizArr, nrRaycasts, 0);

	if (nrHits > 0)
	{
		double lambda = hitHorizArr[0].lambda;

		if (lambda <= 0.5)
		{
			double depth = (lambda) * widthVector.Length();
			m_Velocity.x = 0;
			m_Position.x += depth;
		}
		else if (lambda > 0.5)
		{
			double depth = (1-lambda) * widthVector.Length();
			m_Velocity.x = 0;
			m_Position.x -= depth;
		}

		m_IsGoingLeft = !m_IsGoingLeft;
	}
}

void Goomba::SetVelocityToZero(bool velocityX, bool velocityY)
{
	if (velocityX)
		m_Velocity.x = 0;
	if (velocityY)
		m_Velocity.y = 0;
}

int Goomba::GetEnemyID()
{
	return ID;
}

bool Goomba::Destory()
{
	if (m_Position.y >= DESTROY_ALTITUDE || m_StompedTimer <= 0)
	{
		return true;
	}
	else
		return false;
}

void Goomba::SetDirection(bool dir)
{
	m_IsGoingLeft = dir;	// dir mario true == dir goomba false, and visa versa. Relative to original direction.
}