//Copyright Paul Reiche, Fred Ford. 1992-2002

/*
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include "menustat.h"

#include "colors.h"
#include "controls.h"
#include "units.h"
#include "options.h"
#include "setup.h"
#include "gamestr.h"
#include "libs/graphics/gfx_common.h"
#include "libs/tasklib.h"
#include "libs/log.h"

extern Task flash_task;
extern RECT flash_rect;

static BYTE GetEndMenuState (BYTE BaseState);
static BYTE GetBeginMenuState (BYTE BaseState);
static BYTE FixMenuState (BYTE BadState);
static BYTE NextMenuState (BYTE BaseState, BYTE CurState);
static BYTE PreviousMenuState (BYTE BaseState, BYTE CurState);
static BOOLEAN GetAlternateMenu (BYTE *BaseState, BYTE *CurState);
static BYTE ConvertAlternateMenu (BYTE BaseState, BYTE NewState);


/* Draw the blue background for PC Menu Text, with a border around it.
 * The specified rectangle includes the border. */
static void
DrawPCMenuFrame (RECT *r)
{
	// Draw the top and left of the outer border.
	// This actually draws a rectangle, but the bottom and right parts
	// are drawn over in the next step.
	SetContextForeGroundColor (PCMENU_TOP_LEFT_BORDER_COLOR);
	DrawRectangle (r);

	// Draw the right and bottom of the outer border.
	// This actually draws a rectangle, with the top and left segments just
	// within the total area, but these segments are drawn over in the next
	// step.
	r->corner.x++;
	r->corner.y++;
	r->extent.height--;
	r->extent.width--;
	SetContextForeGroundColor (PCMENU_BOTTOM_RIGHT_BORDER_COLOR);
	DrawRectangle (r);

	// Draw the background.
	r->extent.height--;
	r->extent.width--;
	SetContextForeGroundColor (PCMENU_BACKGROUND_COLOR);
	DrawFilledRectangle (r);
}

#define ALT_MANIFEST 0x80
#define ALT_EXIT_MANIFEST 0x81

static UNICODE pm_crew_str[128];
static UNICODE pm_fuel_str[128];

/* Actually display the menu text */
static void
DrawPCMenu (BYTE beg_index, BYTE end_index, BYTE NewState, BYTE hilite, RECT *r)
{
#define PC_MENU_HEIGHT 8
	BYTE pos;
	COUNT i;
	int num_items;
	FONT OldFont;
	TEXT t;
	UNICODE buf[256];

	pos = beg_index + NewState;
	num_items = 1 + end_index - beg_index;
	r->corner.x -= 1;
	r->extent.width += 1;
	DrawFilledRectangle (r);
	if (num_items * PC_MENU_HEIGHT > r->extent.height)
		log_add (log_Error, "Warning, no room for all menu items!");
	else
		r->corner.y += (r->extent.height - num_items * PC_MENU_HEIGHT) / 2;
	r->extent.height = num_items * PC_MENU_HEIGHT + 4;
	DrawPCMenuFrame (r);
	OldFont = SetContextFont (StarConFont);
	t.align = ALIGN_LEFT;
	t.baseline.x = r->corner.x + 2;
	t.baseline.y = r->corner.y + PC_MENU_HEIGHT -1;
	t.pStr = buf;
	t.CharCount = (COUNT)~0;
	r->corner.x++;
	r->extent.width -= 2;
	for (i = beg_index; i <= end_index; i++)
	{
		utf8StringCopy (buf, sizeof buf,
						(i == PM_FUEL) ? pm_fuel_str :
						(i == PM_CREW) ? pm_crew_str :
						GAME_STRING (MAINMENU_STRING_BASE + i));
		if (hilite && pos == i)
		{
			// Currently selected menu option.
			
			// Draw the background of the selection.
			SetContextForeGroundColor (PCMENU_SELECTION_BACKGROUND_COLOR);
			r->corner.y = t.baseline.y - PC_MENU_HEIGHT + 2;
			r->extent.height = PC_MENU_HEIGHT - 1;
			DrawFilledRectangle (r);

			// Draw the text of the selected item.
			SetContextForeGroundColor (PCMENU_SELECTION_TEXT_COLOR);
			font_DrawText (&t);
		}
		else
		{
			// Draw the text of an unselected item.
			SetContextForeGroundColor (PCMENU_TEXT_COLOR);
			font_DrawText (&t);
		}
		t.baseline.y += PC_MENU_HEIGHT;
	}
	SetContextFont (OldFont);
}

/* Determine the last text item to display */
static BYTE
GetEndMenuState (BYTE BaseState)
{
	switch (BaseState)
	{
		case PM_SCAN:
		case PM_STARMAP:
			return PM_NAVIGATE;
			break;
		case PM_MIN_SCAN:
			return PM_LAUNCH_LANDER;
			break;
		case PM_SAVE_GAME:
			return PM_EXIT_GAME_MENU;
			break;
		case PM_CONVERSE:
			return PM_ENCOUNTER_GAME_MENU;
			break;
		case PM_FUEL:
			return PM_EXIT_OUTFIT;
			break;
		case PM_CREW:
			return PM_EXIT_SHIPYARD;
			break;
		case PM_SOUND_ON:
			return PM_EXIT_SETTINGS;
			break;
		case PM_ALT_SCAN:
		case PM_ALT_STARMAP:
			return PM_ALT_NAVIGATE;
			break;
		case PM_ALT_CARGO:
			return PM_ALT_EXIT_MANIFEST;
			break;
		case PM_ALT_MSCAN:
			return PM_ALT_EXIT_SCAN;
			break;
	}
	return BaseState;
}

static BYTE
GetBeginMenuState (BYTE BaseState)
{
	return BaseState;
}

/* Correct Menu State for cases where the Menu shouldn't move */
static BYTE
FixMenuState (BYTE BadState)
{
	switch (BadState)
	{
		case PM_SOUND_ON:
			if (GLOBAL (glob_flags) & SOUND_DISABLED)
				return PM_SOUND_OFF;
			else
				return PM_SOUND_ON;
		case PM_MUSIC_ON:
			if (GLOBAL (glob_flags) & MUSIC_DISABLED)
				return PM_MUSIC_OFF;
			else
				return PM_MUSIC_ON;
		case PM_CYBORG_OFF:
			return (PM_CYBORG_OFF +
				((BYTE)(GLOBAL (glob_flags) & COMBAT_SPEED_MASK) >>
				COMBAT_SPEED_SHIFT));
	}
	return BadState;
}

/* Choose the next menu to hilight in the 'forward' direction */ 
static BYTE
NextMenuState (BYTE BaseState, BYTE CurState)
{
	BYTE NextState;
	BYTE AdjBase = BaseState;

	if (BaseState == PM_STARMAP)
		AdjBase--;

	switch (AdjBase + CurState)
	{
		case PM_SOUND_ON:
		case PM_SOUND_OFF:
			NextState = PM_MUSIC_ON;
			break;
		case PM_MUSIC_ON:
		case PM_MUSIC_OFF:
			NextState = PM_CYBORG_OFF;
			break;
		case PM_CYBORG_OFF:
		case PM_CYBORG_NORMAL:
		case PM_CYBORG_DOUBLE:
		case PM_CYBORG_SUPER:
			NextState = PM_CHANGE_CAPTAIN;
			break;
		default:
			NextState = AdjBase + CurState + 1;
	}	
	if (NextState > GetEndMenuState (BaseState))
		NextState = GetBeginMenuState (BaseState);
	return (FixMenuState (NextState) - AdjBase);
}

/* Choose the next menu to hilight in the 'back' direction */ 
BYTE
PreviousMenuState (BYTE BaseState, BYTE CurState)
{
	SWORD NextState;
	BYTE AdjBase = BaseState;

	if (BaseState == PM_STARMAP)
		AdjBase--;

	switch (AdjBase + CurState)
	{
		case PM_SOUND_OFF:
			NextState = PM_EXIT_SETTINGS;
			break;
		case PM_MUSIC_ON:
		case PM_MUSIC_OFF:
			NextState = PM_SOUND_ON;
			break;
		case PM_CYBORG_OFF:
		case PM_CYBORG_NORMAL:
		case PM_CYBORG_DOUBLE:
		case PM_CYBORG_SUPER:
			NextState = PM_MUSIC_ON;
			break;
		case PM_CHANGE_CAPTAIN:
			NextState = PM_CYBORG_OFF;
			break;
		default:
			NextState = AdjBase + CurState - 1;
	}	
	if (NextState < GetBeginMenuState (BaseState))
		NextState = GetEndMenuState (BaseState);
	return (FixMenuState ((BYTE)NextState) - AdjBase);
}


/* When using PC hierarchy, convert 3do->PC */
static BOOLEAN
GetAlternateMenu (BYTE *BaseState, BYTE *CurState)
{
	BYTE AdjBase = *BaseState;
	BYTE adj = 0;
	if (*BaseState == PM_STARMAP)
	{
		AdjBase--;
		adj = 1;
	}
	if (*CurState & 0x80)
	{
		switch (*CurState)
		{
			case ALT_MANIFEST:
				*BaseState = PM_ALT_SCAN + adj;
				*CurState = PM_ALT_MANIFEST - PM_ALT_SCAN - adj;
				return TRUE;
			case ALT_EXIT_MANIFEST:
				*BaseState = PM_ALT_CARGO;
				*CurState = PM_ALT_EXIT_MANIFEST - PM_ALT_CARGO;
				return TRUE;
		}
		log_add (log_Error, "Unknown state combination: %d, %d",
				*BaseState, *CurState);
		return FALSE;
	}
	else
	{
		switch (AdjBase + *CurState)
		{
			case PM_SCAN:
				*BaseState = PM_ALT_SCAN;
				*CurState = PM_ALT_SCAN - PM_ALT_SCAN;
				return TRUE;
			case PM_STARMAP:
				*BaseState = PM_ALT_SCAN + adj;
				*CurState = PM_ALT_STARMAP - PM_ALT_SCAN - adj;
				return TRUE;
			case PM_DEVICES:
				*BaseState = PM_ALT_CARGO;
				*CurState = PM_ALT_DEVICES - PM_ALT_CARGO;
				return TRUE;
			case PM_CARGO:
				*BaseState = PM_ALT_CARGO;
				*CurState = PM_ALT_CARGO - PM_ALT_CARGO;
				return TRUE;
			case PM_ROSTER:
				*BaseState = PM_ALT_CARGO;
				*CurState = PM_ALT_ROSTER - PM_ALT_CARGO;
				return TRUE;
			case PM_GAME_MENU:
				*BaseState = PM_ALT_SCAN + adj;
				*CurState = PM_ALT_GAME_MENU - PM_ALT_SCAN - adj;
				return TRUE;
			case PM_NAVIGATE:
				*BaseState = PM_ALT_SCAN + adj;
				*CurState = PM_ALT_NAVIGATE - PM_ALT_SCAN - adj;
				return TRUE;
			case PM_MIN_SCAN:
				*BaseState = PM_ALT_MSCAN;
				*CurState = PM_ALT_MSCAN - PM_ALT_MSCAN;
				return TRUE;
			case PM_ENE_SCAN:
				*BaseState = PM_ALT_MSCAN;
				*CurState = PM_ALT_ESCAN - PM_ALT_MSCAN;
				return TRUE;
			case PM_BIO_SCAN:
				*BaseState = PM_ALT_MSCAN;
				*CurState = PM_ALT_BSCAN - PM_ALT_MSCAN;
				return TRUE;
			case PM_EXIT_SCAN:
				*BaseState = PM_ALT_MSCAN;
				*CurState = PM_ALT_EXIT_SCAN - PM_ALT_MSCAN;
				return TRUE;
			case PM_AUTO_SCAN:
				*BaseState = PM_ALT_MSCAN;
				*CurState = PM_ALT_ASCAN - PM_ALT_MSCAN;
				return TRUE;
			case PM_LAUNCH_LANDER:
				*BaseState = PM_ALT_MSCAN;
				*CurState = PM_ALT_DISPATCH - PM_ALT_MSCAN;
				return TRUE;
		}
		return FALSE;
	}
}

/* When using PC hierarchy, convert PC->3DO */
static BYTE
ConvertAlternateMenu (BYTE BaseState, BYTE NewState)
{
	switch (BaseState + NewState)
	{
		case PM_ALT_SCAN:
			return (PM_SCAN - PM_SCAN);
		case PM_ALT_STARMAP:
			return (PM_STARMAP - PM_SCAN);
		case PM_ALT_MANIFEST:
			return (ALT_MANIFEST);
		case PM_ALT_GAME_MENU:
			return (PM_GAME_MENU - PM_SCAN);
		case PM_ALT_NAVIGATE:
			return (PM_NAVIGATE - PM_SCAN);
		case PM_ALT_CARGO:
			return (PM_CARGO - PM_SCAN);
		case PM_ALT_DEVICES:
			return (PM_DEVICES - PM_SCAN);
		case PM_ALT_ROSTER:
			return (PM_ROSTER - PM_SCAN);
		case PM_ALT_EXIT_MANIFEST:
			return (ALT_EXIT_MANIFEST);
		case PM_ALT_MSCAN:
			return (PM_MIN_SCAN - PM_MIN_SCAN);
		case PM_ALT_ESCAN:
			return (PM_ENE_SCAN - PM_MIN_SCAN);
		case PM_ALT_BSCAN:
			return (PM_BIO_SCAN - PM_MIN_SCAN);
		case PM_ALT_ASCAN:
			return (PM_AUTO_SCAN - PM_MIN_SCAN);
		case PM_ALT_DISPATCH:
			return (PM_LAUNCH_LANDER - PM_MIN_SCAN);
		case PM_ALT_EXIT_SCAN:
			return (PM_EXIT_SCAN - PM_MIN_SCAN);
	}
	return (NewState);
}

BOOLEAN
DoMenuChooser (MENU_STATE *pMS, BYTE BaseState)
{
	BYTE NewState = pMS->CurState;
	BYTE OrigBase = BaseState;
	BOOLEAN useAltMenu = FALSE;
	if (optWhichMenu == OPT_PC)
		useAltMenu = GetAlternateMenu (&BaseState, &NewState);
	if (PulsedInputState.menu[KEY_MENU_LEFT] ||
			PulsedInputState.menu[KEY_MENU_UP])
		NewState = PreviousMenuState (BaseState, NewState);
	else if (PulsedInputState.menu[KEY_MENU_RIGHT] ||
			PulsedInputState.menu[KEY_MENU_DOWN])
		NewState = NextMenuState (BaseState, NewState);
	else if (useAltMenu && PulsedInputState.menu[KEY_MENU_SELECT])
	{
		NewState = ConvertAlternateMenu (BaseState, NewState);
		if (NewState == ALT_MANIFEST)
		{
			DrawMenuStateStrings (PM_ALT_CARGO, 0);
			pMS->CurState = PM_CARGO - PM_SCAN;
			return TRUE;
		}
		if (NewState == ALT_EXIT_MANIFEST)
		{
			if (OrigBase == PM_SCAN)
				DrawMenuStateStrings (PM_ALT_SCAN,
						PM_ALT_MANIFEST - PM_ALT_SCAN);
			else
				DrawMenuStateStrings (PM_ALT_STARMAP,
						PM_ALT_MANIFEST - PM_ALT_STARMAP);
			pMS->CurState = ALT_MANIFEST;
			return TRUE;
		}
		return FALSE;
	}
	else if ((optWhichMenu == OPT_PC) &&
			PulsedInputState.menu[KEY_MENU_CANCEL] && 
			(BaseState == PM_ALT_CARGO))
	{
		if (OrigBase == PM_SCAN)
			DrawMenuStateStrings (PM_ALT_SCAN,
					PM_ALT_MANIFEST - PM_ALT_SCAN);
		else
			DrawMenuStateStrings (PM_ALT_STARMAP,
					PM_ALT_MANIFEST - PM_ALT_STARMAP);
		pMS->CurState = ALT_MANIFEST;
		return TRUE;
	}
	else
		return FALSE;

	DrawMenuStateStrings (BaseState, NewState);
	if (useAltMenu)
		NewState = ConvertAlternateMenu (BaseState, NewState);
	pMS->CurState = NewState;
	SleepThread (ONE_SECOND / 20);
	return TRUE;
}

void
DrawMenuStateStrings (BYTE beg_index, SWORD NewState)
{
	BYTE end_index;
	RECT r;
	STAMP s;
	CONTEXT OldContext;
	BYTE hilite = 1;
	extern FRAME PlayFrame;

	if (NewState < 0)
	{
		NewState = -NewState;
		hilite = 0;
	}

	if (optWhichMenu == OPT_PC)
	{
		BYTE tmpState = (BYTE)NewState;
		GetAlternateMenu (&beg_index, &tmpState);
		NewState = tmpState;
	}

	if (beg_index == PM_STARMAP)
		NewState--;
	end_index = GetEndMenuState (beg_index);

	s.frame = 0;
	if (NewState <= end_index - beg_index)
		s.frame = SetAbsFrameIndex (PlayFrame, beg_index + NewState);

	LockMutex (GraphicsLock);
	OldContext = SetContext (StatusContext);
	GetContextClipRect (&r);
	s.origin.x = RADAR_X - r.corner.x;
	s.origin.y = RADAR_Y - r.corner.y;
	r.corner.x = s.origin.x - 1;
	r.corner.y = s.origin.y - 11;
	r.extent.width = RADAR_WIDTH + 2;
	BatchGraphics ();
	SetContextForeGroundColor (
			BUILD_COLOR (MAKE_RGB15 (0x0A, 0x0A, 0x0A), 0x08));
	if (s.frame && optWhichMenu == OPT_PC)
	{
		if (beg_index == PM_CREW)
			snprintf (pm_crew_str, sizeof pm_crew_str, "%s(%d)",
					 GAME_STRING (MAINMENU_STRING_BASE + PM_CREW),
					 GLOBAL (CrewCost));
		if (beg_index == PM_FUEL)
			snprintf (pm_fuel_str, sizeof pm_fuel_str, "%s(%d)",
					 GAME_STRING (MAINMENU_STRING_BASE + PM_FUEL),
					 GLOBAL (FuelCost));
		if (beg_index == PM_SOUND_ON)
		{
			end_index = beg_index + 5;
			switch (beg_index + NewState)
			{
				case PM_SOUND_ON:
				case PM_SOUND_OFF:
					NewState = 0;
					break;
				case PM_MUSIC_ON:
				case PM_MUSIC_OFF:
					NewState = 1;
					break;
				case PM_CYBORG_OFF:
				case PM_CYBORG_NORMAL:
				case PM_CYBORG_DOUBLE:
				case PM_CYBORG_SUPER:
					NewState = 2;
					break;
				case PM_CHANGE_CAPTAIN:
					NewState = 3;
					break;
				case PM_CHANGE_SHIP:
					NewState = 4;
					break;
				case PM_EXIT_SETTINGS:
					NewState = 5;
					break;
			}
		}
		r.extent.height = RADAR_HEIGHT + 11;
		DrawPCMenu (beg_index, end_index, (BYTE)NewState, hilite, &r);
		s.frame = 0;
	}
	else
	{
		if (optWhichMenu == OPT_PC)
		{
			r.corner.x -= 1;
			r.extent.width += 1;
			r.extent.height = RADAR_HEIGHT + 11;
		}
		else
			r.extent.height = 11;
		DrawFilledRectangle (&r);
	}
	if (s.frame)
	{
		DrawStamp (&s);
		switch (beg_index + NewState)
		{
			TEXT t;
			UNICODE buf[20];

			case PM_CREW:
				t.baseline.x = s.origin.x + RADAR_WIDTH - 2;
				t.baseline.y = s.origin.y + RADAR_HEIGHT - 2;
				t.align = ALIGN_RIGHT;
				t.CharCount = (COUNT)~0;
				t.pStr = buf;
				snprintf (buf, sizeof buf, "%u", GLOBAL (CrewCost));
				SetContextFont (TinyFont);
				SetContextForeGroundColor (THREEDOMENU_TEXT_COLOR);
				font_DrawText (&t);
				break;
			case PM_FUEL:
				t.baseline.x = s.origin.x + RADAR_WIDTH - 2;
				t.baseline.y = s.origin.y + RADAR_HEIGHT - 2;
				t.align = ALIGN_RIGHT;
				t.CharCount = (COUNT)~0;
				t.pStr = buf;
				snprintf (buf, sizeof buf, "%u", GLOBAL (FuelCost));
				SetContextFont (TinyFont);
				SetContextForeGroundColor (THREEDOMENU_TEXT_COLOR);
				font_DrawText (&t);
				break;
		}
	}
	UnbatchGraphics ();
	if (flash_task
			&& flash_rect.corner.x == RADAR_X
			&& flash_rect.corner.y == RADAR_Y
			&& flash_rect.extent.width == RADAR_WIDTH
			&& flash_rect.extent.height == RADAR_HEIGHT)
		SetFlashRect (SFR_MENU_3DO);
	SetContext (OldContext);
	UnlockMutex (GraphicsLock);
}

