#include "const.h"
#include "struct.h"
#include "gamedirector.h"
#include <iostream>

const Uint8 *keystates;

//Calcula el cuadrado de la distancia entre dos puntos
double distanceSquared(int x1, int y1, int x2, int y2)
{
	int deltaX = x2 - x1;
	int deltaY = y2 - y1;
	return deltaX * deltaX + deltaY * deltaY;
}

//Detector de colisiones entre dos circulos
bool checkCollision(Circle &a, Circle &b)
{
	//Calcula el radio total al cuadrado
	int totalRadiusSquared = a.r + b.r;
	totalRadiusSquared = totalRadiusSquared * totalRadiusSquared;

	//Si la distancia entre el centro de los circulos es inferior a la suma de sus radios
	if (distanceSquared(a.x, a.y, b.x, b.y) < (totalRadiusSquared))
	{
		//Los circulos han colisionado
		return true;
	}

	//En caso contrario
	return false;
}

//Constructor
GameDirector::GameDirector()
{
	//Texturas
	mPlayerTexture = new LTexture();
	mGameBackgroundTexture = new LTexture();
	mTitleBackgroundTexture = new LTexture();
	mWhiteFontTexture = new LTexture();
	mNokiaFontTexture = new LTexture();
	mMenuTexture = new LTexture();
	mBalloonTexture = new LTexture();
	mBulletTexture = new LTexture();
	mMiscTexture = new LTexture();
	mIntroTexture = new LTexture();

	//Manejador de eventos
	mEventHandler = new SDL_Event();

	//Objetos para el juego
	mPlayer = new Player();
	for (Uint8 i = 0; i < MAX_BALLOONS; i++)
	{
		mBalloon[i] = new Balloon();
	}
	for (Uint8 i = 0; i < MAX_BULLETS; i++)
	{
		mBullet[i] = new Bullet();
	}
	mGameBackground = new Sprite();
	mTitleBackground = new Sprite();
	mWhiteText = new Text();
	mNewText = new Text();
	mMenuTitle = new Menu();
	mMenuPause = new Menu();
	mGetReady = new SmartSprite();
	mCoffeeBitmap = new SmartSprite();
	mCrisisBitmap = new SmartSprite();
	for (Uint8 i = 0; i < INTRO_TOTAL_BITMAPS; i++)
	{
		mIntroBitmap[i] = new SmartSprite();
	}
	for (Uint8 i = 0; i < INTRO_TOTAL_TEXTS; i++)
	{
		mIntroText[i] = new Text2();
	}
	init(false);
}

GameDirector::~GameDirector()
{
	close();

	//Destruye las texturas
	delete mPlayerTexture;
	delete mGameBackgroundTexture;
	delete mTitleBackgroundTexture;
	delete mWhiteFontTexture;
	delete mNokiaFontTexture;
	delete mMenuTexture;
	delete mBalloonTexture;
	delete mBulletTexture;
	delete mMiscTexture;
	delete mIntroTexture;

	//Destuye el manejador de eventos
	delete mEventHandler;

	//Destruye los objetos
	delete mPlayer;
	for (Uint8 i = 0; i < MAX_BALLOONS; i++)
	{
		delete mBalloon[i];
	}
	for (Uint8 i = 0; i < MAX_BULLETS; i++)
	{
		delete mBullet[i];
	}
	delete mGameBackground;
	delete mTitleBackground;
	delete mWhiteText;
	delete mNewText;
	delete mMenuTitle;
	delete mMenuPause;
	delete mGetReady;
	delete mCoffeeBitmap;
	delete mCrisisBitmap;
	for (Uint8 i = 0; i < INTRO_TOTAL_BITMAPS; i++)
	{
		delete mIntroBitmap[i];
	}
	for (Uint8 i = 0; i < INTRO_TOTAL_TEXTS; i++)
	{
		delete mIntroText[i];
	}
}

//Iniciador
void GameDirector::init(bool reset)
{
	if (true)
	{
		//Variables
		mOldTicks = 0;
		mGameSpeed = 15;
		mMenaceLevel = 0;
		mMenaceLevelThreshold = 0;
		mScore = 0;
		mHiScoreAchieved = false;
		mPlayFieldDrawOnly = true;
		mDebug = false;

		//Objeto jugador
		mPlayer->init(PLAY_AREA_CENTER_X - 12, PLAY_AREA_BOTTOM - 24, mPlayerTexture, mRenderer);

		//Establece a cero todos los valores del vector de objetos globo
		resetBalloons();

		//Crea objetos globo y los centra en el area de juego
		mBalloon[0]->init(0, BLOCK, BALLOON_4, BALLON_VELX_POSITIVE, 0, mBalloonTexture, mRenderer);
		mBalloon[0]->allignTo(PLAY_AREA_CENTER_X);

		//Con los globos creados, calcula el nivel de amenaza
		calculateMenaceLevel();

		//Establece a cero todos los valores del vector de objetos bala
		resetBullets();

		//Inicializa el bitmap de GetReady!
		mGetReady->init();
		mGetReady->setTexture(mMiscTexture);
		mGetReady->setRenderer(mRenderer);
		mGetReady->setPosX(-113);
		mGetReady->setPosY(PLAY_AREA_CENTER_Y - 10);
		mGetReady->setWidth(109);
		mGetReady->setHeight(20);
		mGetReady->setVelX(2.0f);
		mGetReady->setVelY(0.0f);
		mGetReady->setAccelX(0.0f);
		mGetReady->setAccelY(0.0f);
		mGetReady->setSpriteClip(0, 0, 109, 20);
		mGetReady->setEnabled(true);
		mGetReady->setEnabledTimer(0);
		mGetReady->setDestX(PLAY_AREA_RIGHT);
		mGetReady->setDestY(PLAY_AREA_CENTER_Y - 10);

		//Inicializa el bitmap de Coffee
		mCoffeeBitmap->init();
		mCoffeeBitmap->setTexture(mTitleBackgroundTexture);
		mCoffeeBitmap->setRenderer(mRenderer);
		mCoffeeBitmap->setPosX(45);
		mCoffeeBitmap->setPosY(11 - 200);
		mCoffeeBitmap->setWidth(167);
		mCoffeeBitmap->setHeight(46);
		mCoffeeBitmap->setVelX(0.0f);
		mCoffeeBitmap->setVelY(2.5f);
		mCoffeeBitmap->setAccelX(0.0f);
		mCoffeeBitmap->setAccelY(0.1f);
		mCoffeeBitmap->setSpriteClip(45, 11, 167, 46);
		mCoffeeBitmap->setEnabled(true);
		mCoffeeBitmap->setEnabledTimer(0);
		mCoffeeBitmap->setDestX(45);
		mCoffeeBitmap->setDestY(11);

		//Inicializa el bitmap de Crisis
		mCrisisBitmap->init();
		mCrisisBitmap->setTexture(mTitleBackgroundTexture);
		mCrisisBitmap->setRenderer(mRenderer);
		mCrisisBitmap->setPosX(60);
		mCrisisBitmap->setPosY(57 + 200);
		mCrisisBitmap->setWidth(137);
		mCrisisBitmap->setHeight(46);
		mCrisisBitmap->setVelX(0.0f);
		mCrisisBitmap->setVelY(-2.5f);
		mCrisisBitmap->setAccelX(0.0f);
		mCrisisBitmap->setAccelY(-0.1f);
		mCrisisBitmap->setSpriteClip(60, 57, 137, 46);
		mCrisisBitmap->setEnabled(true);
		mCrisisBitmap->setEnabledTimer(0);
		mCrisisBitmap->setDestX(60);
		mCrisisBitmap->setDestY(57);

		//Inicializa el vector de eventos de la intro
		for (Uint8 i = 0; i < INTRO_TOTAL_EVENTS; i++)
		{
			mIntroEvents[i] = EVENT_WAITING;
		}

		//Inicializa los bitmaps de la intro
		for (Uint8 i = 0; i < INTRO_TOTAL_BITMAPS; i++)
		{
			mIntroBitmap[i]->init();
			mIntroBitmap[i]->setId(i);
			mIntroBitmap[i]->setIntroEvents(&mIntroEvents[0]);
			mIntroBitmap[i]->setTexture(mIntroTexture);
			mIntroBitmap[i]->setRenderer(mRenderer);
			mIntroBitmap[i]->setWidth(128);
			mIntroBitmap[i]->setHeight(96);
			mIntroBitmap[i]->setEnabled(false);
			mIntroBitmap[i]->setEnabledTimer(20);
			mIntroBitmap[i]->setDestX(SCREEN_CENTER_X - 64);
			mIntroBitmap[i]->setDestY(SCREEN_FIRST_QUARTER_Y - 24);
		}

		mIntroBitmap[0]->setPosX(-128);
		mIntroBitmap[0]->setPosY(SCREEN_FIRST_QUARTER_Y - 24);
		mIntroBitmap[0]->setVelX(0.0f);
		mIntroBitmap[0]->setVelY(0.0f);
		mIntroBitmap[0]->setAccelX(0.6f);
		mIntroBitmap[0]->setAccelY(0.0f);
		mIntroBitmap[0]->setSpriteClip(0, 0, 128, 96);

		mIntroBitmap[1]->setPosX(SCREEN_WIDTH);
		mIntroBitmap[1]->setPosY(SCREEN_FIRST_QUARTER_Y - 24);
		mIntroBitmap[1]->setVelX(-1.0f);
		mIntroBitmap[1]->setVelY(0.0f);
		mIntroBitmap[1]->setAccelX(-0.3f);
		mIntroBitmap[1]->setAccelY(0.0f);
		mIntroBitmap[1]->setSpriteClip(128, 0, 128, 96);

		mIntroBitmap[2]->setPosX(SCREEN_CENTER_X - 64);
		mIntroBitmap[2]->setPosY(-96);
		mIntroBitmap[2]->setVelX(0.0f);
		mIntroBitmap[2]->setVelY(3.0f);
		mIntroBitmap[2]->setAccelX(0.1f);
		mIntroBitmap[2]->setAccelY(0.3f);
		mIntroBitmap[2]->setSpriteClip(0, 96, 128, 96);

		mIntroBitmap[2]->setEnabledTimer(250);

		mIntroBitmap[3]->setPosX(SCREEN_CENTER_X - 64);
		mIntroBitmap[3]->setPosY(SCREEN_HEIGHT);
		mIntroBitmap[3]->setVelX(0.0f);
		mIntroBitmap[3]->setVelY(-0.7f);
		mIntroBitmap[3]->setAccelX(0.0f);
		mIntroBitmap[3]->setAccelY(0.0f);
		mIntroBitmap[3]->setSpriteClip(128, 96, 128, 96);

		mIntroBitmap[4]->setPosX(SCREEN_CENTER_X - 64);
		mIntroBitmap[4]->setPosY(-96);
		mIntroBitmap[4]->setVelX(0.0f);
		mIntroBitmap[4]->setVelY(3.0f);
		mIntroBitmap[4]->setAccelX(0.1f);
		mIntroBitmap[4]->setAccelY(0.3f);
		mIntroBitmap[4]->setSpriteClip(0, 192, 128, 96);

		mIntroBitmap[5]->setPosX(SCREEN_WIDTH);
		mIntroBitmap[5]->setPosY(SCREEN_FIRST_QUARTER_Y - 24);
		mIntroBitmap[5]->setVelX(-0.7f);
		mIntroBitmap[5]->setVelY(0.0f);
		mIntroBitmap[5]->setAccelX(0.0f);
		mIntroBitmap[5]->setAccelY(0.0f);
		mIntroBitmap[5]->setSpriteClip(128, 192, 128, 96);

		//Inicializa los textos de la intro
		for (Uint8 i = 0; i < INTRO_TOTAL_TEXTS; i++)
		{
			mIntroText[i]->init(mNokiaFontTexture, mRenderer, TEXT_VARIABLE, 10);
			mIntroText[i]->setId(6 + i);
			mIntroText[i]->setIntroEvents(&mIntroEvents[0]);
			mIntroText[i]->setPosX(BLOCK * 0);
			mIntroText[i]->setPosY(SCREEN_HEIGHT - (BLOCK * 6));
			mIntroText[i]->setKerning(-1);
			mIntroText[i]->setEnabled(false);
			mIntroText[i]->setEnabledTimer(180);
		}

		mIntroText[0]->setCaption("Un dia qualsevol de l'any 2000");
		mIntroText[0]->setWrittingSpeed(10);

		mIntroText[1]->setCaption("Tot esta tranquil a la UPV");
		mIntroText[1]->setWrittingSpeed(10);

		mIntroText[2]->setCaption("Fins que un desaprensiu...");
		mIntroText[2]->setWrittingSpeed(15);

		mIntroText[3]->setCaption("HEY! ME ANE A FERME UN CORTAET...");
		mIntroText[3]->setWrittingSpeed(10);

		mIntroText[4]->setCaption("UAAAAAAAAAAAAA!!!");
		mIntroText[4]->setWrittingSpeed(1);

		mIntroText[5]->setCaption("Espera un moment...");
		mIntroText[5]->setWrittingSpeed(20);

		mIntroText[6]->setCaption("Si resulta que no tinc solt!");
		mIntroText[6]->setWrittingSpeed(2);

		mIntroText[7]->setCaption("MERDA DE MAQUINA!");
		mIntroText[7]->setWrittingSpeed(3);

		mIntroText[8]->setCaption("Blop..blop..blop..");
		mIntroText[8]->setWrittingSpeed(25);

		for (Uint8 i = 0; i < INTRO_TOTAL_TEXTS; i++)
		{
			mIntroText[i]->center(SCREEN_CENTER_X);
		}
	}

	if (reset == false)
	{
		//Variables
		mGameStatus = GAME_STATE_INTRO;

		//Carga la máxima puntuación a partir del fichero de datos
		if (mDataFile[0] == 0)
		{
			mHiScore = 10000;
		}
		//Comprueba el checksum para ver si se ha modificado el fichero
		else if (mDataFile[0] % 43 == mDataFile[1])
		{
			mHiScore = mDataFile[0];
		}
		else
		{
			mHiScore = 10000;
		}

		//Los fondos
		mGameBackground->init(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, mGameBackgroundTexture, mRenderer);
		mTitleBackground->init(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, mTitleBackgroundTexture, mRenderer);

		//Objetos texto, uno de cada color
		mWhiteText->init(mWhiteFontTexture, mRenderer, TEXT_FIXED, 8);
		mNewText->init(mNokiaFontTexture, mRenderer, TEXT_VARIABLE, 10);

		//Inicializa el objeto con el menu del titulo
		mMenuTitle->init(0, 16 * BLOCK, MENU_BACKGROUND_TRANSPARENT, mMenuTexture, mRenderer, mWhiteText);
		mMenuTitle->addItem("START");
		mMenuTitle->addItem("EXIT");
		mMenuTitle->setBackgroundColor(0, 0, 0, 255);
		mMenuTitle->centerMenu(SCREEN_CENTER_X);
		mMenuTitle->centerMenuElements(SCREEN_CENTER_X);

		//Inicializa el objeto con el menu de pausa
		mMenuPause->init(0, 12 * BLOCK, MENU_BACKGROUND_SOLID, mMenuTexture, mRenderer, mWhiteText);
		mMenuPause->addItem("CONTINUE");
		mMenuPause->addItem("EXIT TO TITLE");
		mMenuPause->setBackgroundColor(0x73, 0x27, 0x5c, 255);
		mMenuPause->centerMenu(SCREEN_CENTER_X);
	}
}

//Arranca SDL y crea la ventana
bool GameDirector::initSDL()
{
	//Indicador de inicialización
	bool success = true;

	//Inicializa SDL
	if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_AUDIO) < 0)
	{
		printf("SDL could not initialize! SDL Error: %s\n", SDL_GetError());
		success = false;
	}
	else
	{
		//Establece el filtro de la textura a nearest
		if (!SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"))
		{
			printf("Warning: Nearest texture filtering not enabled!");
		}

		//Inicializa SDL_mixer
		if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048) < 0)
		{
			printf("SDL_mixer could not initialize! SDL_mixer Error: %s\n", Mix_GetError());
			success = false;
		}

		//Comprueba los mandos
		if (SDL_NumJoysticks() < 1)
		{
			printf("Warning: No joysticks connected!\n");
		}
		else
		{
			//Carga el mando
			gGameController = SDL_JoystickOpen(0);
			if (gGameController == NULL)
			{
				printf("Warning: Unable to open game controller! SDL Error: %s\n", SDL_GetError());
			}
			printf("%i joysticks were found.\n", SDL_NumJoysticks());
			std::cout << SDL_JoystickNumButtons(gGameController) << " buttons\n";
		}

		//Crea la ventana
		mWindow = SDL_CreateWindow("Coffe Crisis", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, VIEW_WIDTH, VIEW_HEIGHT, SDL_WINDOW_SHOWN);
		if (mWindow == NULL)
		{
			printf("Window could not be created! SDL Error: %s\n", SDL_GetError());
			success = false;
		}
		else
		{
			//Crea un renderizador para la ventana con vsync
			mRenderer = SDL_CreateRenderer(mWindow, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
			if (mRenderer == NULL)
			{
				printf("Renderer could not be created! SDL Error: %s\n", SDL_GetError());
				success = false;
			}
			else
			{
				//Inicializa el color de renderizado
				SDL_SetRenderDrawColor(mRenderer, 0x00, 0x00, 0x00, 0xFF);

				//Establece el tamaño del buffer de renderizado
				SDL_RenderSetLogicalSize(mRenderer, SCREEN_WIDTH, SCREEN_HEIGHT);

				//Inicializa el cargador de PNG
				int imgFlags = IMG_INIT_PNG;
				if (!(IMG_Init(imgFlags) & imgFlags))
				{
					printf("SDL_image could not initialize! SDL_image Error: %s\n", IMG_GetError());
					success = false;
				}
			}
		}
	}

	return success;
}

//Carga todos los recursos
bool GameDirector::loadMedia()
{
	//Indicador de éxito en la carga
	bool success = true;

	//Abrimos el fichero para leer en binario
	SDL_RWops *file = SDL_RWFromFile("data.bin", "r+b");

	//El fichero no existe
	if (file == NULL)
	{
		printf("Warning: Unable to open data.bin file! SDL Error: %s\n", SDL_GetError());

		//Creamos el fichero para escritura
		file = SDL_RWFromFile("data.bin", "w+b");
		if (file != NULL)
		{
			printf("New file created!\n");

			//Inicializamos los datos
			for (int i = 0; i < TOTAL_DATA; ++i)
			{
				mDataFile[i] = 0;
				SDL_RWwrite(file, &mDataFile[i], sizeof(Uint32), 1);
			}

			//Cerramos el fichero
			SDL_RWclose(file);
		}
		else
		{
			printf("Error: Unable to create file! SDL Error: %s\n", SDL_GetError());
			success = false;
		}
	}
	//El fichero existe
	else
	{
		//Cargamos los datos
		printf("Reading file...!\n");
		for (int i = 0; i < TOTAL_DATA; ++i)
		{
			SDL_RWread(file, &mDataFile[i], sizeof(Uint32), 1);
		}

		//Cierra el fichero
		SDL_RWclose(file);
	}

	//Carga los gráficos del jugador
	if (!mPlayerTexture->loadFromFile("media/gfx/player.png", mRenderer))
	{
		printf("Failed to load mPlayer texture!\n");
		success = false;
	}

	//Carga los gráficos de los globos
	if (!mBalloonTexture->loadFromFile("media/gfx/balloon.png", mRenderer))
	{
		printf("Failed to load mBalloon texture!\n");
		success = false;
	}

	//Carga los gráficos de las balas
	if (!mBulletTexture->loadFromFile("media/gfx/bullet.png", mRenderer))
	{
		printf("Failed to load mBullet texture!\n");
		success = false;
	}

	//Carga los gráficos del fondo del juego
	if (!mGameBackgroundTexture->loadFromFile("media/gfx/background.png", mRenderer))
	{
		printf("Failed to load game background texture!\n");
		success = false;
	}

	//Carga los gráficos del fondo de la pantalla de titulo
	if (!mTitleBackgroundTexture->loadFromFile("media/gfx/title.png", mRenderer))
	{
		printf("Failed to load title texture!\n");
		success = false;
	}

	//Carga varios gráficos para varios propósitos
	if (!mMiscTexture->loadFromFile("media/gfx/misc.png", mRenderer))
	{
		printf("Failed to load misc texture!\n");
		success = false;
	}

	//Carga los gráficos para el menu
	if (!mMenuTexture->loadFromFile("media/gfx/menu.png", mRenderer))
	{
		printf("Failed to load menu texture!\n");
		success = false;
	}

	//Carga los gráficos para el texto blanco
	if (!mWhiteFontTexture->loadFromFile("media/gfx/whitefont2.png", mRenderer))
	{
		printf("Failed to load white font texture!\n");
		success = false;
	}

	//Carga los gráficos para el texto negro
	if (!mNokiaFontTexture->loadFromFile("media/gfx/nokiafont.png", mRenderer))
	{
		printf("Failed to load black font texture!\n");
		success = false;
	}

	//Carga los gráficos para el texto negro
	if (!mIntroTexture->loadFromFile("media/gfx/intro.png", mRenderer))
	{
		printf("Failed to load intro texture!\n");
		success = false;
	}

	//Carga la música del titulo
	mTitleMusic = Mix_LoadMUS("media/music/title.ogg");
	if (mTitleMusic == NULL)
	{
		printf("Failed to load title music! SDL_mixer Error: %s\n", Mix_GetError());
		success = false;
	}

	//Carga la música del juego
	mPlayingMusic = Mix_LoadMUS("media/music/playing.ogg");
	if (mPlayingMusic == NULL)
	{
		printf("Failed to load playing music! SDL_mixer Error: %s\n", Mix_GetError());
		success = false;
	}

	//Carga los efectos de sonido para la explosión de los globos
	mPopBalloonFX = Mix_LoadWAV("media/sound/balloon.wav");
	if (mPopBalloonFX == NULL)
	{
		printf("Failed to load mBalloon sound effect! SDL_mixer Error: %s\n", Mix_GetError());
		success = false;
	}

	//Carga los efectos de sonido para los disparos del jugador
	mBulletFX = Mix_LoadWAV("media/sound/bullet.wav");
	if (mBulletFX == NULL)
	{
		printf("Failed to load mBullet sound effect! SDL_mixer Error: %s\n", Mix_GetError());
		success = false;
	}

	//Carga los efectos de sonido para el menu
	mMenuSelectedFX = Mix_LoadWAV("media/sound/menu_selected.wav");
	if (mMenuSelectedFX == NULL)
	{
		printf("Failed to load menu selected sound effect! SDL_mixer Error: %s\n", Mix_GetError());
		success = false;
	}

	//Carga los efectos de sonido para los disparos del jugador
	mPlayerCollisionFX = Mix_LoadWAV("media/sound/player_collision.wav");
	if (mPlayerCollisionFX == NULL)
	{
		printf("Failed to load mPlayer collision sound effect! SDL_mixer Error: %s\n", Mix_GetError());
		success = false;
	}

	//Carga los efectos de sonido para la puntuación máxima
	mHiScoreFX = Mix_LoadWAV("media/sound/hiscore.wav");
	if (mHiScoreFX == NULL)
	{
		printf("Failed to load hiscore sound effect! SDL_mixer Error: %s\n", Mix_GetError());
		success = false;
	}

	return success;
}

//Libera todos los recursos y cierra SDL
void GameDirector::close()
{
	//Abre el fichero para escribir
	SDL_RWops *file = SDL_RWFromFile("data.bin", "w+b");
	if (file != NULL)
	{
		//Guardamos los datos
		for (int i = 0; i < TOTAL_DATA; ++i)
		{
			SDL_RWwrite(file, &mDataFile[i], sizeof(Uint32), 1);
		}

		//Cerramos el fichero
		SDL_RWclose(file);
	}
	else
	{
		printf("Error: Unable to save file! %s\n", SDL_GetError());
	}

	//Libera todas las imagenes
	mPlayerTexture->free();
	mGameBackgroundTexture->free();
	mTitleBackgroundTexture->free();
	mWhiteFontTexture->free();
	mNokiaFontTexture->free();
	mMenuTexture->free();
	mBalloonTexture->free();
	mMiscTexture->free();

	//Libera los efectos de sonido
	Mix_FreeChunk(mPopBalloonFX);
	mPopBalloonFX = NULL;
	Mix_FreeChunk(mBulletFX);
	mBulletFX = NULL;
	Mix_FreeChunk(mMenuSelectedFX);
	mMenuSelectedFX = NULL;
	Mix_FreeChunk(mPlayerCollisionFX);
	mPlayerCollisionFX = NULL;
	Mix_FreeChunk(mHiScoreFX);
	mHiScoreFX = NULL;

	//Libra la música
	Mix_FreeMusic(mTitleMusic);
	mTitleMusic = NULL;
	Mix_FreeMusic(mPlayingMusic);
	mPlayingMusic = NULL;

	//Libera el mando
	SDL_JoystickClose(gGameController);
	gGameController = NULL;

	//Destruye la ventana
	SDL_DestroyRenderer(mRenderer);
	SDL_DestroyWindow(mWindow);
	mWindow = NULL;
	mRenderer = NULL;

	//Sal del subsistema SDL
	Mix_Quit();
	IMG_Quit();
	SDL_Quit();
}

//Establece el valor de la variable
void GameDirector::setScore(Uint32 score)
{
	mScore = score;
}

//Establece el valor de la variable
void GameDirector::setHiScore(Uint32 score)
{
	mHiScore = score;
}

//Actualiza el valor de HiScore en caso necesario
void GameDirector::updateHiScore()
{
	//La puntuación es mayor que la máxima puntuación
	if (mScore > mHiScore)
	{
		//Actualiza la máxima puntuación
		mHiScore = mScore;

		//Almacena la máxima puntuación en el fichero junto con un checksum
		mDataFile[0] = mHiScore;
		mDataFile[1] = mHiScore % 43;

		//Si superamos la máxima puntuación
		if (mHiScoreAchieved == false)
		{
			mHiScoreAchieved = true;
			Mix_PlayChannel(-1, mHiScoreFX, 0);
		}
	}
}

//Transforma un valor numérico en una cadena de 6 cifras
std::string GameDirector::updateScoreText(Uint32 num)
{
	if ((num >= 0) && (num <= 9))
	{
		return ("00000" + std::to_string(num));
	}

	if ((num >= 10) && (num <= 99))
	{
		return ("0000" + std::to_string(num));
	}

	if ((num >= 100) && (num <= 999))
	{
		return ("000" + std::to_string(num));
	}

	if ((num >= 1000) && (num <= 9999))
	{
		return ("00" + std::to_string(num));
	}

	if ((num >= 010000) && (num <= 99999))
	{
		return ("0" + std::to_string(num));
	}

	if ((num >= 100000) && (num <= 999999))
	{
		return (std::to_string(num));
	}

	return (std::to_string(num));
}

//Pinta el marcador en pantalla usando un objeto texto
void GameDirector::renderScoreBoard()
{
	mWhiteText->write(SCORE_WORD_X, SCORE_WORD_Y, "SCORE", 0);
	mWhiteText->write(SCORE_NUMBER_X, SCORE_NUMBER_Y, updateScoreText(mScore), 0);

	mWhiteText->write(MULTIPLIER_WORD_X, MULTIPLIER_WORD_Y, "MULT", 0);
	mWhiteText->write(MULTIPLIER_NUMBER_X, MULTIPLIER_NUMBER_Y, std::to_string(mPlayer->getScoreMultiplier()), 0, 3);

	mWhiteText->write(HISCORE_WORD_X, HISCORE_WORD_Y, "HI-SCORE", 0);
	mWhiteText->write(HISCORE_NUMBER_X, HISCORE_NUMBER_Y, updateScoreText(mHiScore), 0);
}

//Mueve todos los globos activos
void GameDirector::moveBalloons()
{
	for (Uint8 i = 0; i < MAX_BALLOONS; i++)
	{
		if (mBalloon[i]->isActive())
		{
			mBalloon[i]->update();
		}
	}
}

//Pinta en pantalla todos los globos activos
void GameDirector::renderBalloons()
{
	for (Uint8 i = 0; i < MAX_BALLOONS; i++)
	{
		if (mBalloon[i]->isActive())
		{
			mBalloon[i]->render();
		}
	}
}

//Devuelve el primer indice no activo del vector de globos
Uint8 GameDirector::getBallonFreeIndex()
{
	int index = 0;

	for (Uint8 i = 0; i < MAX_BALLOONS; i++)
	{
		if (mBalloon[i]->isActive() == false)
		{
			index = i;
			break;
		}
	}

	return index;
}

//Crea un globo nuevo en el vector de globos
Uint8 GameDirector::createNewBalloon(float x, int y, Uint8 kind, float velx, Uint16 creationtimer, LTexture *texture)
{
	Uint8 index = getBallonFreeIndex();
	mBalloon[index]->init(x, y, kind, velx, creationtimer, texture, mRenderer);
	return index;
}

//Establece a cero todos los valores del vector de objetos globo
void GameDirector::resetBalloons()
{
	for (Uint8 i = 0; i < MAX_BALLOONS; i++)
	{
		mBalloon[i]->erase();
	}
}

//Explosiona un globo. Lo destruye y crea otros dos si es el caso
void GameDirector::popBalloon(Uint8 index)
{
	if (mBalloon[index]->isActive())
	{
		Uint8 kind = mBalloon[index]->getKind();
		Uint8 freeIndex = 0;
		switch (kind)
		{
		//Si es del tipo más pequeño, simplemente elimina el globo
		case BALLOON_1:
			mBalloon[index]->pop();
			break;

		//En cualquier otro caso, crea dos globos de un tipo inferior
		default:
			freeIndex = getBallonFreeIndex();
			mBalloon[freeIndex]->init(0, mBalloon[index]->getPosY(), mBalloon[index]->getKind() - 1, BALLON_VELX_NEGATIVE, 0, mBalloonTexture, mRenderer);
			mBalloon[freeIndex]->allignTo(mBalloon[index]->getPosX() + (mBalloon[index]->getWidth() / 2));
			mBalloon[freeIndex]->setVelY(-2.5);

			freeIndex = getBallonFreeIndex();
			mBalloon[freeIndex]->init(0, mBalloon[index]->getPosY(), mBalloon[index]->getKind() - 1, BALLON_VELX_POSITIVE, 0, mBalloonTexture, mRenderer);
			mBalloon[freeIndex]->allignTo(mBalloon[index]->getPosX() + (mBalloon[index]->getWidth() / 2));
			mBalloon[freeIndex]->setVelY(-2.5);

			//Elimina el globo
			mBalloon[index]->pop();
			break;
		}
	}
}

//Detiene todos los globos
void GameDirector::stopAllBalloons(Uint16 time)
{
	for (Uint8 i = 0; i < MAX_BALLOONS; i++)
	{
		if (mBalloon[i]->isActive())
		{
			mBalloon[i]->setStop(true);
			mBalloon[i]->setStoppedTimer(time);
		}
	}
}

//Pone en marcha todos los globos
void GameDirector::startAllBalloons()
{
	for (Uint8 i = 0; i < MAX_BALLOONS; i++)
	{
		if (mBalloon[i]->isActive())
		{
			mBalloon[i]->setStop(false);
			mBalloon[i]->setStoppedTimer(0);
		}
	}
}

//Obtiene el numero de globos activos
Uint8 GameDirector::countBalloons()
{
	Uint8 num = 0;
	for (Uint8 i = 0; i < MAX_BALLOONS; i++)
	{
		if (mBalloon[i]->isActive())
		{
			++num;
		}
	}
	return num;
}

//Comprueba la colisión entre el jugador y los globos activos
bool GameDirector::checkPlayerBallonCollision()
{
	bool result = false;
	for (Uint8 i = 0; i < MAX_BALLOONS; i++)
	{
		if ((mBalloon[i]->isActive()) && (mBalloon[i]->isStopped() == false))
		{
			if (checkCollision(mPlayer->getCollider(), mBalloon[i]->getCollider()))
			{
				result = true;
				break;
			}
		}
	}
	return result;
}

//Comprueba y procesa la colisión entre las balas y los globos
void GameDirector::processBulletBallonCollision()
{
	for (Uint8 i = 0; i < MAX_BALLOONS; i++)
	{
		for (Uint8 j = 0; j < MAX_BULLETS; j++)
		{
			if (mBalloon[i]->isActive() && !(mBalloon[i]->isInvulnerable()) && mBullet[j]->isActive())
			{
				if (checkCollision(mBalloon[i]->getCollider(), mBullet[j]->getCollider()))
				{
					mPlayer->addScore(Uint32(mBalloon[i]->getScore() * mPlayer->getScoreMultiplier()));
					mPlayer->incScoreMultiplier();
					setScore(mPlayer->getScore());
					updateHiScore();
					popBalloon(i);
					Mix_PlayChannel(-1, mPopBalloonFX, 0);
					mBullet[j]->erase();
					calculateMenaceLevel();
					break;
				}
			}
		}
	}
}

//Mueve las balas activas
void GameDirector::moveBullets()
{
	for (Uint8 i = 0; i < MAX_BULLETS; i++)
	{
		if (mBullet[i]->isActive())
		{
			if (mBullet[i]->move() == MSG_BULLET_OUT)
			{
				mPlayer->decScoreMultiplier();
			}
		}
	}
}

//Pinta las balas activas
void GameDirector::renderBullets()
{
	for (Uint8 i = 0; i < MAX_BULLETS; i++)
	{
		if (mBullet[i]->isActive())
		{
			mBullet[i]->render();
		}
	}
}

//Devuelve el primer indice no activo del vector de balas
Uint8 GameDirector::getBulletFreeIndex()
{
	Uint8 index = 0;

	for (int i = 0; i < MAX_BULLETS; i++)
	{
		if (mBullet[i]->isActive() == false)
		{
			index = i;
			break;
		}
	}

	return index;
}

//Establece a cero todos los valores del vector de objetos bala
void GameDirector::resetBullets()
{
	for (Uint8 i = 0; i < MAX_BULLETS; i++)
	{
		mBullet[i]->erase();
	}
}

//Crea un objeto bala
void GameDirector::createBullet(int x, int y, Uint8 kind)
{
	mBullet[getBulletFreeIndex()]->init(x, y, kind, mBulletTexture, mRenderer);
}

//Acciones a realizar cuando el jugador muere
void GameDirector::killPlayer()
{
	stopAllBalloons(5000);
	Mix_HaltMusic();
	Mix_PlayChannel(-1, mPlayerCollisionFX, 0);
	shakeScreen();
	mPlayer->setAlive(false);
	SDL_Delay(1000);
	setGameStatus(GAME_STATE_GAME_OVER_SCREEN);
}

//Calcula y establece el valor de amenaza en funcion de los globos activos
void GameDirector::calculateMenaceLevel()
{
	mMenaceLevel = 0;
	for (Uint8 i = 0; i < MAX_BALLOONS; i++)
	{
		mMenaceLevel += mBalloon[i]->getMenace();
	}
}

//Obtiene el valor de la variable
Uint8 GameDirector::getMenaceLevel()
{
	return mMenaceLevel;
}

//Obtiene el valor de la variable
bool GameDirector::isPlayFieldDrawOnly()
{
	return mPlayFieldDrawOnly;
}

//Establece el valor de la variable
void GameDirector::setPlayFieldDrawOnly(bool state)
{
	mPlayFieldDrawOnly = state;
}

//Gestiona el nivel de amenaza
void GameDirector::checkMenaceLevel()
{
	//Aumenta el nivel de amenaza en función de la puntuación
	mMenaceLevelThreshold = 7 + (4 * (mScore / 10000));

	//Si el nivel de amenza es inferior al umbral
	if (mMenaceLevel < mMenaceLevelThreshold)
	{
		Uint8 index = 0;

		//Obtiene el centro del jugador en el eje X
		int x = mPlayer->getPosX() + (mPlayer->getWidth() / 2);

		//Crea un globo sobre el jugador en dirección hacia el centro
		if (x < (PLAY_AREA_WIDTH / 2))
		{
			index = createNewBalloon(0, PLAY_AREA_TOP + BLOCK - 37, BALLOON_4, BALLON_VELX_POSITIVE, 400, mBalloonTexture);
		}
		else
		{
			index = createNewBalloon(0, PLAY_AREA_TOP + BLOCK - 37, BALLOON_4, BALLON_VELX_NEGATIVE, 400, mBalloonTexture);
		}
		mBalloon[index]->allignTo(x);

		//Recalcula el nivel de amenaza con el nuevo globo
		calculateMenaceLevel();
	}
}

//Gestiona la entrada de teclado y mando durante el juego
void GameDirector::checkGameInput()
{
	//Obtiene el estado de las teclas pulsadas del teclado
	keystates = SDL_GetKeyboardState(NULL);

	//Si está pulsada la tecla izquierda o el mando hacia la izquierda
	if ((keystates[SDL_SCANCODE_LEFT] != 0) || (SDL_JoystickGetAxis(gGameController, 0) < -JOYSTICK_DEAD_ZONE))
	{
		mPlayer->checkInput(INPUT_LEFT);
	}
	//Si está pulsada la tecla derecha o el mando hacia la derecha
	else if ((keystates[SDL_SCANCODE_RIGHT] != 0) || (SDL_JoystickGetAxis(gGameController, 0) > JOYSTICK_DEAD_ZONE))
	{
		mPlayer->checkInput(INPUT_RIGHT);
	}
	//Ninguna de las dos direcciones pulsadas
	else
	{
		mPlayer->checkInput(NO_INPUT);
	}

	//Comprobamos la tecla o el botón de disparo central
	if ((SDL_JoystickGetButton(gGameController, BUTTON_X)) || (keystates[SDL_SCANCODE_W] != 0))
	{
		if (mPlayer->canFire())
		{
			createBullet(mPlayer->getPosX() + (mPlayer->getWidth() / 2) - 4, mPlayer->getPosY(), BULLET_UP);
			mPlayer->setFireCooldown(10);

			//Reproduce el sonido de disparo
			Mix_PlayChannel(-1, mBulletFX, 0);
		}
	}

	//Comprobamos la tecla o el botón de disparo izquierdo
	if ((SDL_JoystickGetButton(gGameController, BUTTON_Y)) || (keystates[SDL_SCANCODE_Q] != 0))
	{
		if (mPlayer->canFire())
		{
			createBullet(mPlayer->getPosX() + (mPlayer->getWidth() / 2) - 4, mPlayer->getPosY(), BULLET_LEFT);
			mPlayer->setFireCooldown(10);

			//Reproduce el sonido de disparo
			Mix_PlayChannel(-1, mBulletFX, 0);
		}
	}

	//Comprobamos la tecla o el botón de disparo derecho
	if ((SDL_JoystickGetButton(gGameController, BUTTON_A)) || (keystates[SDL_SCANCODE_E] != 0))
	{
		if (mPlayer->canFire())
		{
			createBullet(mPlayer->getPosX() + (mPlayer->getWidth() / 2) - 4, mPlayer->getPosY(), BULLET_RIGHT);
			mPlayer->setFireCooldown(10);

			//Reproduce el sonido de disparo
			Mix_PlayChannel(-1, mBulletFX, 0);
		}
	}

	//Comprobamos la tecla o el botón de pausa/menu
	if ((SDL_JoystickGetButton(gGameController, BUTTON_START)) || (keystates[SDL_SCANCODE_ESCAPE] != 0))
	{
		setGameStatus(GAME_STATE_PAUSED);

		//Detiene la música
		Mix_HaltMusic();
	}
}

//Gestiona la entrada de teclado y mando durante el menu
void GameDirector::checkMenuInput(Menu *menu)
{
	//Obtiene el estado de las teclas pulsadas del teclado
	keystates = SDL_GetKeyboardState(NULL);

	//Si está pulsada la tecla izquierda o el mando hacia la izquierda
	if ((keystates[SDL_SCANCODE_UP] != 0) || (SDL_JoystickGetAxis(gGameController, 1) < -JOYSTICK_DEAD_ZONE))
	{
		menu->checkInput(INPUT_UP);
	}
	//Si está pulsada la tecla derecha o el mando hacia la derecha
	else if ((keystates[SDL_SCANCODE_DOWN] != 0) || (SDL_JoystickGetAxis(gGameController, 1) > JOYSTICK_DEAD_ZONE))
	{
		menu->checkInput(INPUT_DOWN);
	}
	//Comprobamos la tecla o el botón de menu/pausa
	else if (keystates[SDL_SCANCODE_RETURN] != 0 || (SDL_JoystickGetButton(gGameController, BUTTON_A)))
	{
		menu->checkInput(INPUT_FIRE);
	}
}

//Obtiene el valor de la variable
Uint8 GameDirector::getGameStatus()
{
	return mGameStatus;
}

//Establece el valor de la variable
void GameDirector::setGameStatus(Uint8 status)
{
	mGameStatus = status;
}

//Pinta una transición en pantalla
void GameDirector::renderTransition(Uint8 index)
{
	switch (index)
	{
	case 0:
		SDL_Rect rect;
		rect.x = 0;
		rect.y = 0;
		rect.w = SCREEN_WIDTH;
		rect.h = 0;

		SDL_RenderPresent(mRenderer);
		SDL_SetRenderDrawColor(mRenderer, 0x27, 0x27, 0x36, 0);

		for (Uint16 i = 0; i < SCREEN_HEIGHT; i = i + 2)
		{
			rect.h = i;
			SDL_RenderFillRect(mRenderer, &rect);
			SDL_RenderPresent(mRenderer);
		}
		break;

	case 1:
		SDL_Rect rect1;
		rect1.x = 0;
		rect1.y = 0;
		rect1.w = SCREEN_WIDTH;
		rect1.h = 0;

		SDL_Rect rect2;
		rect2.x = 0;
		rect2.y = 0;
		rect2.w = SCREEN_WIDTH;
		rect2.h = 0;

		SDL_RenderPresent(mRenderer);
		SDL_SetRenderDrawBlendMode(mRenderer, SDL_BLENDMODE_BLEND);
		SDL_SetRenderDrawColor(mRenderer, 0x27, 0x27, 0x36, 64);

		for (Uint16 i = 0; i < (SCREEN_HEIGHT / 2); i = i + 4)
		{
			rect1.h = i;
			SDL_RenderFillRect(mRenderer, &rect1);

			rect2.h = i;
			rect2.y = SCREEN_HEIGHT - (i);
			SDL_RenderFillRect(mRenderer, &rect2);

			SDL_RenderPresent(mRenderer);
		}

		rect1.x = 0;
		rect1.y = 0;
		rect1.w = SCREEN_WIDTH;
		rect1.h = SCREEN_HEIGHT;
		SDL_SetRenderDrawColor(mRenderer, 0, 0, 0, 64);

		for (Uint16 i = 0; i < 32; i++)
		{
			SDL_RenderFillRect(mRenderer, &rect1);
			SDL_RenderPresent(mRenderer);
		}
		break;

	default:
		break;
	}
}

//Pinta el texto GetReady en pantalla
void GameDirector::renderGetReady()
{
	if (mGetReady->isEnabled())
	{
		mGetReady->update();
		mGetReady->render();
		if (mGetReady->isOnDestination())
		{
			setPlayFieldDrawOnly(false);
			mGetReady->setEnabled(false);
		}
	}
}

//Agita la pantalla
void GameDirector::shakeScreen()
{
	int v[] = {-1, 1, -1, 1, -1, 1, -1, 0};
	for (Uint8 n = 0; n < 8; n++)
	{
		//Limpia la pantalla
		SDL_SetRenderDrawColor(mRenderer, 0x00, 0x00, 0x00, 0xFF);
		SDL_RenderClear(mRenderer);

		//Dibuja los objetos
		mGameBackground->setPosX(v[n]);
		mGameBackground->render();
		renderBalloons();
		renderBullets();
		mPlayer->render();
		renderScoreBoard();

		//Actualiza la pantalla
		SDL_RenderPresent(mRenderer);
		SDL_Delay(50);
	}
}

//Bucle para la intro del juego
void GameDirector::runIntro()
{
	if (SDL_GetTicks() - mOldTicks > mGameSpeed)
	{
		//Actualiza el contador de ticks
		mOldTicks = SDL_GetTicks();

		//Comprueba los eventos que hay en la cola
		while (SDL_PollEvent(mEventHandler) != 0)
		{
			//Evento de salida de la aplicación
			if (mEventHandler->type == SDL_QUIT)
			{
				setGameStatus(GAME_STATE_QUIT);
				break;
			}

			//Tecla ESPACIO pulsada
			if (mEventHandler->key.keysym.sym == SDLK_SPACE)
			{
				setGameStatus(GAME_STATE_TITLE);
				break;
			}
		}

		//Actualiza los objetos
		for (Uint8 i = 0; i < INTRO_TOTAL_BITMAPS; i++)
		{
			mIntroBitmap[i]->update();
		}
		for (Uint8 i = 0; i < INTRO_TOTAL_TEXTS; i++)
		{
			mIntroText[i]->update();
		}

		//Guión de eventos
		//Primera imagen - UPV
		if (mIntroEvents[BITMAP0] == EVENT_WAITING)
		{
			mIntroBitmap[0]->setEnabled(true);
			mIntroEvents[BITMAP0] = EVENT_RUNNING;
		}

		//Primer texto de la primera imagen
		if ((mIntroEvents[BITMAP0] == EVENT_COMPLETED) && (mIntroEvents[TEXT0] == EVENT_WAITING))
		{
			mIntroText[0]->setEnabled(true);
			mIntroEvents[TEXT0] = EVENT_RUNNING;
		}

		//Segundo texto de la primera imagen
		if ((mIntroEvents[TEXT0] == EVENT_COMPLETED) && (mIntroEvents[TEXT1] == EVENT_WAITING))
		{
			mIntroText[0]->setEnabled(false);
			mIntroText[1]->setEnabled(true);
			mIntroEvents[TEXT1] = EVENT_RUNNING;
		}

		//Tercer texto de la primera imagen
		if ((mIntroEvents[TEXT1] == EVENT_COMPLETED) && (mIntroEvents[TEXT2] == EVENT_WAITING))
		{
			mIntroText[1]->setEnabled(false);
			mIntroText[2]->setEnabled(true);
			mIntroEvents[TEXT2] = EVENT_RUNNING;
		}

		//Segunda imagen - Máquina
		if ((mIntroEvents[TEXT2] == EVENT_COMPLETED) && (mIntroEvents[BITMAP1] == EVENT_WAITING))
		{
			mIntroBitmap[0]->setEnabled(false);
			mIntroText[2]->setEnabled(false);
			mIntroBitmap[1]->setEnabled(true);
			mIntroEvents[BITMAP1] = EVENT_RUNNING;
		}

		//Primer texto de la segunda imagen
		if ((mIntroEvents[BITMAP1] == EVENT_COMPLETED) && (mIntroEvents[TEXT3] == EVENT_WAITING))
		{
			mIntroText[3]->setEnabled(true);
			mIntroEvents[TEXT3] = EVENT_RUNNING;
		}

		//Tercera imagen junto con primer texto - GRITO
		if ((mIntroEvents[TEXT3] == EVENT_COMPLETED) && (mIntroEvents[BITMAP2] == EVENT_WAITING) && (mIntroEvents[TEXT4] == EVENT_WAITING))
		{
			mIntroBitmap[1]->setEnabled(false);
			mIntroText[3]->setEnabled(false);
			mIntroBitmap[2]->setEnabled(true);
			mIntroText[4]->setEnabled(true);
			mIntroEvents[BITMAP2] = EVENT_RUNNING;
			mIntroEvents[TEXT4] = EVENT_RUNNING;
		}

		//Cuarta imagen junto con primer texto - Reflexión
		if ((mIntroEvents[TEXT4] == EVENT_COMPLETED) && (mIntroEvents[BITMAP3] == EVENT_WAITING) && (mIntroEvents[TEXT5] == EVENT_WAITING))
		{
			mIntroBitmap[2]->setEnabled(false);
			mIntroText[4]->setEnabled(false);
			mIntroBitmap[3]->setEnabled(true);
			mIntroText[5]->setEnabled(true);
			mIntroEvents[BITMAP3] = EVENT_RUNNING;
			mIntroEvents[TEXT5] = EVENT_RUNNING;
		}

		//Segundo texto de la cuarta imagen
		if ((mIntroEvents[TEXT5] == EVENT_COMPLETED) && (mIntroEvents[TEXT6] == EVENT_WAITING))
		{
			mIntroText[5]->setEnabled(false);
			mIntroText[6]->setEnabled(true);
			mIntroEvents[TEXT6] = EVENT_RUNNING;
		}

		//Quinta imagen - Patada
		if ((mIntroEvents[TEXT6] == EVENT_COMPLETED) && (mIntroEvents[BITMAP4] == EVENT_WAITING))
		{
			mIntroBitmap[3]->setEnabled(false);
			mIntroText[6]->setEnabled(false);
			mIntroBitmap[4]->setEnabled(true);
			mIntroEvents[BITMAP4] = EVENT_RUNNING;
		}

		//Primer texto de la quinta imagen
		if ((mIntroEvents[BITMAP4] == EVENT_COMPLETED) && (mIntroEvents[TEXT7] == EVENT_WAITING))
		{
			mIntroText[7]->setEnabled(true);
			mIntroEvents[TEXT7] = EVENT_RUNNING;
		}

		//Sexta imagen junto con texto - Globos de café
		if ((mIntroEvents[TEXT7] == EVENT_COMPLETED) && (mIntroEvents[BITMAP5] == EVENT_WAITING) && (mIntroEvents[TEXT8] == EVENT_WAITING))
		{
			mIntroBitmap[4]->setEnabled(false);
			mIntroText[7]->setEnabled(false);
			mIntroBitmap[5]->setEnabled(true);
			mIntroText[8]->setEnabled(true);
			mIntroEvents[BITMAP5] = EVENT_RUNNING;
			mIntroEvents[TEXT8] = EVENT_RUNNING;
		}

		//Acaba el último texto
		if (mIntroEvents[TEXT8] == EVENT_COMPLETED)
		{
			mIntroText[8]->setEnabled(false);
			mGameStatus = GAME_STATE_TITLE;
		}
	}

	//Limpia la pantalla
	SDL_SetRenderDrawColor(mRenderer, 0x27, 0x27, 0x36, 0xFF);
	SDL_RenderClear(mRenderer);

	//Dibuja los objetos
	for (Uint8 i = 0; i < INTRO_TOTAL_BITMAPS; i++)
	{
		mIntroBitmap[i]->render();
	}
	for (Uint8 i = 0; i < INTRO_TOTAL_TEXTS; i++)
	{
		mIntroText[i]->render();
	}

	//Actualiza la pantalla
	SDL_RenderPresent(mRenderer);
}

//Bucle para el titulo del juego
void GameDirector::runTitle()
{
	//Si la música no está sonando
	if (Mix_PlayingMusic() == 0)
	{
		//Reproduce la música
		Mix_PlayMusic(mTitleMusic, -1);
	}

	//Comprueba los eventos que hay en la cola
	while (SDL_PollEvent(mEventHandler) != 0)
	{
		//Evento de salida de la aplicación
		if (mEventHandler->type == SDL_QUIT)
		{
			setGameStatus(GAME_STATE_QUIT);
		}
	}

	//Limpia la pantalla
	SDL_SetRenderDrawColor(mRenderer, 0x43, 0x43, 0x4F, 0xFF);
	SDL_RenderClear(mRenderer);

	//Actualiza los objetos
	mCoffeeBitmap->update();
	mCrisisBitmap->update();

	//Dibuja los objetos
	if (mCoffeeBitmap->isOnDestination())
	{
		mTitleBackground->render();
	}
	mMenuTitle->render();
	mCoffeeBitmap->render();
	mCrisisBitmap->render();

	//Actualiza la pantalla
	SDL_RenderPresent(mRenderer);

	//Comprueba las entradas para el menu
	checkMenuInput(mMenuTitle);

	//Comprueba si se ha seleccionado algún item del menú
	switch (mMenuTitle->getItemSelected())
	{
	case 0:
		setGameStatus(GAME_STATE_PLAYING);
		mMenuTitle->resetMenu();
		Mix_PlayChannel(-1, mMenuSelectedFX, 0);
		renderTransition(1);
		Mix_HaltMusic();
		init(true);
		break;
	case 1:
		setGameStatus(GAME_STATE_QUIT);
		mMenuTitle->resetMenu();
		renderTransition(1);
		Mix_HaltMusic();
		break;
	default:
		break;
	}
}

//Bucle para el juego
void GameDirector::runGame()
{
	//Si la música no está sonando
	if (Mix_PlayingMusic() == 0)
	{
		//Reproduce la música
		Mix_PlayMusic(mPlayingMusic, -1);
	}

	//Lógica del juego
	//Comprueba que la diferencia de ticks sea mayor a la velocidad del juego
	if (SDL_GetTicks() - mOldTicks > mGameSpeed)
	{
		//Actualiza el contador de ticks
		mOldTicks = SDL_GetTicks();

		//Comprueba los eventos que hay en la cola
		while (SDL_PollEvent(mEventHandler) != 0)
		{
			//Evento de salida de la aplicación
			if (mEventHandler->type == SDL_QUIT)
			{
				setGameStatus(GAME_STATE_QUIT);
			}

			//Tecla D pulsada
			if (mEventHandler->key.keysym.sym == SDLK_d)
			{
				mDebug = true;
				break;
			}

			//Tecla F pulsada
			if (mEventHandler->key.keysym.sym == SDLK_f)
			{
				mDebug = false;
				break;
			}
		}

		if (isPlayFieldDrawOnly() == false)
		{
			//Comprueba el teclado/mando
			checkGameInput();

			//Actualiza el jugador
			mPlayer->update();

			//Mueve los globos
			moveBalloons();

			//Mueve las balas
			moveBullets();

			//Procesa las colisiones entre globos y balas
			processBulletBallonCollision();

			//Comprueba el nivel de amenaza
			checkMenaceLevel();

			//Comprueba la colisión entre el jugador y los globos
			if (checkPlayerBallonCollision())
			{
				if (mPlayer->isAlive())
				{
					killPlayer();
				}
			}
		}
	}

	//Limpia la pantalla
	SDL_SetRenderDrawColor(mRenderer, 0x00, 0x00, 0x00, 0xFF);
	SDL_RenderClear(mRenderer);

	//Dibuja los objetos
	mGameBackground->render();
	renderBalloons();
	renderBullets();
	mPlayer->render();
	renderScoreBoard();
	renderGetReady();
	renderDebugInfo();

	//Actualiza la pantalla
	SDL_RenderPresent(mRenderer);
}

//Bucle para el menu de pausa del juego
void GameDirector::runPausedGame()
{
	//Comprueba los eventos que hay en la cola
	while (SDL_PollEvent(mEventHandler) != 0)
	{
		//Evento de salida de la aplicación
		if (mEventHandler->type == SDL_QUIT)
		{
			setGameStatus(GAME_STATE_QUIT);
		}
	}

	//Dibuja los objetos
	mGameBackground->render();
	renderBalloons();
	renderBullets();
	mPlayer->render();
	renderScoreBoard();
	mMenuPause->render();

	//Limpia la pantalla
	SDL_RenderPresent(mRenderer);

	//Comprueba las entradas para el menu
	checkMenuInput(mMenuPause);

	//Comprueba si se ha seleccionado algún item del menú
	switch (mMenuPause->getItemSelected())
	{
	case 0:
		setGameStatus(GAME_STATE_PLAYING);
		mMenuPause->resetMenu();
		break;
	case 1:
		setGameStatus(GAME_STATE_TITLE);
		mMenuPause->resetMenu();
		Mix_PlayChannel(-1, mMenuSelectedFX, 0);
		renderTransition(1);
		init(true);
		break;
	default:
		break;
	}
}

//Bucle para la pantalla de game over
void GameDirector::runGameOverScreen()
{
	//Comprueba los eventos que hay en la cola
	while (SDL_PollEvent(mEventHandler) != 0)
	{
		//Evento de salida de la aplicación
		if (mEventHandler->type == SDL_QUIT)
		{
			setGameStatus(GAME_STATE_QUIT);
		}
	}

	//Limpia la pantalla
	SDL_SetRenderDrawColor(mRenderer, 0x27, 0x27, 0x36, 0xFF);
	SDL_RenderClear(mRenderer);

	//Dibuja los objetos
	renderScoreBoard();
	mWhiteText->write(PLAY_AREA_CENTER_X - (mWhiteText->lenght("GAME OVER", 0) / 2), PLAY_AREA_CENTER_Y, "GAME OVER", 0);

	//Muestra la pantalla
	SDL_RenderPresent(mRenderer);

	//Retiene la pantalla de 'game over' y luego limpia la pantalla y vuelve al titulo
	SDL_Delay(4000);
	SDL_RenderClear(mRenderer);
	SDL_RenderPresent(mRenderer);
	init(true);
	//SDL_Delay(1200);
	setGameStatus(GAME_STATE_INTRO);
}

//Dibuja la informacion de debug en pantalla
void GameDirector::renderDebugInfo()
{
	if (mDebug)
	{
		mWhiteText->write(0, 0 * BLOCK, "menace: " + std::to_string(mMenaceLevelThreshold) + "/" + std::to_string(mMenaceLevel));
		mWhiteText->write(0, 1 * BLOCK, ": ");
		mWhiteText->write(0, 2 * BLOCK, ": ");
		mWhiteText->write(0, 3 * BLOCK, ": ");
		mWhiteText->write(0, 4 * BLOCK, ": ");
		mWhiteText->write(0, 5 * BLOCK, ": ");
		mWhiteText->write(0, 6 * BLOCK, ": ");
		mWhiteText->write(0, 7 * BLOCK, ": ");
	}
}