
#include "nx.h"
#include "statusbar.h"
#include "statusbar.fdh"

#ifdef WIDESCREEN
	#define STATUS_X	10
	#define STATUS_Y	10
#else
	#define STATUS_X	16
	#define STATUS_Y	16
#endif

#define HEALTH_X			(STATUS_X+0)
#define HEALTH_Y			(STATUS_Y+24)
#define HEALTHFILL_X		(HEALTH_X+24)
#define HEALTHFILL_Y		(HEALTH_Y+1)
#define HEALTHFILL_MAXLEN	39

#define WEAPONBAR_Y			(STATUS_Y+1)
#define CURWEAPON_X			(STATUS_X+1)

#define XPBAR_Y				(STATUS_Y+16)		// Y coordinate of "XP" area
#define XPBAR_X				(STATUS_X+24)		// X of yellow XP bar

#define AMMO_X				(STATUS_X+32)
#define AMMO_Y				(STATUS_Y+0)

#define NIKU_X				STATUS_X
#define NIKU_Y				8

#define FRAME_XP_BAR		0			// empty bar frame
#define FRAME_XP_FILL		1			// sprite to fill bar with
#define FRAME_XP_FLASH		2			// white flashing when XP is gained
#define FRAME_XP_MAX		3			// "MAX" when XP is at max on L3

//int displayed_health = 0;
//int healthdectimer = 0;
StatusBar statusbar;
static PercentBar PHealthBar;

// the "slide" effect when changing weapons
struct stWeaponSlide
{
	int lv_offset;					// offset of XP bar
	int wpn_offset;					// offset of weapon bar
	int ammo_offset;				// offset of ammo, in addition to wpn_offset
	char timer;
	int move_dir;
	int firstWeapon;				// weapon to show as current weapon
} slide;
#define SLIDE_LV_OFFSET			16
#define SLIDE_TIMER_START		5


bool statusbar_init(void)
{
	InitPercentBar(&PHealthBar, player->hp);
	
	memset(&slide, 0, sizeof(slide));
	slide.firstWeapon = player->curWeapon;
	return 0;
}


void DrawStatusBar(void)
{
int level, curxp, maxxp;
int w, x;
bool maxed_out;

	//debug("%08x", game.bossbar.object);
	//debug("%s", game.bossbar.defeated ? "true" : "false");
	
	// handle animations etc
	RunStatusBar();
	
	// draw boss bar
	if (game.bossbar.object && !game.bossbar.defeated)
	{
		#define BOSSBAR_W	198
		// BOSS_X = 32 at normal resolution
		#define BOSS_X		((SCREEN_WIDTH / 2) - (BOSSBAR_W / 2) - 29)
		#define BOSS_Y		(SCREEN_HEIGHT-20)
		draw_sprite(BOSS_X, BOSS_Y, SPR_TEXTBOX, 0, 0);
		draw_sprite(BOSS_X, BOSS_Y+8, SPR_TEXTBOX, 2, 0);
		draw_sprite(BOSS_X+8, BOSS_Y+4, SPR_BOSSHPICON, 0, 0);
		
		// e.g. bosses w/ multiple forms (Ballos)
		if (game.bossbar.object->hp > game.bossbar.starting_hp)
			game.bossbar.starting_hp = game.bossbar.object->hp;
		
		RunPercentBar(&game.bossbar.bar, game.bossbar.object->hp);
		DrawPercentBar(&game.bossbar.bar, BOSS_X+40, BOSS_Y+5, game.bossbar.object->hp, game.bossbar.starting_hp, BOSSBAR_W);
	}
	
	if (game.frozen || player->inputs_locked) return;
	if (fade.getstate() != FS_NO_FADE) return;
	
	if (player->hp)
	{
		if (!player->hurt_flash_state)
      {
         // -- draw the health bar -----------------------------
         draw_sprite(HEALTH_X, HEALTH_Y, SPR_HEALTHBAR, 0, 0);

         DrawPercentBar(&PHealthBar, HEALTHFILL_X, HEALTHFILL_Y, player->hp, player->maxHealth, HEALTHFILL_MAXLEN);

         // draw the health in numbers
         DrawNumberRAlign(HEALTH_X+24, HEALTH_Y, SPR_WHITENUMBERS, PHealthBar.displayed_value);

         // -- draw the XP bar ---------------------------------
         level = player->weapons[player->curWeapon].level;
         curxp = player->weapons[player->curWeapon].xp;
         maxxp = player->weapons[player->curWeapon].max_xp[level];

         if (player->curWeapon == WPN_NONE)
         {
            curxp = 0;
            maxxp = 1;
         }

         // draw XP bar and fill it
         draw_sprite(XPBAR_X+slide.lv_offset, XPBAR_Y, SPR_XPBAR, FRAME_XP_BAR, 0);

         maxed_out = ((curxp == maxxp) && level == 2);
         if (!maxed_out)
            DrawPercentage(XPBAR_X+slide.lv_offset, XPBAR_Y, SPR_XPBAR, FRAME_XP_FILL, curxp, maxxp, sprites[SPR_XPBAR].w);

         // draw the white flashing if we just got more XP
         // the time-left and flash-state are in separate variables--
         // otherwise the Spur will not flash XP bar
         if (statusbar.xpflashcount)
         {
            if (++statusbar.xpflashstate & 2)
            {
               draw_sprite(XPBAR_X+slide.lv_offset, XPBAR_Y, SPR_XPBAR, FRAME_XP_FLASH, 0);
            }

            statusbar.xpflashcount--;
         }
         else statusbar.xpflashstate = 0;

         // draw "MAX"
         if (maxed_out)
            draw_sprite(XPBAR_X+slide.lv_offset, XPBAR_Y, SPR_XPBAR, FRAME_XP_MAX, 0);

         // Level Number
         DrawWeaponLevel(HEALTH_X + slide.lv_offset, XPBAR_Y, player->curWeapon);
      }
		
		// -- draw the weapon bar -----------------------------
		// draw current weapon
		if (player->curWeapon != WPN_NONE)
			draw_sprite(CURWEAPON_X + slide.wpn_offset, WEAPONBAR_Y, SPR_ARMSICONS, slide.firstWeapon, 0);
		
		// draw ammo, note we draw ammo of firstweapon NOT current weapon, for slide effect
		DrawWeaponAmmo((AMMO_X + slide.wpn_offset + slide.ammo_offset), AMMO_Y, slide.firstWeapon);
		
		// draw other weapons
		w = slide.firstWeapon;
		x = STATUS_X + 64 + slide.wpn_offset + 1;
		for(;;)
		{
			if (++w >= WPN_COUNT) w = 0;
			if (w==slide.firstWeapon) break;
			
			if (player->weapons[w].hasWeapon)
			{
				draw_sprite(x, WEAPONBAR_Y, SPR_ARMSICONS, w, RIGHT);
				x += 16;
			}
		}
		
		DrawAirLeft((SCREEN_WIDTH/2) - (5*8), ((SCREEN_HEIGHT)/2)-16);
	}
}

void DrawAirLeft(int x, int y)
{
	if (player->airshowtimer)
	{
		draw_sprite(x, y, SPR_AIR, (player->airleft%30 > 10) ? 0:1, RIGHT);
		
		if (player->airshowtimer%6 < 4)
			DrawNumber(x+32, y, player->airleft/10);
	}
}

void DrawWeaponAmmo(int x, int y, int wpn)
{
	// draw slash
	if (!player->hurt_flash_state || game.mode != GM_NORMAL)
	{
		draw_sprite(x, y+8, SPR_WHITENUMBERS, 11, 0);
	}
	
	if (!player->weapons[wpn].maxammo)
	{	// ammo is "not applicable"
		x += 16;
		draw_sprite(x, y, SPR_NAAMMO, 0, 0);
		draw_sprite(x, y+8, SPR_NAAMMO, 0, 0);
	}
	else
	{
		DrawNumber(x, y, player->weapons[wpn].ammo);
		DrawNumber(x, y+8, player->weapons[wpn].maxammo);
	}
}

void DrawWeaponLevel(int x, int y, int wpn)
{
	int level = (player->weapons[wpn].level + 1);
	if (wpn == WPN_NONE) level = 0;
	
	draw_sprite(x, y, SPR_XPLEVELICON, 0, 0);
	draw_sprite(x+16, y, SPR_WHITENUMBERS, level, 0);
}


static void RunStatusBar(void)
{
	// handle slowly decreasing the health when player is hurt
	// note how it only decrements while it's actually visible--i thought that was a nice touch
	if (!player->hurt_flash_state)
	{
		RunPercentBar(&PHealthBar, player->hp);
	}
	
	if (game.frozen || player->inputs_locked) return;
	if (fade.getstate() != FS_NO_FADE) return;
	
	// sliding effect when changing weapons
	if (slide.lv_offset)
	{	// next weapon
		slide.lv_offset += slide.move_dir;
		if (--slide.timer > 0)
		{
			slide.wpn_offset += slide.move_dir;
		}
		else
		{
			if (!slide.timer) slide.firstWeapon = player->curWeapon;
			slide.wpn_offset = slide.lv_offset;
			slide.ammo_offset = 0;
		}
	}
	else
	{
		slide.firstWeapon = player->curWeapon;
	}
}


// start the slide effect. if dir = LEFT, slides left (next weapon), if RIGHT does "prev weapon"
// newwpn = the weapon to change to
void weapon_slide(int dir, int newwpn)
{
int sign;
	if (slide.lv_offset) slide.firstWeapon = player->curWeapon;		// if already sliding change immediately
	if (dir==RIGHT) sign = -1; else sign = 1;
	
	slide.lv_offset = SLIDE_LV_OFFSET * sign;
	slide.timer = SLIDE_TIMER_START;
	slide.ammo_offset = 16 * sign;
	slide.move_dir = -2 * sign;
	player->curWeapon = newwpn;
}

// the opening slide effect on load/new game
void weapon_introslide()
{
	if (player->curWeapon == WPN_NONE)
	{
		weapon_slide(LEFT, player->curWeapon);
		return;
	}
	
	stat_PrevWeapon(true);
	slide.lv_offset = 0;
	stat_NextWeapon(true);
}

// switches to the next weapon in inventory
void stat_NextWeapon(bool quiet)
{
int w;

	w = player->curWeapon;
	if (w == WPN_NONE) return;
	
	for(;;)
	{
		if (++w >= WPN_COUNT) w = 0;
		
		if (player->weapons[w].hasWeapon || w == player->curWeapon)
		{
			if (!quiet) sound(SND_SWITCH_WEAPON);
			weapon_slide(LEFT, w);
			return;
		}
	} while(w != player->curWeapon);
}

// switches to the previous weapon in inventory
void stat_PrevWeapon(bool quiet)
{
int w;

	w = player->curWeapon;
	if (w == WPN_NONE) return;
	
	for(;;)
	{
		if (--w < 0) w = WPN_COUNT-1;
		
		if (player->weapons[w].hasWeapon || w == player->curWeapon)
		{
			if (!quiet) sound(SND_SWITCH_WEAPON);
			weapon_slide(RIGHT, w);
			return;
		}
	}
}


void InitPercentBar(PercentBar *bar, int starting_value)
{
	PHealthBar.displayed_value = starting_value;
	PHealthBar.dectimer = 0;
}

void RunPercentBar(PercentBar *bar, int current_value)
{
	if (current_value != bar->displayed_value)
	{
		if (current_value > bar->displayed_value)
		{
			bar->displayed_value = current_value;
		}
		else
		{
			if (++bar->dectimer > 0x1e)
			{
				bar->displayed_value--;
			}
		}
	}
	else bar->dectimer = 0;
}

void DrawPercentBar(PercentBar *bar, int x, int y, int curvalue, int maxvalue, int width)
{
	if (bar->displayed_value != curvalue)
		DrawPercentage(x, y, SPR_HEALTHFILL, 1, bar->displayed_value, maxvalue, width);
		
	DrawPercentage(x, y, SPR_HEALTHFILL, 0, curvalue, maxvalue, width);
}

/*
void c------------------------------() {}
*/

// draws number "num" at x,y.
// leading zeroes are omitted, however, the space for them is still
// skipped over (left blank). thus it always leaves space for 3 digits.
void DrawNumber(int x, int y, int num)
{
static const int numtable[3] = { 1000, 100, 10 };
int place, digit, total;

	if (num > 9999) num = 9999;
	
	place = total = 0;
	while(place < 3)
	{
		digit = 0;
		while(num >= numtable[place])
		{
			num -= numtable[place];
			digit++;
		}
		
		total += digit;
		
		if (total)
			draw_sprite(x+(place*8), y, SPR_WHITENUMBERS, digit);
		
		place++;
	}
	
	draw_sprite(x+(3*8), y, SPR_WHITENUMBERS, num);
}


void DrawPercentage(int x, int y, int fill_sprite, int fsframe, int curvalue, int maxvalue, int width_at_max)
{
	if (curvalue < 0) curvalue = 0;
	if (curvalue > 0 || maxvalue==0)
	{
		int fillwidth;
		
		if (curvalue >= maxvalue)
		{
			fillwidth = width_at_max;
		}
		else
		{
			fillwidth = (int)(((float)width_at_max / (float)maxvalue) * (float)curvalue);
			if (!fillwidth) return;
		}
		
		draw_sprite_clip_width(x, y, fill_sprite, fsframe, fillwidth);
	}
}

// draws a given number using sprite 's' as the font
// the numbers are drawn right-aligned to "x".
void DrawNumberRAlign(int x, int y, int s, int num)
{
   char str[50];
   int i, len;
   int fontwidth = sprites[s].w;

	snprintf(str, sizeof(str), "%d", num);
	x -= strlen(str) * fontwidth;
	
	len = strlen(str);
	for(i=0;i<len;i++)
	{
		draw_sprite(x, y, s, str[i] - '0');
		x += fontwidth;
	}
}

void DrawTwoDigitNumber(int x, int y, int num)
{
	DrawDigit(x+0, y, num/10);
	DrawDigit(x+8, y, num%10);
}

void DrawDigit(int x, int y, int digit)
{
	draw_sprite(x, y, SPR_WHITENUMBERS, digit);
}

/*
void c------------------------------() {}
*/

// Nikumaru Counter
void niku_run()
{
	if (player->equipmask & EQUIP_NIKUMARU)
	{
		if (!game.frozen && !player->inputs_locked)
		{
			if (game.counter < 300000)	// 100'00"0
				game.counter++;
		}
	}
	else
	{
		game.counter = 0;
	}
}

void niku_draw(int value, bool force_white)
{
	int clkframe = (game.counter % 30) <= 10;
	if (game.frozen || player->inputs_locked || force_white) clkframe = 0;
	
	draw_sprite(NIKU_X, NIKU_Y, SPR_NIKU_CLOCK, clkframe);
	
	int mins = (value / 3000);		// the game runs at 50 fps
	int secs = (value / 50) % 60;
	int tens = (value / 5) % 10;
	
	DrawNumber(NIKU_X, NIKU_Y, mins);
	DrawTwoDigitNumber(NIKU_X+36, NIKU_Y, secs);
	DrawDigit(NIKU_X+56, NIKU_Y, tens);
	
	draw_sprite(NIKU_X+30, NIKU_Y, SPR_NIKU_PUNC);
}




