#include "BreakoutGameScene.h"

#include "GameConfig.h"

#include "EventManager.h"
#include "ApplicationEvents.h"
#include "InputEvents.h"
#include "BreakoutEvents.h"

#include "BreakoutLGLines.h"
#include "BreakoutLGCircle.h"
#include "BreakoutLGAlternate.h"

#include "DebugUtil.h"

BreakoutGameScene::BreakoutGameScene()
: m_bIsActive(false), m_pBall(0), m_pPaddle(0), m_pWorldBoundaries(0), m_deathLineY(0),
  m_playerLives(0), m_playerScore(0), m_levelNumber(0), m_levelStatus(eLevelStarting),
  m_bgSprite(0), m_startLevelText(0), m_endLevelText(0), m_pScoreCounter(0), m_pLevelCounter(0),
  m_statusChangeTime(0), m_pLivesCounter(0), m_pCurrentLevel(0)
{

}

BreakoutGameScene::~BreakoutGameScene()
{

}

void BreakoutGameScene::Initialise()
{
	DebugUtil::outputDebugText("Breakout Game Scene: Initialisation Start.\n");
	m_deathLineY = SCREEN_HEIGHT;
	m_playerLives = PLAYER_START_LIVES;

	m_pBall = new BreakoutBall(7, ColourRGB(255,0,0), ColourRGB(0,0,255));
	m_pWorldBoundaries = new BreakoutWorldBoundaries(Vector2i(WALL_BORDER_SIZE, WALL_BORDER_SIZE), Vector2i(SCREEN_WIDTH - WALL_BORDER_SIZE, SCREEN_HEIGHT + 20));
	m_pPaddle =  new BreakoutPaddle(Vector2i(160, SCREEN_HEIGHT - WALL_BORDER_SIZE - 15), Vector2i(60, 5), ColourRGB(15,255,15), ColourRGB(64,255,128));
	m_pScoreCounter = new BreakoutScoreCounter(Vector2i(2,2), ColourRGB(255,255,255), m_playerScore);
	m_pLevelCounter = new BreakoutScoreCounter(Vector2i(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2), ColourRGB(255,255,0), m_levelNumber);
	m_pLivesCounter = new BreakoutScoreCounter(Vector2i(SCREEN_WIDTH - (CHARACTER_WIDTH * 3), 2), ColourRGB(255,255,255), m_playerLives);
	
	DebugUtil::outputDebugText("Breakout Game Scene: Objects created.\n");

	AABBi locationArea(BOX_AREA_POS_X, BOX_AREA_POS_X + BOX_AREA_WIDTH, BOX_AREA_POS_Y + BOX_AREA_HEIGHT, BOX_AREA_POS_Y);
	Vector2i boxSize(MEDIUM_BOX_WIDTH, MEDIUM_BOX_HEIGHT);
	m_pBoxLocations = new BreakoutLocationArray(locationArea, boxSize, BOX_LOCATION_PADDING);

 	BreakoutLGLines* pLinesGen = new BreakoutLGLines();
 	m_levelGenerators.push_back(pLinesGen);

	BreakoutLGCircle* pCircleGen = new BreakoutLGCircle();
	m_levelGenerators.push_back(pCircleGen);

	BreakoutLGAlternate* pAlternateGen = new BreakoutLGAlternate();
	m_levelGenerators.push_back(pAlternateGen);

	DebugUtil::outputDebugText("Breakout Game Scene: Level generators created.\n");

	StartNewLevel();
	DebugUtil::outputDebugText("Breakout Game Scene: Level generated.\n");

	m_bgSprite = Visualisation::getSingleton()->CreateSpriteStarryBackground(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_WIDTH / 5);
	m_startLevelText = Visualisation::getSingleton()->CreateSpriteText("Level Start!", ColourRGB(255,255,0));
	m_endLevelText = Visualisation::getSingleton()->CreateSpriteText("Level Complete!", ColourRGB(255,255,0));
	m_livesLabel = Visualisation::getSingleton()->CreateSpriteText("Lives:", ColourRGB(255,255,255));

	EventManager::getSingleton()->AddEventListener(ButtonInputEvent::cEventName, this);

	DebugUtil::outputDebugText("Breakout Game Scene Initialisation End.\n");
}

void BreakoutGameScene::Update()
{
	if(!m_bIsActive)
		return;

	if(m_levelStatus == eLevelPlaying)
	{
		m_pPaddle->Update();
		m_pBall->Update();


		m_pBall->HandleCollisionWithWorldBoundaries(m_pWorldBoundaries);
		m_pBall->HandleCollisionWithPaddle(m_pPaddle);
		m_pPaddle->HandleCollisionWithWorldBoundaries(m_pWorldBoundaries);

		for(size_t i = 0; i < m_pCurrentLevel->size(); i++)
		{
			if(m_pCurrentLevel->at(i)->IsEnabled())
			{
				if(m_pBall->HandleCollisionWithBox(m_pCurrentLevel->at(i)))
				{
					m_pCurrentLevel->at(i)->TakeHit();
					m_playerScore += GAME_POINTS_PER_HIT;
					m_pScoreCounter->Update(m_playerScore);
				}
			}
		}

		
		BreakoutLevel::iterator levelIter = m_pCurrentLevel->begin();
		while(levelIter != m_pCurrentLevel->end())
		{
			if((*levelIter)->IsAlive() == false)
			{
				delete *levelIter;
				levelIter = m_pCurrentLevel->erase(levelIter);
			}
			else
				levelIter++;
		}

		InputHandler* pInput = InputHandler::getSingleton();
		if(pInput->IsKeyDown(eKeyLeft) || pInput->IsKeyDown(eKeyUpLeft) || pInput->IsKeyDown(eKeyDownLeft))
			m_pPaddle->SetVelocity(Vector2i(-PADDLE_SPEED, m_pPaddle->GetVelocity().y));
		else if(pInput->IsKeyDown(eKeyRight) || pInput->IsKeyDown(eKeyUpRight) || pInput->IsKeyDown(eKeyDownRight))
			m_pPaddle->SetVelocity(Vector2i(PADDLE_SPEED, m_pPaddle->GetVelocity().y));
		else
			m_pPaddle->SetVelocity(Vector2i(0, m_pPaddle->GetVelocity().y));

		CheckGameConditions();
	}
	else if(m_levelStatus == eLevelStarting)
	{
		if(m_statusChangeTime + LEVEL_LEAD_IN_TIME < SDL_GetTicks())
			m_levelStatus = eLevelPlaying;
	}
	else if(m_levelStatus == eLevelEnding)
	{
		if(m_statusChangeTime + LEVEL_LEAD_IN_TIME < SDL_GetTicks())
			StartNewLevel();
	}
}

void BreakoutGameScene::CheckGameConditions()
{
	if(m_pBall->GetPosition().y > m_deathLineY)
	{
		m_playerLives--;
		if(m_playerLives < 0)
		{
			BreakoutGameOverEvent* pEvent = new BreakoutGameOverEvent();
			EventManager::getSingleton()->QueueEvent(pEvent);
			return;
		}

		m_pLivesCounter->Update(m_playerLives);
		m_pPaddle->AttachBallToPaddle(m_pBall);
	}


	if(m_pCurrentLevel->size() == 0)
	{
		m_levelStatus = eLevelEnding;
		m_playerLives++; //Give player an extra life for finishing the level
		m_statusChangeTime = SDL_GetTicks();
	}
}

void BreakoutGameScene::StartNewLevel()
{
	if(m_pCurrentLevel)
		delete m_pCurrentLevel;

	m_levelNumber++;
	m_pLevelCounter->Update(m_levelNumber);

	Vector2i boxSize(MEDIUM_BOX_WIDTH, MEDIUM_BOX_HEIGHT);

	int levelGenIndex = rand() % m_levelGenerators.size();
	m_pCurrentLevel = m_levelGenerators[levelGenIndex]->GenerateLevel(m_pBoxLocations, boxSize, m_levelNumber);
	m_pPaddle->AttachBallToPaddle(m_pBall);
	m_levelStatus = eLevelStarting;
	m_statusChangeTime = SDL_GetTicks();
}

void BreakoutGameScene::Reset()
{
	m_levelNumber = 0;
	m_playerLives = PLAYER_START_LIVES;
	m_playerScore = 0;

	StartNewLevel();
}

void BreakoutGameScene::Draw()
{
	if(!m_bIsActive)
		return;

	Visualisation::getSingleton()->DrawSprite(m_bgSprite, 0, 0);
	m_pScoreCounter->Draw();

	Vector2i livesPos = m_pLivesCounter->GetPosition();
	livesPos.x -= Visualisation::getSingleton()->GetSpriteWidth(m_livesLabel);
	Visualisation::getSingleton()->DrawSprite(m_livesLabel, livesPos.x, livesPos.y);
	m_pLivesCounter->Draw();

	if(m_levelStatus == eLevelPlaying)
	{	
		m_pBall->Draw();
		m_pPaddle->Draw();

		if(m_pCurrentLevel)
		{
			for(size_t i = 0; i < m_pCurrentLevel->size(); i++)
				m_pCurrentLevel->at(i)->Draw();
		}
	}
	else if(m_levelStatus == eLevelStarting)
	{
		int width = Visualisation::getSingleton()->GetSpriteWidth(m_startLevelText);
		Visualisation::getSingleton()->DrawSprite(m_startLevelText, (SCREEN_WIDTH / 2) - (width / 2), SCREEN_HEIGHT / 4);
		m_pLevelCounter->Draw();
	}
	else if(m_levelStatus == eLevelEnding)
	{
		int width = Visualisation::getSingleton()->GetSpriteWidth(m_endLevelText);
		Visualisation::getSingleton()->DrawSprite(m_endLevelText, (SCREEN_WIDTH / 2) - (width / 2), SCREEN_HEIGHT / 4);
	}
}

void BreakoutGameScene::Shutdown()
{
	delete m_pBall;
	delete m_pPaddle;
	delete m_pWorldBoundaries;
	delete m_pCurrentLevel;
	delete m_pBoxLocations;
	for(size_t i = 0; i < m_levelGenerators.size(); i++)
		delete m_levelGenerators[i];

	m_levelGenerators.clear();

	Visualisation::getSingleton()->RemoveSprite(m_bgSprite);
	Visualisation::getSingleton()->RemoveSprite(m_startLevelText);
	Visualisation::getSingleton()->RemoveSprite(m_endLevelText);
	Visualisation::getSingleton()->RemoveSprite(m_livesLabel);
}

void BreakoutGameScene::Activate()
{
	m_bIsActive = true;
	m_statusChangeTime = SDL_GetTicks();
}

void BreakoutGameScene::Deactivate()
{
	m_bIsActive = false;
}

bool BreakoutGameScene::HandleEvent(IEvent *pEvent)
{
	if(!m_bIsActive)
		return false;

	if(pEvent->GetEventType() == ButtonInputEvent::cEventName)
	{
		ButtonInputEvent* pButtonEvent = (ButtonInputEvent*)pEvent;
		int key = pButtonEvent->GetKey();

		if(key == eKeyButtonStart && pButtonEvent->IsKeyDown())
		{
			BreakoutPauseGameEvent* pEvent = new BreakoutPauseGameEvent();
			EventManager::getSingleton()->QueueEvent(pEvent);

			return true;
		}

		if((key == eKeyButtonB || key == eKeyButtonX) && pButtonEvent->IsKeyDown())
		{
			if(m_pPaddle->IsBallAttached())
			{
				m_pPaddle->DetachBallFromPaddle();
				m_pBall->SetVelocity(Vector2i(BALL_START_SPEED_X, BALL_START_SPEED_Y));
			}

			return true;
		}

		return false;
	}
	
	return false;
}
