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

//---------------------------
// Includes
//---------------------------
#include "Mario.h"
#include "Fireball.h"
#include "SuperMarioBros.h"
#include "HUD.h"

#include <iostream>
#include <fstream>
#include <sstream>
using namespace std;
//---------------------------
// Defines
//---------------------------
#define GAME_ENGINE (GameEngine::GetSingleton())
#define LEVEL		(Level::GetSingleton())
#define SOUND		(Sound::GetSingleton())
#define SMB			(SuperMarioBros::GetSingleton())
#define HUD			(HUD::GetSingleton())
Mario* Mario::m_MarioPtr=nullptr;

//---------------------------
// Constructor & Destructor
//---------------------------
Mario::Mario()
	:m_BmpMarioPtr(nullptr)
	,m_BmpFireballPtr(nullptr)
	,m_BmpExplosionPtr(nullptr)
	,m_HitWorldPtr(nullptr)
	,m_MarioWidth(16)
	,m_SmallMarioHeight(16)
	,m_BigMarioHeight(32)
	,m_TickCount()
	,m_AnimationSlowDown()
	,m_DirectionScale(1)
	,m_FormState()
	,m_BehaviourState(BehaviourState::WAITING)
	,m_CanJump(false)
	,m_IsGoingRight(true)
	,m_DeltaTime()
	,m_IsAlive(true)
	,m_TransformTickCount()
	,m_TransformTime(TRANSFORM_TIME)
	,m_NoCollisionRects(false)
	,m_IsTransforming(false)
	,m_TransformSmallToBig(false)
	,m_TransformingBigToFire(false)
	,m_TransformingBigToSmall(false)
	,m_TransformingFireToSmall(false)
	,m_TransformSwitch(false)
	,m_ProtectionTimer()
	,m_CheckpointPassed(false)
	,m_IsStarman(false)
	,m_StarmanTimer()
	,m_StarmanTickCount()
	,m_AnimationLimit()
	,m_PipeAnimTimer()
	,m_InPipeAnimationDown(false)
	,m_InPipeAnimationRight(false)
	,m_InCastleGoing(false)
{
	// bitmap
	m_BmpMarioPtr = new Bitmap("Resources/Images/Marios.png");

	// hit
	m_HitWorldPtr = LEVEL->GetHitRegion();

	// fireballs
	m_BmpFireballPtr = new Bitmap("Resources/Images/Fireball.bmp");
	m_BmpFireballPtr->SetTransparencyColor(255,0,255);
	m_BmpExplosionPtr = new Bitmap("Resources/Images/Explosion.bmp");
	m_BmpExplosionPtr->SetTransparencyColor(255,0,255);

	// fireballs
	for (int i = 0; i < NR_FIREBALLS; ++i)
	{
		m_FireballPtrArr[i] = nullptr;
	}

	// data
	m_Gravity = DOUBLE2 (0, FORCE_GRAVITY);

	// read from file
	LoadFromFile();
}

Mario::~Mario()
{
	delete m_BmpMarioPtr;			// delete own sprite sheet (mario sprites)
	m_BmpMarioPtr = nullptr;

	// delete firebals
	delete m_BmpFireballPtr;
	m_BmpFireballPtr = nullptr;

	delete m_BmpExplosionPtr;
	m_BmpExplosionPtr = nullptr;

	for (int i = 0; i < NR_FIREBALLS; ++i)
	{
		delete m_FireballPtrArr[i];
		m_FireballPtrArr[i] = nullptr;
	}
}

Mario* Mario::GetSingleton()
{
	if (m_MarioPtr == nullptr)
	{
		m_MarioPtr = new Mario();
	}
	return m_MarioPtr;
}

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

void Mario::Tick(double deltaTime)
{
	//m_Velocity.x = 0;
	const int normalSpeed = SPEED_VALUE;		//
	const int jumpValue = JUMP_HEIGHT_MAX;		// 335 (normal) <> 500 (testing)
	const int maxSpeed = SPEED_MAX;				// top speed mario is allowed to run
	const int friction = FORCE_FRICTION;		// break value when stopping with running
	const int stopValue = 5;					// stop mario from sliding if velocity is very low
	m_DeltaTime = deltaTime;					// used in some methods

	// check if transforming
	if (m_TransformSmallToBig || m_TransformingBigToFire || m_TransformingFireToSmall || m_TransformingBigToSmall)
		m_IsTransforming = true;
	else 
	{
		m_TransformSwitch = false;
		m_IsTransforming = false;
		m_TransformTickCount = 0;
		m_TransformTime = TRANSFORM_TIME;
	}

	// protection (egg. afer harm)
	ProtectionTick();

	// behaviour state tick
	switch (m_BehaviourState)
	{
	case Mario::BehaviourState::WAITING:
		m_TickCount = 0;

		// go to falling
		if (abs(m_Velocity.y) > .5)
		{
			m_BehaviourState = BehaviourState::FALLING;
			break;
		}

		if (GAME_ENGINE->IsKeyDown(VK_LEFT) || GAME_ENGINE->IsKeyDown(VK_RIGHT))
		{
			m_BehaviourState = BehaviourState::WALKING;
			break;
		}

		if (GAME_ENGINE->IsKeyDown(VK_UP))
		{
			m_TickCount = 6;
			m_Velocity.y -= jumpValue;
			SOUND->SFX(1,1,-1);
			m_BehaviourState = BehaviourState::FALLING;
			break;
		}

		// if nothing happens
		// slowdown from friction force ( and prevends ice sliding if standing still )
		if (m_Velocity.x > -stopValue && m_Velocity.x < stopValue)
			m_Velocity.x = 0;

		if (m_Velocity.x > 0)
			m_Velocity.x -= friction * m_DeltaTime;
		else if (m_Velocity.x < 0)
			m_Velocity.x += friction * m_DeltaTime;

		break;

	case Mario::BehaviourState::WALKING:
		++m_AnimationSlowDown;
		if (m_AnimationSlowDown >= 6)
		{
			m_AnimationSlowDown = 0;
			m_TickCount = (m_TickCount % 4) + 1;	// walking ticks from 1,2,3
		}

		// go to falling
		if (abs(m_Velocity.y) > .5)
		{
			m_BehaviourState = BehaviourState::FALLING;
			break;
		}

		// jump
		if (GAME_ENGINE->IsKeyDown(VK_UP))
		{
			m_TickCount = 6;
			m_Velocity.y -= jumpValue;
			SOUND->SFX(1,1,-1);
			m_BehaviourState = BehaviourState::FALLING;
			break;
		}

		// go right
		if (GAME_ENGINE->IsKeyDown(VK_RIGHT))
		{
			if (m_Velocity.x < 0)
				m_Velocity.x += friction * m_DeltaTime;	// use friction to help breaking speed

			m_Velocity.x += normalSpeed * m_DeltaTime;
			break;
		}

		// go left
		if (GAME_ENGINE->IsKeyDown(VK_LEFT))
		{
			if (m_Velocity.x > 0)
				m_Velocity.x -= friction * m_DeltaTime;	// use friction to help breaking speed

			m_Velocity.x -= normalSpeed * m_DeltaTime;
			break;
		}

		// if nothing happens, go to waiting state
		m_BehaviourState = BehaviourState::WAITING;
		break;

	case Mario::BehaviourState::DYING:
		m_TickCount = 7;
		m_IsAlive=false;
		break;

	case Mario::BehaviourState::FALLING:
		if(abs(m_Velocity.y) < 0.1)		// 0 leads to falling animation on the gound, too high value (2) leads to double jump
		{
			m_TickCount = 0;
			m_BehaviourState = BehaviourState::WAITING;
			break;
		}

		if (GAME_ENGINE->IsKeyDown(VK_LEFT))
		{
			if (m_Velocity.x > 0)
				m_Velocity.x -= friction * m_DeltaTime;	// use friction to help breaking speed

			m_Velocity.x -= normalSpeed * m_DeltaTime;
			break;
		}
		if (GAME_ENGINE->IsKeyDown(VK_RIGHT))
		{
			if (m_Velocity.x < 0)
				m_Velocity.x += friction * m_DeltaTime;	// use friction to help breaking speed

			m_Velocity.x += normalSpeed * m_DeltaTime;
			break;
		}

		// if nothing... break
		break;

	case Mario::BehaviourState::SHRINK:
		if (m_FormState != FormState::SMALL || m_IsTransforming)
		{
			ActivateProtection(TRANSFORM_TIME);
			if (m_FormState == FormState::BIG || m_TransformingBigToSmall)
				ShrinkBigToSmall();

			if (m_FormState == FormState::FIRE  || m_TransformingFireToSmall)
				ShrinkFireToSmall();
		}
		break;

	case Mario::BehaviourState::GROW:
		if (m_FormState != FormState::FIRE || m_IsTransforming)
		{
			ActivateProtection(TRANSFORM_TIME);
			if (m_FormState == FormState::SMALL || m_TransformSmallToBig)
			{
				GrowSmallToBig();
				break;
			}

			if (m_FormState == FormState::BIG || m_TransformingBigToFire)
			{
				GrowBigToFire();
				break;
			}
		}
		break;

	case Mario::BehaviourState::TRANSFORMATION_END:
		m_TransformingBigToFire = false;
		m_TransformingBigToSmall = false;
		m_TransformingFireToSmall = false;
		m_TransformSmallToBig = false;
		m_IsTransforming = false;	// is done in the start of tick, this is to be sure.
		m_ProtectionTimer = 0;
		SetNoCollisionRects(false);
		SetVelocityToZero(true, true);
		m_BehaviourState = BehaviourState::WAITING;
		break;

	default:
		break;
	}

	// set speed limitations
	if (m_Velocity.x > 0 && m_Velocity.x > maxSpeed)
		m_Velocity.x = maxSpeed;
	if(m_Velocity.x < 0 && m_Velocity.x < -maxSpeed)
		m_Velocity.x = -maxSpeed;

	if (!m_IsTransforming)	// stays on place when transforming
	{
		if (!m_InPipeAnimationDown && !m_InPipeAnimationRight && !m_InCastleGoing)
		{
			// Gravity
			m_Velocity += m_Gravity * m_DeltaTime;

			// Resulting position
			m_Position += m_Velocity * m_DeltaTime;
		}
		else
		{
			if (m_InPipeAnimationDown)
				GoDownInPipe();
			if (m_InPipeAnimationRight)
				GoRightInPipe();
			if (m_InCastleGoing)
				GoInCastle();
		}
	}

	// check if checkpoint is passed
	if (m_Position.x >= LEVEL->CHECK_POINT)
		m_RespawnPos = DOUBLE2 (1072, 180);

	// Level bounds so mario does not leave the game, also if dead, check when to respawn
	LevelBoundaries();
	AutoRespawn();

	// Hit detection with world
	RaycastsHorizontal();
	RaycastsVertical();

	// death sound
	if (!m_IsAlive)
		SOUND->SFX(8);

	// starman tick
	StarmanTick();

	// paint fireballs
	for (int i = 0; i < NR_FIREBALLS; i++)
	{
		if (m_FireballPtrArr[i] != nullptr)
			m_FireballPtrArr[i]->Tick(deltaTime);
	}

}

void Mario::Paint(MATRIX3X2 matView)
{
	MATRIX3X2 matTransform, matCenter, matTranslate, matScale;

	// check direction
	CheckDirection();

	if (!m_IsGoingRight)
		matScale.SetAsScale(-1,1);
	else 
		matScale.SetAsScale(1,1);

	// apply matrices
	matCenter.SetAsTranslate(-m_MarioWidth/2, 0);
	matTranslate.SetAsTranslate(m_Position);
	matTransform = matCenter * matScale * matTranslate * matView;

	GAME_ENGINE->SetTransformMatrix(matTransform);
	PaintMario();

	// paint fireballs
	for (int i = 0; i < NR_FIREBALLS; i++)
	{
		if (m_FireballPtrArr[i] != nullptr)
			m_FireballPtrArr[i]->Paint(matView);
	}

	// mario info
	//PrintMarioInfo(matView);
}

void Mario::PaintMario()
{
	RECT clip;

	int vertSpacing = 0;
	const int starmanInterval = 60;
	const int maxSpacing = 8;
	if (m_IsStarman)
	{	
		++m_StarmanTickCount;
		if (m_StarmanTickCount % starmanInterval == 0)
			++m_AnimationLimit;
		m_AnimationLimit = (m_AnimationLimit % maxSpacing) + 1;
		vertSpacing = 48 * m_AnimationLimit;
	}
	OutputDebugString(String("") + m_StarmanTimer +" // " +m_StarmanTickCount +"\n");
	switch (m_FormState)
	{
	case Mario::FormState::SMALL:
		clip.top = 32 + vertSpacing;
		clip.bottom = clip.top + STEP_HEIGHT_SMALL;
		clip.left = STEP_WIDTH * m_TickCount;
		clip.right = clip.left + STEP_WIDTH;
		GAME_ENGINE->DrawBitmap(m_BmpMarioPtr, 0, 0, clip);
		break;

	case Mario::FormState::BIG:
		clip.top = 0 + vertSpacing;
		clip.bottom = clip.top + STEP_HEIGHT_BIG;
		clip.left = STEP_WIDTH * m_TickCount;
		clip.right = clip.left + STEP_WIDTH;
		GAME_ENGINE->DrawBitmap(m_BmpMarioPtr, 0, 0, clip);
		break;

	case Mario::FormState::FIRE:
		clip.top = 96 + vertSpacing;
		clip.bottom = clip.top + STEP_HEIGHT_BIG;
		clip.left = STEP_WIDTH * m_TickCount;
		clip.right = clip.left + STEP_WIDTH;
		GAME_ENGINE->DrawBitmap(m_BmpMarioPtr, 0, 0, clip);
		break;

	default:
		break;
	}

}

void Mario::LevelBoundaries()
{
	int levelWidth = LEVEL->GetWidth();
	int levelHeight = LEVEL->GetHeight();
	//int groundHeight = 220;

	switch (m_FormState)
	{
	case Mario::FormState::SMALL:
		if (m_Position.x - m_MarioWidth/2 < 0)
			m_Position.x = m_MarioWidth/2;

		if (m_Position.x + m_MarioWidth/2 > levelWidth)
			m_Position.x = levelWidth - m_MarioWidth/2;
		break;

	case Mario::FormState::BIG:
		if (m_Position.x - m_MarioWidth/2 < 0)
			m_Position.x = m_MarioWidth/2;

		if (m_Position.x + m_MarioWidth/2 > levelWidth)
			m_Position.x = levelWidth - m_MarioWidth/2;
		break;

	case Mario::FormState::FIRE:
		if (m_Position.x - m_MarioWidth/2 < 0)
			m_Position.x = m_MarioWidth/2;

		if (m_Position.x + m_MarioWidth/2 > levelWidth)
			m_Position.x = levelWidth - m_MarioWidth/2;
		break;
	}
}

int Mario::GetWidth()
{
	switch (m_FormState)
	{
	case Mario::FormState::SMALL:
		return m_MarioWidth;
	case Mario::FormState::BIG:
		return m_MarioWidth;;
	}

	return 0;
}

int Mario::GetHeight()
{
	switch (m_FormState)
	{
	case Mario::FormState::SMALL:
		return m_SmallMarioHeight;
	case Mario::FormState::BIG:
		return m_BigMarioHeight;
	}
	return 0;
}

DOUBLE2 Mario::GetPosition()
{
	return m_Position;
}

void Mario::PrintMarioInfo(MATRIX3X2 matView)
{
	//MATRIX3X2 matIdentity;
	//matIdentity.SetAsIdentity();
	//GAME_ENGINE->SetTransformMatrix(matIdentity);

	//GAME_ENGINE->SetColor(COLOR( 240, 0, 0 ));
	//GAME_ENGINE->DrawString(String("Mario position ") + m_Position.ToString(), 0, 0);
	//GAME_ENGINE->DrawString(String("Mario velocity ") + m_Velocity.ToString(), 0, 15);

	//GAME_ENGINE->SetColor(COLOR(0,0,0));
}

void Mario::RaycastsVertical()
{
	if (m_IsAlive)
	{
		if (!m_InPipeAnimationDown && !m_InPipeAnimationRight)
		{
			int buffer = 2;	// the amount an exterior ray is shifted inwards ( to prevent teleporting/hovering issues on corners )
			int marioHeight;

			switch (m_FormState)
			{
			case 0:
				marioHeight = STEP_HEIGHT_SMALL;
				break;
			case 1:
				marioHeight = STEP_HEIGHT_BIG;
				break;
			case 2:
				marioHeight = STEP_HEIGHT_BIG;
				break;
			}

			//(m_FormState == FormState::BIG) ?marioHeight = m_BigMarioHeight :marioHeight = m_SmallMarioHeight;

			const int nrRaycasts = 1;
			DOUBLE2 heightVector (0, marioHeight);

			for (int i = 0; i < 2; ++i)
			{
				DOUBLE2 rayStart;
				if (i == 0)	// left ray, pointing down
					rayStart = DOUBLE2 ((m_Position.x - m_MarioWidth/2) + buffer, m_Position.y);

				if (i == 1)	// right ray, pointing down
					rayStart = DOUBLE2 ((m_Position.x + m_MarioWidth/2) - buffer, m_Position.y);

				HIT hitArr[nrRaycasts]={};
				int nrHits = m_HitWorldPtr->Raycast(rayStart, heightVector, hitArr, nrRaycasts, -1);

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

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

			// middle ray, pointing up
			heightVector = DOUBLE2 (0, -marioHeight);
			DOUBLE2 rayStart ((m_Position.x - m_MarioWidth/2), m_Position.y + marioHeight);	// ray 2 is used for interacting with blocks (breaking or getting loot)
			HIT hitArr[nrRaycasts]={};
			int nrHits = m_HitWorldPtr->Raycast(rayStart, heightVector, hitArr, nrRaycasts, -1);

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

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

			//if (m_Velocity.y != 0)
			//	m_BehaviourState = BehaviourState::FALLING;
		}
	}
}

void Mario::RaycastsHorizontal()
{
	if (m_IsAlive)
	{
		if (!m_InPipeAnimationDown && !m_InPipeAnimationRight)
		{
			int rays = 2;
			int bufferVector = 1;			// move the rays inwards a little to fit the bitmap/animation better
			int buffer = 0;					// bufferVector: corrects the vector. buffer: shift th rays closer to the middle on the Y-axis;
			int marioHeight;

			switch (m_FormState)
			{
			case 0:
				marioHeight = STEP_HEIGHT_SMALL;
				break;
			case 1:
				marioHeight = STEP_HEIGHT_BIG;
				break;
			case 2:
				marioHeight = STEP_HEIGHT_BIG;
				break;
			}

			const int nrRayscasts = 1;
			DOUBLE2 widthVector (m_MarioWidth - bufferVector, 0);

			for (int i = 0; i < rays; ++i)			// right going rays
			{
				DOUBLE2 rayStart (m_Position.x - m_MarioWidth/2, m_Position.y + (marioHeight/2-buffer * i));
				HIT hitArr[nrRayscasts]={};
				int nrHits = m_HitWorldPtr->Raycast(rayStart, widthVector, hitArr, nrRayscasts, 0);

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

					m_Velocity.x = 0;

					if (lambda > 0.5)
						m_Position.x -= depth;
					if (lambda <= 0.5)
						m_Position.x += depth2;
				}
			}
		}
	}
}

void Mario::CheckDirection()
{
	if (GAME_ENGINE->IsKeyDown(VK_LEFT))
		m_IsGoingRight = false;

	if (GAME_ENGINE->IsKeyDown(VK_RIGHT))
		m_IsGoingRight = true;
}

int Mario::GetFormState()
{
	switch (m_FormState)
	{
	case 0:			// small
		return 0;

	case 1:			// big
		return 1;

	case 2:			// fire
		return 2;

	default:
		return 0;
	}
}

int Mario::GetBehaviourState()
{
	return (int)m_BehaviourState;
}

RECT2 Mario::GetHRectOfMario()
{
	if (!m_IsAlive || m_NoCollisionRects)
	{
		RECT2 zeroRect;
		return zeroRect;
	}

	int offset = 3;
	RECT2 marioRect;

	switch (m_FormState)
	{
		//case Mario::FormState::SMALL:
	case 0:
		marioRect.top = m_Position.y + offset;
		marioRect.bottom = marioRect.top + STEP_HEIGHT_SMALL - 2*offset;
		marioRect.left = m_Position.x - m_MarioWidth/2;
		marioRect.right = marioRect.left + STEP_WIDTH;
		break;

		//case Mario::FormState::BIG:
	case 1:
		marioRect.top = m_Position.y + offset;
		marioRect.bottom = marioRect.top + STEP_HEIGHT_BIG - 2*offset;
		marioRect.left = m_Position.x - m_MarioWidth/2;
		marioRect.right = marioRect.left + STEP_WIDTH;
		break;

	case 2:
		marioRect.top = m_Position.y + offset;
		marioRect.bottom = marioRect.top + STEP_HEIGHT_BIG - 2*offset;
		marioRect.left = m_Position.x - m_MarioWidth/2;
		marioRect.right = marioRect.left + STEP_WIDTH;
		break;
	}

	return marioRect;
}

RECT2 Mario::GetVRectOfMario()
{
	if (!m_IsAlive || m_NoCollisionRects)
	{
		RECT2 zeroRect;
		return zeroRect;
	}

	int offset = 8;
	RECT2 marioRect;

	switch (m_FormState)
	{
	case 0:	// small 
		marioRect.top = m_Position.y;
		marioRect.bottom = marioRect.top + STEP_HEIGHT_SMALL;
		marioRect.left = m_Position.x - STEP_WIDTH/2 + offset;
		marioRect.right = marioRect.left + STEP_WIDTH - 2*offset;
		break;

	case 1:	// big
		marioRect.top = m_Position.y;
		marioRect.bottom = marioRect.top + STEP_HEIGHT_BIG;
		marioRect.left = m_Position.x - m_MarioWidth/2 + offset;
		marioRect.right = marioRect.left + STEP_WIDTH - 2*offset;
		break;

	case 2:	// fire
		marioRect.top = m_Position.y;
		marioRect.bottom = marioRect.top + STEP_HEIGHT_BIG;
		marioRect.left = m_Position.x - STEP_WIDTH/2 + offset;
		marioRect.right = marioRect.left + STEP_WIDTH - 2*offset;
		break;
	}
	return marioRect;
}

void Mario::Move(DOUBLE2 direction)
{
	m_Position += direction;
}

void Mario::SetVelocityToZero(bool horizontalVelocity, bool verticalVelocity)
{
	if (horizontalVelocity)
		m_Velocity.x = 0;

	if (verticalVelocity)
		m_Velocity.y = 0;
}

void Mario::SetBehaviourState(int stateNumber)
{
	m_BehaviourState = (Mario::BehaviourState)stateNumber;
}

void Mario::PaintHitregions(MATRIX3X2 matView)
{
	// mario RECT2 collision boxes
	GAME_ENGINE->SetTransformMatrix(matView);

	GAME_ENGINE->SetColor(COLOR(0,0,200,100));
	GAME_ENGINE->FillRect(GetVRectOfMario());

	GAME_ENGINE->SetColor(COLOR(0,200,0,100));
	GAME_ENGINE->FillRect(GetHRectOfMario());

	GAME_ENGINE->SetColor(COLOR(0, 0, 255));
	GAME_ENGINE->DrawRect(GetVRectOfMario());
}

void Mario::SetPosition(double x = 0.123, double y = 0.123)		// .123 is easy to remember and unlikely to be used for a set position :)
{
	if (x != .123 && y != .123)
	{
		m_Position = DOUBLE2 (x, y);
		return;
	}

	if (x == .123)
	{
		m_Position.y = y;
		return;
	}

	if (y == .123)
	{
		m_Position.x = x;
		return;
	}
}

void Mario::SetFormState(int state)
{
	m_FormState = (Mario::FormState)state;
}

void Mario::WarpToSpawn()
{
	SetVelocityToZero(true, true);
	SMB->SetCamPos(0,0);
	m_Position = DOUBLE2 (50, 150);

	if (HUD->GetTime() > 100) SOUND->Theme(0);
	else SOUND->Theme(1);
}

void Mario::WarpToSecretArea()
{
	SetVelocityToZero(true, true);
	SMB->SetCamPos(0,SMB->SECRET_ROOM_Y);
	m_Position = DOUBLE2 (50, 250);

	if (HUD->GetTime() > 100) SOUND->Theme(2);
	else SOUND->Theme(3);
}

void Mario::WarpToSecretAreaExit()
{
	SetVelocityToZero(true, true);
	//SMB->SetCamPos(0,0);
	m_Position = DOUBLE2 (2623, 136);

	if (HUD->GetTime() > 100) SOUND->Theme(0);
	else SOUND->Theme(1);
}

void Mario::ReverseVerticalVelocity()
{
	m_Velocity.y = -m_Velocity.y/2;
}

void Mario::DeathJump()
{
	m_Velocity.y = -JUMP_HEIGHT_MAX;
}

void Mario::KillJump()
{
	m_Velocity.y = -JUMP_HEIGHT_KILL;
}

void Mario::SetLife(bool isAlive)
{
	m_IsAlive = isAlive;

	if (m_IsAlive)
	{
		SetBehaviourState(0);
	}
	else
	{
		SetBehaviourState(2);
		SetVelocityToZero(true,true);
		DeathJump();
	}
}

void Mario::AutoRespawn()
{
	if (m_Position.y > RESPAWN_DEPTH_LIMIT)
	{
		SetBehaviourState(0);
		m_IsAlive = true;
		WarpToCheckPoint();
	}
}

void Mario::WarpToCheckPoint()
{
	SetBehaviourState(0);
	m_IsAlive = true;
	SetVelocityToZero(true, true);
	SMB->SetCamPos(0,0);
	m_Position = m_RespawnPos;
}

bool Mario::IsAlive()
{
	return m_IsAlive;
}

bool Mario::GetDirection()
{
	return m_IsGoingRight;
}

void Mario::ShootFireball()
{
	bool shotOne = false;
	for (int i = 0; i < NR_FIREBALLS; ++i)
	{
		// make fire ball if one is nullptr
		if (m_FireballPtrArr[i] == nullptr && !shotOne)
		{
			DOUBLE2 pos (m_Position.x, m_Position.y + 5);
			m_FireballPtrArr[i] = new Fireball(pos, m_BmpFireballPtr, m_BmpExplosionPtr);
			shotOne = true;
			//SOUND->SFX(11,1,75);		// sound is done in fireball self
		}

		//check for deletable fireballs
		if (m_FireballPtrArr[i] != nullptr)
			if (m_FireballPtrArr[i]->CanDestroy())
			{
				delete m_FireballPtrArr[i];
				m_FireballPtrArr[i] = nullptr;
			}
	}
}

void Mario::ShrinkBigToSmall()
{
	--m_TransformTime;
	m_TransformingBigToSmall = true;

	if (m_TransformTime  > 0)
	{
		if (m_TransformTime % TRANSFORM_INTERVAL == 0)
		{
			++m_TransformTickCount;
			m_TransformTickCount %= 2;
			m_TransformSwitch = !m_TransformSwitch;
		}
		if (m_TransformSwitch)
			m_FormState = FormState::BIG;
		else
			m_FormState = FormState::SMALL;
	}

	else	// when transform time is up
	{
		m_FormState = FormState::SMALL;
		m_BehaviourState = BehaviourState::TRANSFORMATION_END;
	}
}

void Mario::ShrinkFireToSmall()
{
	--m_TransformTime;
	m_TransformingFireToSmall = true;

	if (m_TransformTime  > 0)
	{
		if (m_TransformTime % TRANSFORM_INTERVAL == 0)
		{
			++m_TransformTickCount;
			m_TransformTickCount %= 2;
			m_TransformSwitch = !m_TransformSwitch;
		}
		if (m_TransformSwitch)
			m_FormState = FormState::FIRE;
		else
			m_FormState = FormState::SMALL;
	}

	else	// when transform time is up
	{
		m_FormState = FormState::SMALL;
		m_BehaviourState = BehaviourState::TRANSFORMATION_END;
	}
}

void Mario::GrowSmallToBig()
{
	--m_TransformTime;
	m_TransformSmallToBig = true;

	if (m_TransformTime  > 0)
	{
		if (m_TransformTime % TRANSFORM_INTERVAL == 0)
		{
			++m_TransformTickCount;
			m_TransformTickCount %= 2;
			m_TransformSwitch = !m_TransformSwitch;
		}
		if (m_TransformSwitch)
			m_FormState = FormState::SMALL;
		else
			m_FormState = FormState::BIG;
	}

	else	// when transform time is up
	{
		m_FormState = FormState::BIG;
		m_BehaviourState = BehaviourState::TRANSFORMATION_END;
	}
}

void Mario::GrowBigToFire()
{
	--m_TransformTime;
	m_TransformingBigToFire = true;

	if (m_TransformTime > 0)
	{
		if (m_TransformTime % TRANSFORM_INTERVAL == 0)
		{
			++m_TransformTickCount;
			m_TransformTickCount %= 2;
			m_TransformSwitch = !m_TransformSwitch;
		}
		if (m_TransformSwitch)
			m_FormState = FormState::BIG;
		else
			m_FormState = FormState::FIRE;
	}

	else	// when transform time is up
	{
		m_FormState = FormState::FIRE;
		m_BehaviourState = BehaviourState::TRANSFORMATION_END;
	}
}

void Mario::SetNoCollisionRects(bool noCollision)
{
	m_NoCollisionRects = noCollision;
}

void Mario::Shrink()
{
	m_BehaviourState = BehaviourState::SHRINK;
}

void Mario::Grow()
{
	m_BehaviourState = BehaviourState::GROW;
}

bool Mario::HasNoCollisionRects()
{
	return m_NoCollisionRects;
}

void Mario::IsHit()
{
	// if big or fire
	if (m_FormState != FormState::SMALL)
	{
		SOUND->SFX(3);
		ActivateProtection();
		m_BehaviourState = BehaviourState::SHRINK;
	}

	// if small
	else
	{
		SetVelocityToZero(true,true);
		DeathJump();
		m_BehaviourState = BehaviourState::DYING;
	}
}

void Mario::ActivateProtection(int time)
{
	if (time <= 0)
		m_ProtectionTimer = PROTECTION_TIME;
	else
		m_ProtectionTimer = time;
}

void Mario::ProtectionTick()
{
	--m_ProtectionTimer;

	if (m_ProtectionTimer > 0)
		m_NoCollisionRects = true;
	else
		m_NoCollisionRects = false;
}

void Mario::ActivateStarman(bool withSound)
{
	//if (withSound)
	SOUND->Theme(5);
	m_StarmanTimer = STARMAN_TIME;
	m_IsStarman = true;
}

void Mario::StarmanTick()
{
	// check timer
	--m_StarmanTimer;

	if (m_StarmanTimer == 0)
	{
		m_IsStarman = false;
		if (LEVEL->FlagIsDown())
			SOUND->Theme(4);
		else
		{
			if (HUD->GetTime() > 100) SOUND->Theme(0);
			else SOUND->Theme(1);
		}
	}
	else if (m_StarmanTimer < 0)
		m_IsStarman = false;
	else 
		m_IsStarman = true;
}

bool Mario::IsStarman()
{
	return m_IsStarman;
}

void Mario::LoadFromFile()
{
	string sFileName =  "Resources/Files/Data_Mario.txt";
	ifstream file(sFileName);

	if (!file)
	{
		GAME_ENGINE->MessageBox(String("File <Data_Mario.txt> Could not be found!"));
	}
	else
	{
		int posX=0;
		int posY=0;
		int formState=0;

		string sLine;
		while ( !file.eof() )
		{

			getline(file, sLine);
			if (sLine != "")
			{
				string sVar = sLine.substr(0, 15);
				string sValue = sLine.substr(15, sLine.length());

				if (sVar == "PositionX:     ")
				{
					posX = stoi(sValue);
				}
				else if (sVar == "PositionY:     ")
				{
					posY = stoi(sValue);
				}
				else if (sVar == "FormState:     ")
				{
					formState = stoi(sValue);

					if (posX && posY)
					{
						// use values 
						m_Position = DOUBLE2(posX, posY);
						m_FormState = (FormState)formState;

						posX=0;
						posY=0;
						formState=0;
						sVar.clear();
					}
				}
			}
		}
	}
	file.close();
}

void Mario::StartPipeAnimation(int animNr)
{
	if (animNr >= 0 && animNr <= 2)
	{
		m_PipeAnimTimer = PIPE_ANIM_TIME;
	}

	if (animNr == 0)
		m_InPipeAnimationDown = true;
	if (animNr == 1)
		m_InPipeAnimationRight = true;
	if (animNr == 2)
		m_InCastleGoing = true;
}

void Mario::GoDownInPipe()
{
	--m_PipeAnimTimer;

	double speed = 35;

	if (m_PipeAnimTimer >= 0)
	{
		m_Position.y += speed * m_DeltaTime;
	}
	else
	{
		m_InPipeAnimationDown = false;
		WarpToSecretArea();
	}
}

void Mario::GoRightInPipe()
{
	--m_PipeAnimTimer;

	double speed = 18;

	if (m_PipeAnimTimer >= 0)
	{
		m_IsGoingRight = true;
		m_BehaviourState = BehaviourState::WALKING;
		m_Position.x += speed * m_DeltaTime;
	}
	else
	{
		m_InPipeAnimationRight = false;
		WarpToSecretAreaExit();
	}
}

void Mario::GoInCastle()
{
	if (m_InCastleGoing)
	{
		m_IsGoingRight = true;
		m_BehaviourState = BehaviourState::WALKING;
		DOUBLE2 dir (25.,40.);
		m_Position += dir * m_DeltaTime;
	}
}