#include <stdlib.h>
#include "TinyLibrary.hpp"
#include "Player.hpp"
#include "BgMap.hpp"
#include "Enemy.hpp"
#include "Resource.hpp"
#include "Global.hpp"

Player::Player(void)
:x(START_X)
,y(START_Y)
,vx(0)
,vy(0)
,permit_jump(true)
,jump(false)
,jump_keep_count(0)
,jump_keep(false)
,anime_count(0)
,bg_map(NULL)
,alive(false)
,dead_count(0)
{
}

Player::~Player(void)
{
}

void Player::Initialize(BgMap* bg_map)
{
	this->x = START_X;
	this->y = START_Y;
	this->vx = 0;
	this->vy = 0;
	this->permit_jump = false;
	this->jump = false;
	this->jump_keep_count = 0;
	this->jump_keep = false;
	this->anime_count = 0;
	this->bg_map = bg_map;
	this->alive = true;
	this->dead_count = 0;
}

void Player::Update(int& map_x, int& map_y)
{
	Controller& controller = Controller::GetInstance();
	unsigned int button = controller.GetButton();
	bool jump_button = false;
	if(this->alive == true)
	{
		if(button & (Controller::BUTTON_1 | Controller::BUTTON_2))
		{
			jump_button = true;
		}
	}
	else
	{
		this->vx = 0;
		++ this->dead_count;
	}
	if(this->jump == false)
	{
		if((this->permit_jump == true) && (jump_button == true))
		{
			this->vy = -JUMP_FORTH;
			this->jump = true;
			this->permit_jump = false;
			this->jump_keep_count = 0;
			this->jump_keep = true;
			SoundPlayer& sound_player = SoundPlayer::GetInstance();
			Global& global = Global::GetInstance();
			sound_player.Play(global.jump_handle);
		}
		if(jump_button == false)
		{
			this->permit_jump = true;
		}
	}
	else
	{
		if((this->jump_keep == true) && (this->permit_jump == false))
		{
			if(this->jump == true)
			{
				this->vy = -JUMP_KEEP_ACCEL;
				++ this->jump_keep_count;
				if(this->jump_keep_count >= JUMP_KEEP_COUNT)
				{
					this->jump_keep = false;
				}
			}
			if(jump_button == false)
			{
				this->jump_keep = false;
			}
		}
	}
	bool accel = false;
	if(this->alive == true)
	{
		if(button & Controller::BUTTON_LEFT)
		{
			this->vx -= ACCEL_SPEED;
			if(this->vx < -VX_MAX)
			{
				this->vx = -VX_MAX;
			}
			accel = true;
		}
		if(button & Controller::BUTTON_RIGHT)
		{
			this->vx += ACCEL_SPEED;
			if(this->vx > VX_MAX)
			{
				this->vx = VX_MAX;
			}
			accel = true;
		}
	}
	this->x += this->vx;
	this->y += this->vy;
	if(this->y > LIMIT_Y)
	{
		this->y = LIMIT_Y;
	}
	if((accel == false) && (this->jump == false))
	{
		if(this->vx > 0)
		{
			this->vx -= STOP_SPEED;
			if(this->vx < 0)
			{
				this->vx = 0;
			}
		}
		else if(this->vx < 0)
		{
			this->vx += STOP_SPEED;
			if(this->vx > 0)
			{
				this->vx = 0;
			}
		}
	}
	if((this->alive == true) && (this->y >= LIMIT_Y))
	{
		this->y = LIMIT_Y;
		Dead();
		return;
	}
	++ this->anime_count;
	if(this->anime_count >= 16)
	{
		this->anime_count = 0;
	}
	// BgMap collision
	int push_back;
	if(this->vy > 0)
	{
		push_back = this->bg_map->GetPushBackTop(this->x / 1000 + 2, this->y / 1000 + 10);
		if(push_back != 0)
		{
			this->vy = 0;
			this->y -= (push_back * 1000);
			this->jump = false;
		}
		push_back = this->bg_map->GetPushBackTop(this->x / 1000 + 8, this->y / 1000 + 10);
		if(push_back != 0)
		{
			this->vy = 0;
			this->y -= (push_back * 1000);
			this->jump = false;
		}
	}
	else if(this->vy < 0)
	{
		push_back = this->bg_map->GetPushBackBottom(this->x / 1000 + 2, this->y / 1000);
		if(push_back != 0)
		{
			this->vy = 0;
			this->y += (push_back * 1000);
			this->jump_keep_count = JUMP_KEEP_COUNT;
		}
		push_back = this->bg_map->GetPushBackBottom(this->x / 1000 + 8, this->y / 1000);
		if(push_back != 0)
		{
			this->vy = 0;
			this->y += (push_back * 1000);
			this->jump_keep_count = JUMP_KEEP_COUNT;
		}
	}
	if(this->vx > 0)
	{
		push_back = this->bg_map->GetPushBackLeft(this->x / 1000 + 10, this->y / 1000 + 2);
		if(push_back != 0)
		{
			this->vx = 0;
			this->x -= (push_back * 1000);
		}
		push_back = this->bg_map->GetPushBackLeft(this->x / 1000 + 10, this->y / 1000 + 8);
		if(push_back != 0)
		{
			this->vx = 0;
			this->x -= (push_back * 1000);
		}
	}
	else if(this->vx < 0)
	{
		push_back = this->bg_map->GetPushBackRight(this->x / 1000, this->y / 1000 + 2);
		if(push_back != 0)
		{
			this->vx = 0;
			this->x += (push_back * 1000);
		}
		push_back = this->bg_map->GetPushBackRight(this->x / 1000, this->y / 1000 + 8);
		if(push_back != 0)
		{
			this->vx = 0;
			this->x += (push_back * 1000);
		}
	}
	// Gravity
	if(this->jump == true)
	{
		this->vy += GRAVITY;
		if(this->vy > VY_MAX)
		{
			this->vy = VY_MAX;
		}
	}
	// Drop
	if(this->vy == 0)
	{
		int chip_left;
		int chip_right;
		chip_left = this->bg_map->GetScreenChip(this->x / 1000 + 1, this->y / 1000 + 10);
		chip_right = this->bg_map->GetScreenChip(this->x / 1000 + 8, this->y / 1000 + 10);
		if((chip_left == 0) && (chip_right == 0))
		{
			this->jump = true;
		}
	}
	// Scroll
	int bg_map_width = 0;
	int bg_map_height = 0;
	int player_right_limit = (Screen::WIDTH - 12) * 1000;
	this->bg_map->GetSize(bg_map_width, bg_map_height);
	if((map_x > 0) && (this->x < SCROLL_LEFT))
	{
		if(this->x < 0)
		{
			this->x = 0;
		}
	}
	else if((map_x < ((-bg_map_width + Screen::WIDTH) * 1000)) && (this->x > SCROLL_RIGHT))
	{
		if(this->x > player_right_limit)
		{
			this->x = player_right_limit;
		}
	}
	else
	{
		int map_vx = 0;
		if(this->x <= SCROLL_LEFT)
		{
			map_vx = SCROLL_LEFT - this->x;
			this->x = SCROLL_LEFT;
		}
		else if(this->x >= SCROLL_RIGHT)
		{
			map_vx = -(this->x - SCROLL_RIGHT);
			this->x = SCROLL_RIGHT;
		}
		map_x += map_vx;
	}
}

void Player::Draw(void)
{
	Screen& screen = Screen::GetInstance();
	int x = this->x / 1000;
	int y = this->y / 1000;
	if(this->alive == false)
	{
		int dead_count = this->dead_count;
		if(dead_count > 23)
		{
			dead_count = 23;
		}
		int image_number = METEOR_5 + dead_count / 8;
		ImageData* image_data = GetImageData(image_number);
		screen.DrawSprite(image_data->buffer, x, y, image_data->width, image_data->height, 0);
	}
	else if(this->jump == false)
	{
		if(this->anime_count < 8)
		{
			ImageData* image_data = GetImageData(METEOR_1);
			screen.DrawSprite(image_data->buffer, x, y, image_data->width, image_data->height, 0);
		}
		else
		{
			ImageData* image_data = GetImageData(METEOR_2);
			screen.DrawSprite(image_data->buffer, x, y, image_data->width, image_data->height, 0);
		}
	}
	else
	{
		ImageData* image_data = GetImageData(METEOR_4);
		screen.DrawSprite(image_data->buffer, x, y, image_data->width, image_data->height, 0);
	}
}

unsigned int Player::GetStatus(void)
{
	if(this->alive == false)
	{
		if(this->dead_count >= 60)
		{
			return STATUS_DEAD;
		}
	}
	return STATUS_ALIVE;
}

// result (true : Foot hit)
bool Player::Collision(Enemy* enemy)
{
	if(this->alive == false)
	{
		return false;
	}
	int enemy_x = -1;
	int enemy_y = -1;
	enemy->GetPosition(enemy_x, enemy_y);
	int y_diff = enemy_y - this->y;
	int x_diff = abs(enemy_x - this->x);
	if((this->jump == true) && (this->vy > 0) && (x_diff < 10000) && (y_diff > 0) && (y_diff < 10000))
	{
		// Foot hit
		enemy->SetAlive(false);
		return true;
	}
	else if((x_diff < 10000) && (y_diff > -10000) && (y_diff < 10000) && (enemy->GetAlive() == true))
	{
		// Dead hit
		Dead();
		SoundPlayer& sound_player = SoundPlayer::GetInstance();
		Global& global = Global::GetInstance();
		sound_player.Play(global.piyo_handle);
	}
	return false;
}

void Player::SetFootHit(void)
{
	this->vy = -JUMP_FORTH / 2;
	this->permit_jump = false;
	this->jump_keep_count = 0;
	this->jump_keep = false;
}

void Player::GetPosition(int& x, int& y)
{
	x = this->x;
	y = this->y;
}

void Player::Dead(void)
{
	if(this->alive == true)
	{
		// Dead
		this->vy = 0;
		this->jump = false;
		this->alive = false;
		this->dead_count = 0;
	}
}
