#include <stdio.h>
#include <string.h>
#include "TinyLibrary.hpp"
#include "Resource.hpp"
#include "Hiyoko.hpp"
#include "Global.hpp"
#include "Game.hpp"

#if defined(_WIN32)
#pragma warning(disable:4996)
#endif

Game::Game(void)
:stage(1)
,bg_map()
,player()
,bg_map_x(0)
,bg_map_y(-6000)
,stage_clear(false)
,stage_clear_count(0)
,stage_start_count(0)
,continue_count(-1)
,score(0)
,highscore(0)
,time(5 * 60 * 30)
{
	for(int i = 0; i < ENEMY_MAX; ++ i)
	{
		this->enemy[i] = NULL;
	}
}

Game::~Game(void)
{
	for(int i = 0; i < ENEMY_MAX; ++ i)
	{
		if(this->enemy[i] != NULL)
		{
			delete this->enemy[i];
			this->enemy[i] = NULL;
		}
	}
}

void Game::Initialize(int stage)
{
	Restart(stage);
	this->score = 0;
}

void Game::Restart(int stage)
{
	this->stage = stage;
	this->bg_map_x = 0;
	this->bg_map_y = -6000;
	this->bg_map.Initialize(this->stage - 1, 10, 7);
	this->player.Initialize(&this->bg_map);
	for(int i = 0; i < ENEMY_MAX; ++ i)
	{
		if(this->enemy[i] != NULL)
		{
			delete this->enemy[i];
			this->enemy[i] = NULL;
		}
	}
	this->stage_clear = false;
	this->stage_clear_count = 0;
	this->stage_start_count = 0;
	this->continue_count = -1;
	SoundPlayer& sound_player = SoundPlayer::GetInstance();
	Global& global = Global::GetInstance();
	this->highscore = global.gamedata.highscore;
	this->time = 5 * 60 * 30;
	sound_player.Play(global.bgm_handle);
}

void Game::Finalize(void)
{
	SoundPlayer& sound_player = SoundPlayer::GetInstance();
	sound_player.StopAll();
	for(int i = 0; i < ENEMY_MAX; ++ i)
	{
		if(this->enemy[i] != NULL)
		{
			delete this->enemy[i];
			this->enemy[i] = NULL;
		}
	}
	Global& global = Global::GetInstance();
	global.gamedata.highscore = this->highscore;
	global.gamedata.score = this->score;
}

bool Game::Update(void)
{
	SoundPlayer& sound_player = SoundPlayer::GetInstance();
	Global& global = Global::GetInstance();
	if(this->continue_count < 0)
	{
		this->player.Update(this->bg_map_x, this->bg_map_y);
	}
	bool foot_hit = false;
	this->bg_map.SetPosition(this->bg_map_x / 1000, this->bg_map_y / 1000);
	for(int i = 0; i < ENEMY_MAX; ++ i)
	{
		if(this->enemy[i] == NULL)
		{
			continue;
		}
		this->enemy[i]->Update();
		if(this->enemy[i]->GetStatus() != Enemy::STATUS_DEAD)
		{
			// Collision
			bool result = this->player.Collision(this->enemy[i]);
			if(result == true)
			{
				foot_hit = true;
			}
		}
		if(this->enemy[i]->GetStatus() == Enemy::STATUS_DEAD)
		{
			delete this->enemy[i];
			this->enemy[i] = NULL;
		}
	}
	if(foot_hit == true)
	{
		this->score += 100;
		sound_player.Play(global.knockdown_handle);
		this->player.SetFootHit();
	}
	// Spawn enemy
	SpawnEnemy();
	// Player status
	int status = this->player.GetStatus();
	if(status == Player::STATUS_DEAD)
	{
		if(this->continue_count == -1)
		{
			this->continue_count = 30 * 10;
			SoundPlayer& sound_player = SoundPlayer::GetInstance();
			Global& global = Global::GetInstance();
			sound_player.Stop(global.bgm_handle);
		}
	}
	if(this->continue_count > 0)
	{
		-- this->continue_count;
		if(this->continue_count <= 0)
		{
			return true;
		}
		Controller& controller = Controller::GetInstance();
		unsigned int button = controller.GetButton();
		if(button & (Controller::BUTTON_1 | Controller::BUTTON_2))
		{
			Initialize(this->stage);
		}
	}
	else
	{
		// StageClear
		int player_x = -1;
		int player_y = -1;
		this->player.GetPosition(player_x, player_y);
		if(player_x > Player::SCROLL_RIGHT)
		{
			int map_width = 0;
			int map_height = 0;
			this->bg_map.GetSize(map_width, map_height);
			if(this->bg_map_x <= -((map_width - 1) * 1000 - 96000))
			{
				this->stage_clear = true;
			}
		}
		if(this->stage_clear == true)
		{
			++ this->stage_clear_count;
			if(this->stage_clear_count > 30 * 3)
			{
				++ this->stage;
				if(this->stage >= STAGE_MAX + 1)
				{
					this->stage = 1;
				}
				Restart(this->stage);
			}
		}
	}
	++ this->stage_start_count;
	if(this->stage_start_count > 30)
	{
		this->stage_start_count = 30;
	}
	if(this->score > this->highscore)
	{
		this->highscore = this->score;
	}
	if((this->continue_count <= 0) && (this->stage_clear == false))
	{
		-- this->time;
		if(this->time < 0)
		{
			this->player.Dead();
			this->time = 0;
		}
	}
	return false;
}

void Game::Draw(void)
{
	int draw_x;
	Screen& screen = Screen::GetInstance();
	this->bg_map.Draw();
	for(int i = 0; i < ENEMY_MAX; ++ i)
	{
		if(this->enemy[i] == NULL)
		{
			continue;
		}
		this->enemy[i]->Draw();
	}
	this->player.Draw();
	// Start
	if(this->stage_start_count < 30)
	{
		ImageData* image_data;
		image_data = GetImageData(MESSAGE_STAGE);
		ImageData* stage_image_data = GetImageData(NUMBER_0 + this->stage);
		draw_x = (Screen::WIDTH - image_data->width - stage_image_data->width - 4) / 2;
		screen.DrawSprite(image_data->buffer, draw_x, 12, image_data->width, image_data->height, 0);
		screen.DrawSprite(stage_image_data->buffer, draw_x + image_data->width + 4, 12, stage_image_data->width, stage_image_data->height, 0);
		image_data = GetImageData(MESSAGE_START);
		draw_x = (Screen::WIDTH - image_data->width) / 2;
		screen.DrawSprite(image_data->buffer, draw_x, 24, image_data->width, image_data->height, 0);
	}
	// StageClear
	if(this->stage_clear == true)
	{
		ImageData* image_data;
		if(this->stage == STAGE_MAX)
		{
			image_data = GetImageData(MESSAGE_CONGRATULATIONS);
		}
		else
		{
			image_data = GetImageData(MESSAGE_STAGECLEAR);
		}
		draw_x = (Screen::WIDTH - image_data->width) / 2;
		screen.DrawSprite(image_data->buffer, draw_x, 28, image_data->width, image_data->height, 0);
	}
	// Continue?
	if(this->continue_count > 0)
	{
		ImageData* image_data = GetImageData(MESSAGE_CONTINUE);
		draw_x = (Screen::WIDTH - image_data->width) / 2;
		screen.DrawSprite(image_data->buffer, draw_x, 8, image_data->width, image_data->height, 0);
		image_data = GetImageData(NUMBER_0);
		draw_x = (Screen::WIDTH - image_data->width * 2) / 2;
		int continue_count = this->continue_count / 30;
		DrawNumber(draw_x, 20, continue_count, 2);
	}
	int score = this->score;
	if(score > 99999999)
	{
		score = 99999999;
	}
	screen.DrawNumberFont(Screen::LEFT, 0, "%8d", score);
	int highscore = this->highscore;
	if(highscore > 99999999)
	{
		highscore = 99999999;
	}
	screen.DrawNumberFont(Screen::RIGHT, 0, "%2d:%02d", this->time / (60 * 30), this->time % (60 * 30) / 30);
}

unsigned char Game::GetBackColor(void)
{
	return this->bg_map.BackColor8();
}

void Game::SpawnEnemy(void)
{
	BgMapData* bg_map_data = GetBgMapData(this->stage - 1);
	ImageData* image_data = GetImageData(1);
	int width = image_data->width;
	int height = image_data->height;
	int enemy_index = 0;
	int map_x;
	int map_y;
	this->bg_map.GetPosition(map_x, map_y);
	for(int x = 0; x < bg_map_data->width; ++ x)
	{
		int draw_x = map_x + x * width;
		for(int y = 0; y < bg_map_data->height; ++ y)
		{
			int chip = bg_map_data->buffer[y * bg_map_data->width + x];
			if(chip >= 192)
			{
				if(this->enemy[enemy_index] == NULL)
				{
					if((draw_x < -image_data->width) || (draw_x > Screen::WIDTH))
					{
						if((draw_x > -image_data->width * 3) && (draw_x < (Screen::WIDTH + image_data->width * 3)))
						{
							int enemy_no = -1;
							int direction;
							int type;
							switch(chip)
							{
							case 192:
								enemy_no = 0;
								direction = 0;
								type = Hiyoko::TYPE_1;
								break;
							case 193:
								enemy_no = 0;
								direction = 1;
								type = Hiyoko::TYPE_1;
								break;
							case 194:
								enemy_no = 0;
								direction = 0;
								type = Hiyoko::TYPE_2;
								break;
							case 195:
								enemy_no = 0;
								direction = 1;
								type = Hiyoko::TYPE_2;
								break;
							}
							if(enemy_no != -1)
							{
								switch(enemy_no)
								{
								case 0:
									Hiyoko* hiyoko = new Hiyoko;
									hiyoko->Initialize(&this->bg_map, direction, draw_x * 1000, (map_y + y * height) * 1000);
									hiyoko->SetType(type);
									this->enemy[enemy_index] = hiyoko;
									break;
								}
							}
						}
					}
				}
				++ enemy_index;
			}
		}
	}
}

void Game::DrawNumber(int x, int y, int number, int number_of_digits)
{
	char number_text[64];
	char format_text[64];
	if(number_of_digits < 2)
	{
		sprintf(number_text, "%d", number);
	}
	else
	{
		sprintf(format_text, "%%0%dd", number_of_digits);
		sprintf(number_text, format_text, number);
	}
	Screen& screen = Screen::GetInstance();
	ImageData* image_data;
	for(int i = 0; i < static_cast<int>(strlen(number_text)); ++ i)
	{
		int number = NUMBER_0 + number_text[i] - '0';
		image_data = GetImageData(number);
		screen.DrawSprite(image_data->buffer, x, y, image_data->width, image_data->height, 0);
		x += image_data->width;
	}
}
