#include "gamedirector.h"

//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()
{
	init();
}

//Iniciador
void GameDirector::init()
{
	//Variables
	mGameStatus = GAME_STATE_TITLE;
	mOldTicks = 0;
	mMaxBalloons = 50;
	mMaxBullets = 50;
	mGameSpeed = 15;
	mMenaceLevel = 0;
	mMenaceLevelThreshold = 7;
	mScore = 0;
	mHiScore = 0;
	mScoreText = std::to_string(mScore);
	mHiScoreText = std::to_string(mHiScore);
	mGetReady = true;

	//Objeto jugador
	player.init();

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

	//Crea dos objetos globo y los centra en el area de juego
	//balloon[0].init(0, BLOCK, BALLOON_4, BALLON_VELX_POSITIVE, 0);
	//balloon[0].allignTo(PLAY_AREA_WIDTH / 2);
	//balloon[1].init(0, BLOCK, BALLOON_4, BALLON_VELX_NEGATIVE, 0);
	//balloon[1].allignTo(PLAY_AREA_WIDTH / 2);

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

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

#ifdef TEST
	balloonTest.init(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, BALLOON_4, 0);
	balloonTest.stop();
	bulletTest.init(SCREEN_WIDTH / 4, SCREEN_HEIGHT / 2, BULLET_UP);
#endif

	//Los fondos
	gameBackground.init(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT - (0 * BLOCK), &gGameBackgroundTexture);
	titleBackground.init(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, &gTitleBackgroundTexture);

	//Objetos texto, uno de cada color
	whiteText.init(&gWhiteFontTexture);
	blackText.init(&gBlackFontTexture);

	//Inicializa el objeto con el menu del titulo
	menuTitle.init(0, 16 * BLOCK, MENU_SELECTOR_WHITE, MENU_BACKGROUND_TRANSPARENT);
	menuTitle.addItem("START");
	menuTitle.addItem("EXIT");
	menuTitle.setBackgroundColor(0, 0, 0, 255);
	menuTitle.centerMenuOnScreen();

	//Inicializa el objeto con el menu de pausa
	menuPause.init(0, 12 * BLOCK, MENU_SELECTOR_WHITE, MENU_BACKGROUND_SOLID);
	menuPause.addItem("CONTINUE");
	menuPause.addItem("EXIT TO TITLE");
	menuPause.setBackgroundColor(0x73, 0x27, 0x5c, 255);
	menuPause.centerMenuOnScreen();
}

//Hace una pausa de milisegundos
void GameDirector::sleep(Uint16 time)
{
	Uint32 ticks = SDL_GetTicks();
	while (SDL_GetTicks() - ticks < time)
	{
		/* code */
	}
}

//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()
{
	if (mScore > mHiScore)
	{
		mHiScore = mScore;
	}
}

//Transforma un valor numérico en una cadena de 6 cifras
std::string GameDirector::updateScoreText(Uint32 num)
{
	switch (num)
	{
	case 0 ... 9:
		return ("00000" + std::to_string(num));
		break;

	case 10 ... 99:
		return ("0000" + std::to_string(num));
		break;

	case 100 ... 999:
		return ("000" + std::to_string(num));
		break;

	case 1000 ... 9999:
		return ("00" + std::to_string(num));
		break;

	case 10000 ... 99999:
		return ("0" + std::to_string(num));
		break;

	case 100000 ... 999999:
		return (std::to_string(num));
		break;
	default:
		return (std::to_string(num));
		break;
	}
}

//Pinta el marcador en pantalla usando un objeto texto
void GameDirector::renderScoreBoard(Text &text)
{
	mScoreText = updateScoreText(mScore);
	mHiScoreText = updateScoreText(mHiScore);
	text.write(SCORE_WORD_X, SCORE_WORD_Y, "SCORE");
	text.write(SCORE_NUMBER_X, SCORE_NUMBER_Y, mScoreText);
	text.write(HISCORE_WORD_X, HISCORE_WORD_Y, "HI-SCORE");
	text.write(HISCORE_NUMBER_X, HISCORE_NUMBER_Y, mHiScoreText);
}

//Mueve todos los globos activos
void GameDirector::moveBalloons()
{
	for (Uint8 i = 0; i < mMaxBalloons; i++)
	{
		if (balloon[i].isActive())
		{
			balloon[i].move();
		}
	}
}

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

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

	for (Uint8 i = 0; i < mMaxBalloons; i++)
	{
		if (balloon[i].isActive() == false)
		{
			index = i;
			break;
		}
	}

	return index;
}

//Crea un globo nuevo en el vector de globos
Uint8 GameDirector::createNewBalloon(int x, int y, Uint8 kind, float velx, Uint16 creationtimer)
{
	Uint8 index = getBallonFreeIndex();
	balloon[index].init(x, y, kind, velx, creationtimer);
	return index;
}

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

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

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

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

			//Elimina el globo
			balloon[index].erase();
			break;
		}
	}
}

//Detiene todos los globos
void GameDirector::stopAllBalloons()
{
	for (Uint8 i = 0; i < mMaxBalloons; i++)
	{
		if (balloon[i].isActive())
		{
			balloon[i].setStop(true);
		}
	}
}

//Pone en marcha todos los globos
void GameDirector::startAllBalloons()
{
	for (Uint8 i = 0; i < mMaxBalloons; i++)
	{
		if (balloon[i].isActive())
		{
			balloon[i].setStop(false);
		}
	}
}

//Obtiene el numero de globos activos
Uint8 GameDirector::countBalloons()
{
	Uint8 num = 0;
	for (Uint8 i = 0; i < mMaxBalloons; i++)
	{
		if (balloon[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 < mMaxBalloons; i++)
	{
		if (balloon[i].isActive())
		{
			if (checkCollision(player.getCollider(), balloon[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 < mMaxBalloons; i++)
	{
		for (Uint8 j = 0; j < mMaxBullets; j++)
		{
			if (balloon[i].isActive() && !(balloon[i].isInvulnerable()) && bullet[j].isActive())
			{
				if (checkCollision(balloon[i].getCollider(), bullet[j].getCollider()))
				{
					player.addScore(balloon[i].getScore());
					setScore(player.getScore());
					updateHiScore();
					popBalloon(i);
					Mix_PlayChannel(-1, gPopBalloonFX, 0);
					bullet[j].erase();
					calculateMenaceLevel();
					break;
				}
			}
		}
	}
}

//Mueve las balas activas
void GameDirector::moveBullets()
{
	for (Uint8 i = 0; i < mMaxBullets; i++)
	{
		if (bullet[i].isActive())
		{
			bullet[i].move();
		}
	}
}

//Pinta las balas activas
void GameDirector::renderBullets()
{
	for (Uint8 i = 0; i < mMaxBullets; i++)
	{
		if (bullet[i].isActive())
		{
			bullet[i].render();
		}
	}
}

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

	for (int i = 0; i < mMaxBullets; i++)
	{
		if (bullet[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 < mMaxBullets; i++)
	{
		bullet[i].init(0, 0, NO_KIND);
	}
}

//Crea un objeto bala
void GameDirector::createBullet(int x, int y, Uint8 kind)
{
	bullet[getBulletFreeIndex()].init(x, y, kind);
}

//Calcula y establece el valor de amenaza en funcion de los globos activos
void GameDirector::calculateMenaceLevel()
{
	mMenaceLevel = 0;
	for (Uint8 i = 0; i < mMaxBalloons; i++)
	{
		switch (balloon[i].getKind())
		{
		case BALLOON_1:
			mMenaceLevel += 1;
			break;

		case BALLOON_2:
			mMenaceLevel += 2;
			break;

		case BALLOON_3:
			mMenaceLevel += 4;
			break;

		case BALLOON_4:
			mMenaceLevel += 8;
			break;

		default:
			mMenaceLevel += 0;
			break;
		}
	}
}

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

//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 = player.getPosX() + (player.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);
		}
		else
		{
			index = createNewBalloon(0, PLAY_AREA_TOP + BLOCK - 37, BALLOON_4, BALLON_VELX_NEGATIVE, 400);
		}
		balloon[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
	const Uint8 *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))
	{
		player.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))
	{
		player.checkInput(INPUT_RIGHT);
	}
	//Ninguna de las dos direcciones pulsadas
	else
	{
		player.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 (player.canFire())
		{
			createBullet(player.getPosX() + (player.getWidth() / 2) - 4, player.getPosY(), BULLET_UP);
			player.setFireCooldown(10);

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

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

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

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

			//Reproduce el sonido de disparo
			Mix_PlayChannel(-1, gBulletFX, 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
	const Uint8 *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);
	}

#ifdef TEST
	if (SDL_JoystickGetButton(gGameController, 1))
	{
		std::cout << "button1\n";
	}

	if (SDL_JoystickGetButton(gGameController, 1))
	{
		std::cout << "button1\n";
	}

	if (SDL_JoystickGetButton(gGameController, 2))
	{
		std::cout << "button2\n";
	}

	if (SDL_JoystickGetButton(gGameController, 3))
	{
		std::cout << "button3\n";
	}

	if (SDL_JoystickGetButton(gGameController, 4))
	{
		std::cout << "button4\n";
	}

	if (SDL_JoystickGetButton(gGameController, 5))
	{
		std::cout << "button5\n";
	}

	if (SDL_JoystickGetButton(gGameController, 6))
	{
		std::cout << "button6\n";
	}

	if (SDL_JoystickGetButton(gGameController, 7))
	{
		std::cout << "button7\n";
	}

	if (SDL_JoystickGetButton(gGameController, 8))
	{
		std::cout << "button8\n";
	}

	if (SDL_JoystickGetButton(gGameController, 9))
	{
		std::cout << "button9\n";
	}

	if (SDL_JoystickGetButton(gGameController, 10))
	{
		std::cout << "button10\n";
	}

	if (SDL_JoystickGetButton(gGameController, 11))
	{
		std::cout << "button11\n";
	}

	if (SDL_JoystickGetButton(gGameController, 12))
	{
		std::cout << "button12\n";
	}

	if (SDL_JoystickGetButton(gGameController, 13))
	{
		std::cout << "button13\n";
	}

	if (SDL_JoystickGetButton(gGameController, 14))
	{
		std::cout << "button14\n";
	}

	if (SDL_JoystickGetButton(gGameController, 15))
	{
		std::cout << "button15\n";
	}
#endif
}

//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(gRenderer);
		SDL_SetRenderDrawColor(gRenderer, 0, 0, 0, 0);

		for (Uint16 i = 0; i < SCREEN_HEIGHT; i = i + 2)
		{
			rect.h = i;
			SDL_RenderFillRect(gRenderer, &rect);
			SDL_RenderPresent(gRenderer);
		}
		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(gRenderer);
		SDL_SetRenderDrawBlendMode(gRenderer, SDL_BLENDMODE_BLEND);
		SDL_SetRenderDrawColor(gRenderer, 0, 0, 0, 64);

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

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

			SDL_RenderPresent(gRenderer);
		}

		rect1.x = 0;
		rect1.y = 0;
		rect1.w = SCREEN_WIDTH;
		rect1.h = SCREEN_HEIGHT;

		for (Uint16 i = 0; i < (SCREEN_HEIGHT / 2); i = i + 4)
		{
			SDL_RenderFillRect(gRenderer, &rect1);
			SDL_RenderPresent(gRenderer);
		}
		break;

	default:
		break;
	}
}

//Pinta el texto GetReady en pantalla
void GameDirector::renderGetReady()
{
	if (mGetReady)
	{
		Sprite sprite;
		sprite.setTexture(gMiscTexture);
		sprite.setWidth(53);
		sprite.setHeight(10);
		sprite.setPosX((PLAY_AREA_WIDTH / 2) - (sprite.getWidth() / 2));
		sprite.setPosY((PLAY_AREA_HEIGHT / 2) - (sprite.getHeight() / 2));
		sprite.setSpriteClip(0, 0, sprite.getWidth(), sprite.getHeight());
		for (Uint8 i = 0; i < 1; i++)
		{
			sprite.render();
			SDL_RenderPresent(gRenderer);
			SDL_Delay(1500);
		}
		mGetReady = false;
	}
}

//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(gTitleMusic, -1);
	}

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

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

	//Dibuja los objetos
	titleBackground.render();
	menuTitle.render(whiteText);

	//Actualiza la pantalla
	SDL_RenderPresent(gRenderer);

	//Comprueba las entradas para el menu
	checkMenuInput(&menuTitle);

	//Comprueba si se ha seleccionado algún item del menú
	switch (menuTitle.getItemSelected())
	{
	case 0:
		setGameStatus(GAME_STATE_PLAYING);
		menuTitle.resetMenu();
		renderTransition(1);
		Mix_HaltMusic();
		SDL_Delay(1200);
		break;
	case 1:
		setGameStatus(GAME_STATE_QUIT);
		menuTitle.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(gPlayingMusic, -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 el teclado/mando
		checkGameInput();

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

			//Tecla T pulsada
			if (eventHandler.key.keysym.sym == SDLK_t)
			{
				//startAllBalloons();
				popBalloon(0);
				break;
			}
#ifdef TEST
			//W key pressed
			if (eventHandler.key.keysym.sym == SDLK_w)
			{
				bulletTest.setPosY(bulletTest.getPosY() - 1);
				bulletTest.testMove();
				break;
			}

			//S key pressed
			if (eventHandler.key.keysym.sym == SDLK_s)
			{
				bulletTest.setPosY(bulletTest.getPosY() + 1);
				bulletTest.testMove();
				break;
			}

			//A key pressed
			if (eventHandler.key.keysym.sym == SDLK_a)
			{
				bulletTest.setPosX(bulletTest.getPosX() - 1);
				bulletTest.testMove();
				break;
			}

			//D key pressed
			if (eventHandler.key.keysym.sym == SDLK_d)
			{
				bulletTest.setPosX(bulletTest.getPosX() + 1);
				bulletTest.testMove();
				break;
			}
#endif
		}

		//Actualiza el jugador
		player.update();

		//Mueve los globos
		moveBalloons();

#ifdef TEST
		balloonTest.move();
#endif

		//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())
		{
			//stopAllBalloons();
		}
	}

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

	//Dibuja los objetos
	gameBackground.render();
	renderBalloons();
#ifdef TEST
	balloonTest.render();
	bulletTest.render();
	if (checkCollision(balloonTest.getCollider(), bulletTest.getCollider()))
	{
		whiteText.write(0, 0, "X");
	}
#endif
	//whiteText.write(0, 0, std::to_string(mMenaceLevelThreshold));
	//whiteText.write(0, BLOCK, std::to_string(player.getPosX() + player.getWidth()));
	renderBullets();
	player.render();
	renderScoreBoard(whiteText);
	renderGetReady();

	//Actualiza la pantalla
	SDL_RenderPresent(gRenderer);
}

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

	//Dibuja los objetos
	gameBackground.render();
	renderBalloons();
	renderBullets();
	player.render();
	renderScoreBoard(whiteText);
	menuPause.render(whiteText);

	//Limpia la pantalla
	SDL_RenderPresent(gRenderer);

	//Comprueba las entradas para el menu
	checkMenuInput(&menuPause);

	//Comprueba si se ha seleccionado algún item del menú
	switch (menuPause.getItemSelected())
	{
	case 0:
		setGameStatus(GAME_STATE_PLAYING);
		menuPause.resetMenu();
		break;
	case 1:
		setGameStatus(GAME_STATE_TITLE);
		menuPause.resetMenu();
		renderTransition(1);
		init();
		break;
	default:
		break;
	}
}