//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 "../build.h"
#include "../colors.h"
#include "../gendef.h"
#include "../starmap.h"
#include "../encount.h"
#include "../gamestr.h"
#include "../controls.h"
#include "../save.h"
#include "../settings.h"
#include "../shipcont.h"
#include "../setup.h"
#include "../state.h"
#include "../sis.h"
		// for ClearSISRect()
#include "../grpinfo.h"
#include "../sounds.h"
#include "../util.h"
#include "../hyper.h"
		// for SaveSisHyperState()
#include "planets.h"
		// for SaveSolarSysLocation() and tests
#include "libs/strlib.h"


// If DEBUG_DEVICES is defined, the device list shown in the game will
// include the pictures of all devices defined, regardless of which
// devices the player actually possesses.
//#define DEBUG_DEVICES

#define DEVICE_ICON_WIDTH  16
#define DEVICE_ICON_HEIGHT 16

#define DEVICE_ORG_Y       33
#define DEVICE_SPACING_Y   (DEVICE_ICON_HEIGHT + 2)

#define DEVICE_COL_0       4
#define DEVICE_COL_1       40

#define DEVICE_SEL_ORG_X  (DEVICE_COL_0 + DEVICE_ICON_WIDTH)
#define DEVICE_SEL_WIDTH  (FIELD_WIDTH + 1 - DEVICE_SEL_ORG_X + 1)

#define ICON_OFS_Y         1
#define NAME_OFS_Y         2
#define TEXT_BASELINE      6
#define TEXT_SPACING_Y     7

#define MAX_VIS_DEVICES    ((129 - DEVICE_ORG_Y) / DEVICE_SPACING_Y)


typedef enum
{
	DEVICE_FAILURE = 0,
	DEVICE_SUCCESS,
	DEVICE_SUCCESS_NO_SOUND,
} DeviceStatus;

typedef struct
{
	BYTE list[NUM_DEVICES];
			// List of all devices player has
	COUNT count;
			// Number of devices in the list
	COUNT topIndex;
			// Index of the top device displayed
} DEVICES_STATE;


#if 0
static void
EraseDevicesBackground (void)
{
	RECT r;

	r.corner.x = 2 + 1;
	r.extent.width = FIELD_WIDTH + 1 - 2;
	r.corner.y = DEVICE_ORG_Y;
	r.extent.height = MAX_VIS_DEVICES * DEVICE_SPACING_Y;
	SetContextForeGroundColor (DEVICES_BACK_COLOR);
	DrawFilledRectangle (&r);
}
#endif

static void
DrawDevice (COUNT device, COUNT pos, bool selected)
{
	RECT r;
	TEXT t;

	t.align = ALIGN_CENTER;
	t.baseline.x = DEVICE_COL_1;

	r.extent.width = DEVICE_SEL_WIDTH;
	r.extent.height = TEXT_SPACING_Y * 2;
	r.corner.x = DEVICE_SEL_ORG_X;

	// draw line background
	r.corner.y = DEVICE_ORG_Y + pos * DEVICE_SPACING_Y + NAME_OFS_Y;
	SetContextForeGroundColor (selected ?
			DEVICES_SELECTED_BACK_COLOR : DEVICES_BACK_COLOR);
	DrawFilledRectangle (&r);

	SetContextFont (TinyFont);

	// print device name
	SetContextForeGroundColor (selected ?
			DEVICES_SELECTED_NAME_COLOR : DEVICES_NAME_COLOR);
	t.baseline.y = r.corner.y + TEXT_BASELINE;
	t.pStr = GAME_STRING (device + DEVICE_STRING_BASE + 1);
	t.CharCount = utf8StringPos (t.pStr, ' ');
	font_DrawText (&t);
	t.baseline.y += TEXT_SPACING_Y;
	t.pStr = skipUTF8Chars (t.pStr, t.CharCount + 1);
	t.CharCount = (COUNT)~0;
	font_DrawText (&t);
}

static void
DrawDevicesDisplay (DEVICES_STATE *devState)
{
	TEXT t;
	RECT r;
	STAMP s;
	COORD cy;
	COUNT i;

	r.corner.x = 2;
	r.corner.y = 20;
	r.extent.width = FIELD_WIDTH + 1;
	// XXX: Shouldn't the height be 1 less? This draws the bottom border
	//   1 pixel too low. Or if not, why do we need another box anyway?
	r.extent.height = 129 - r.corner.y;
	DrawStarConBox (&r, 1,
			SHADOWBOX_MEDIUM_COLOR, SHADOWBOX_DARK_COLOR,
			TRUE, DEVICES_BACK_COLOR);

	// print the "DEVICES" title
	SetContextFont (StarConFont);
	t.baseline.x = (STATUS_WIDTH >> 1) - 1;
	t.baseline.y = r.corner.y + 7;
	t.align = ALIGN_CENTER;
	t.pStr = GAME_STRING (DEVICE_STRING_BASE);
	t.CharCount = (COUNT)~0;
	SetContextForeGroundColor (DEVICES_SELECTED_NAME_COLOR);
	font_DrawText (&t);

	s.origin.x = DEVICE_COL_0;
	cy = DEVICE_ORG_Y;

	// draw device icons and print names
	for (i = 0; i < MAX_VIS_DEVICES; ++i, cy += DEVICE_SPACING_Y)
	{
		COUNT devIndex = devState->topIndex + i;

		if (devIndex >= devState->count)
			break;

		// draw device icon
		s.origin.y = cy + ICON_OFS_Y;
		s.frame = SetAbsFrameIndex (MiscDataFrame,
				77 + devState->list[devIndex]);
		DrawStamp (&s);

		DrawDevice (devState->list[devIndex], i, false);
	}
}

static void
DrawDevices (DEVICES_STATE *devState, COUNT OldDevice, COUNT NewDevice)
{
	BatchGraphics ();

	SetContext (StatusContext);

	if (OldDevice > NUM_DEVICES)
	{	// Asked for the initial display or refresh
		DrawDevicesDisplay (devState);

		// do not draw unselected again this time
		OldDevice = NewDevice;
	}

	if (OldDevice != NewDevice)
	{	// unselect the previous element
		DrawDevice (devState->list[OldDevice], OldDevice - devState->topIndex,
				false);
	}

	if (NewDevice < NUM_DEVICES)
	{	// select the new element
		DrawDevice (devState->list[NewDevice], NewDevice - devState->topIndex,
				true);
	}

	UnbatchGraphics ();
}

// Returns TRUE if the broadcaster has been successfully activated,
// and FALSE otherwise.
static BOOLEAN
UseCaster (void)
{
	if (inHQSpace ())
	{
		if (GET_GAME_STATE (ARILOU_SPACE_SIDE) <= 1)
		{
			SET_GAME_STATE (USED_BROADCASTER, 1);
			return TRUE;
		}
		return FALSE;
	}

	if (LOBYTE (GLOBAL (CurrentActivity)) != IN_INTERPLANETARY
			|| !playerInSolarSystem ())
		return FALSE;

	if (playerInPlanetOrbit ()
			&& matchWorld (pSolarSysState, pSolarSysState->pOrbitalDesc,
				1, MATCH_PLANET)
			&& CurStarDescPtr->Index == CHMMR_DEFINED
			&& !GET_GAME_STATE (CHMMR_UNLEASHED))
	{
		// In orbit around the Chenjesu/Mmrnmhrm home planet.
		NextActivity |= CHECK_LOAD;  /* fake a load game */
		GLOBAL (CurrentActivity) |= START_ENCOUNTER;

		EncounterGroup = 0;
		PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP);
		ReinitQueue (&GLOBAL (ip_group_q));
		assert (CountLinks (&GLOBAL (npc_built_ship_q)) == 0);

		SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 1 << 7);
		SaveSolarSysLocation ();
		return TRUE;
	}

	{
		BOOLEAN FoundIlwrath;
		HIPGROUP hGroup;

		FoundIlwrath = (CurStarDescPtr->Index == ILWRATH_DEFINED)
				&& StartSphereTracking (ILWRATH_SHIP);
				// In the Ilwrath home system and they are alive?

		if (!FoundIlwrath &&
				(hGroup = GetHeadLink (&GLOBAL (ip_group_q))))
		{
			// Is an Ilwrath ship in the system?
			IP_GROUP *GroupPtr;

			GroupPtr = LockIpGroup (&GLOBAL (ip_group_q), hGroup);
			FoundIlwrath = (GroupPtr->race_id == ILWRATH_SHIP);
			UnlockIpGroup (&GLOBAL (ip_group_q), hGroup);
		}

		if (FoundIlwrath)
		{
			NextActivity |= CHECK_LOAD; /* fake a load game */
			GLOBAL (CurrentActivity) |= START_ENCOUNTER;

			EncounterGroup = 0;
			PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP);
			ReinitQueue (&GLOBAL (ip_group_q));
			assert (CountLinks (&GLOBAL (npc_built_ship_q)) == 0);

			if (CurStarDescPtr->Index == ILWRATH_DEFINED)
			{
				// Ilwrath home system.
				SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 1 << 4);
			}
			else
			{
				// Ilwrath ship.
				SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 1 << 5);
			}
			
			if (playerInPlanetOrbit ())
				SaveSolarSysLocation ();
			return TRUE;
		}
	}

	return FALSE;
}

static DeviceStatus
InvokeDevice (BYTE which_device)
{
	BYTE val;

	switch (which_device)
	{
		case ROSY_SPHERE_DEVICE:
			val = GET_GAME_STATE (ULTRON_CONDITION);
			if (val)
			{
				SET_GAME_STATE (ULTRON_CONDITION, val + 1);
				SET_GAME_STATE (ROSY_SPHERE_ON_SHIP, 0);
				SET_GAME_STATE (DISCUSSED_ULTRON, 0);
				SET_GAME_STATE (SUPOX_ULTRON_HELP, 0);
				return DEVICE_SUCCESS;
			}
			break;
		case ARTIFACT_2_DEVICE:
			break;
		case ARTIFACT_3_DEVICE:
			break;
		case SUN_EFFICIENCY_DEVICE:
			if (LOBYTE (GLOBAL (CurrentActivity)) == IN_INTERPLANETARY
					&& playerInPlanetOrbit ())
			{
				PlayMenuSound (MENU_SOUND_INVOKED);
				SleepThreadUntil (FadeScreen (FadeAllToWhite, ONE_SECOND * 1)
						+ (ONE_SECOND * 2));
				if (CurStarDescPtr->Index != CHMMR_DEFINED
						|| !matchWorld (pSolarSysState,
								pSolarSysState->pOrbitalDesc,
								1, MATCH_PLANET))
				{
					FadeScreen (FadeAllToColor, ONE_SECOND * 2);
				}
				else
				{
					SET_GAME_STATE (CHMMR_EMERGING, 1);

					EncounterGroup = 0;
					GLOBAL (CurrentActivity) |= START_ENCOUNTER;

					PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP);
					ReinitQueue (&GLOBAL (ip_group_q));
					assert (CountLinks (&GLOBAL (npc_built_ship_q)) == 0);

					CloneShipFragment (CHMMR_SHIP,
							&GLOBAL (npc_built_ship_q), 0);
				}
				return DEVICE_SUCCESS_NO_SOUND;
			}
			break;
		case UTWIG_BOMB_DEVICE:
			SET_GAME_STATE (UTWIG_BOMB, 0);
			GLOBAL (CurrentActivity) &= ~IN_BATTLE;
			GLOBAL_SIS (CrewEnlisted) = (COUNT)~0;
			return DEVICE_SUCCESS;
		case ULTRON_0_DEVICE:
			break;
		case ULTRON_1_DEVICE:
			break;
		case ULTRON_2_DEVICE:
			break;
		case ULTRON_3_DEVICE:
			break;
		case MAIDENS_DEVICE:
			break;
		case TALKING_PET_DEVICE:
			NextActivity |= CHECK_LOAD; /* fake a load game */
			GLOBAL (CurrentActivity) |= START_ENCOUNTER;
			SET_GAME_STATE (GLOBAL_FLAGS_AND_DATA, 0);
			if (inHQSpace ())
			{
				if (GetHeadEncounter ())
				{
					SET_GAME_STATE (SHIP_TO_COMPEL, 1);
				}
				GLOBAL (CurrentActivity) &= ~IN_BATTLE;

				SaveSisHyperState ();
			}
			else
			{
				EncounterGroup = 0;
				if (GetHeadLink (&GLOBAL (ip_group_q)))
				{
					SET_GAME_STATE (SHIP_TO_COMPEL, 1);

					PutGroupInfo (GROUPS_RANDOM, GROUP_SAVE_IP);
					ReinitQueue (&GLOBAL (ip_group_q));
					assert (CountLinks (&GLOBAL (npc_built_ship_q)) == 0);
				}

				if (CurStarDescPtr->Index == SAMATRA_DEFINED)
				{
					SET_GAME_STATE (READY_TO_CONFUSE_URQUAN, 1);
				}
				if (playerInPlanetOrbit ())
					SaveSolarSysLocation ();
			}
			return DEVICE_SUCCESS;
		case AQUA_HELIX_DEVICE:
			val = GET_GAME_STATE (ULTRON_CONDITION);
			if (val)
			{
				SET_GAME_STATE (ULTRON_CONDITION, val + 1);
				SET_GAME_STATE (AQUA_HELIX_ON_SHIP, 0);
				SET_GAME_STATE (DISCUSSED_ULTRON, 0);
				SET_GAME_STATE (SUPOX_ULTRON_HELP, 0);
				return DEVICE_SUCCESS;
			}
			break;
		case CLEAR_SPINDLE_DEVICE:
			val = GET_GAME_STATE (ULTRON_CONDITION);
			if (val)
			{
				SET_GAME_STATE (ULTRON_CONDITION, val + 1);
				SET_GAME_STATE (CLEAR_SPINDLE_ON_SHIP, 0);
				SET_GAME_STATE (DISCUSSED_ULTRON, 0);
				SET_GAME_STATE (SUPOX_ULTRON_HELP, 0);
				return DEVICE_SUCCESS;
			}
			break;
		case UMGAH_HYPERWAVE_DEVICE:
		case BURVIX_HYPERWAVE_DEVICE:
			if (UseCaster ())
				return DEVICE_SUCCESS;
			break;
		case TAALO_PROTECTOR_DEVICE:
			break;
		case EGG_CASING0_DEVICE:
		case EGG_CASING1_DEVICE:
		case EGG_CASING2_DEVICE:
			break;
		case SYREEN_SHUTTLE_DEVICE:
			break;
		case VUX_BEAST_DEVICE:
			break;
		case DESTRUCT_CODE_DEVICE:
			break;
		case PORTAL_SPAWNER_DEVICE:
#define PORTAL_FUEL_COST (10 * FUEL_TANK_SCALE)
			if (inHyperSpace ()
					&& GLOBAL_SIS (FuelOnBoard) >= PORTAL_FUEL_COST)
			{
				/* No DeltaSISGauges because the flagship picture
				 * is currently obscured.
				 */
				GLOBAL_SIS (FuelOnBoard) -= PORTAL_FUEL_COST;
				SET_GAME_STATE (PORTAL_COUNTER, 1);
				return DEVICE_SUCCESS;
			}
			break;
		case URQUAN_WARP_DEVICE:
			break;
		case LUNAR_BASE_DEVICE:
			break;
	}

	return DEVICE_FAILURE;
}

static BOOLEAN
DoManipulateDevices (MENU_STATE *pMS)
{
	DEVICES_STATE *devState = pMS->privData;
	BOOLEAN select, cancel, back, forward;
	BOOLEAN pagefwd, pageback;
	
	select = PulsedInputState.menu[KEY_MENU_SELECT];
	cancel = PulsedInputState.menu[KEY_MENU_CANCEL];
	back = PulsedInputState.menu[KEY_MENU_UP] ||
			PulsedInputState.menu[KEY_MENU_LEFT];
	forward = PulsedInputState.menu[KEY_MENU_DOWN]
			|| PulsedInputState.menu[KEY_MENU_RIGHT];
	pagefwd = PulsedInputState.menu[KEY_MENU_PAGE_DOWN];
	pageback = PulsedInputState.menu[KEY_MENU_PAGE_UP];

	if (GLOBAL (CurrentActivity) & CHECK_ABORT)
		return FALSE;

	if (cancel)
	{
		return FALSE;
	}
	else if (select)
	{
		DeviceStatus status;

		status = InvokeDevice (devState->list[pMS->CurState]);
		if (status == DEVICE_FAILURE)
			PlayMenuSound (MENU_SOUND_FAILURE);
		else if (status == DEVICE_SUCCESS)
			PlayMenuSound (MENU_SOUND_INVOKED);

		return (status == DEVICE_FAILURE);
	}
	else
	{
		SIZE NewTop;
		SIZE NewState;

		NewTop = devState->topIndex;
		NewState = pMS->CurState;
		
		if (back)
			--NewState;
		else if (forward)
			++NewState;
		else if (pagefwd)
			NewState += MAX_VIS_DEVICES;
		else if (pageback)
			NewState -= MAX_VIS_DEVICES;

		if (NewState < 0)
			NewState = 0;
		else if (NewState >= devState->count)
			NewState = devState->count - 1;

		if (NewState < NewTop || NewState >= NewTop + MAX_VIS_DEVICES)
			NewTop = NewState - NewState % MAX_VIS_DEVICES;

		if (NewState != pMS->CurState)
		{
			if (NewTop != devState->topIndex)
			{	// redraw the display
				devState->topIndex = NewTop;
				DrawDevices (devState, (COUNT)~0, NewState);
			}
			else
			{	// move selection to new device
				DrawDevices (devState, pMS->CurState, NewState);
			}
			pMS->CurState = NewState;
		}

		SleepThread (ONE_SECOND / 30);
	}

	return TRUE;
}

SIZE
InventoryDevices (BYTE *pDeviceMap, COUNT Size)
{
	BYTE i;
	SIZE DevicesOnBoard;
	
	DevicesOnBoard = 0;
	for (i = 0; i < NUM_DEVICES && Size > 0; ++i)
	{
		BYTE DeviceState;

		DeviceState = 0;
		switch (i)
		{
			case ROSY_SPHERE_DEVICE:
				DeviceState = GET_GAME_STATE (ROSY_SPHERE_ON_SHIP);
				break;
			case ARTIFACT_2_DEVICE:
				DeviceState = GET_GAME_STATE (ARTIFACT_2_ON_SHIP);
				break;
			case ARTIFACT_3_DEVICE:
				DeviceState = GET_GAME_STATE (ARTIFACT_3_ON_SHIP);
				break;
			case SUN_EFFICIENCY_DEVICE:
				DeviceState = GET_GAME_STATE (SUN_DEVICE_ON_SHIP);
				break;
			case UTWIG_BOMB_DEVICE:
				DeviceState = GET_GAME_STATE (UTWIG_BOMB_ON_SHIP);
				break;
			case ULTRON_0_DEVICE:
				DeviceState = (GET_GAME_STATE (ULTRON_CONDITION) == 1);
				break;
			case ULTRON_1_DEVICE:
				DeviceState = (GET_GAME_STATE (ULTRON_CONDITION) == 2);
				break;
			case ULTRON_2_DEVICE:
				DeviceState = (GET_GAME_STATE (ULTRON_CONDITION) == 3);
				break;
			case ULTRON_3_DEVICE:
				DeviceState = (GET_GAME_STATE (ULTRON_CONDITION) == 4);
				break;
			case MAIDENS_DEVICE:
				DeviceState = GET_GAME_STATE (MAIDENS_ON_SHIP);
				break;
			case TALKING_PET_DEVICE:
				DeviceState = GET_GAME_STATE (TALKING_PET_ON_SHIP);
				break;
			case AQUA_HELIX_DEVICE:
				DeviceState = GET_GAME_STATE (AQUA_HELIX_ON_SHIP);
				break;
			case CLEAR_SPINDLE_DEVICE:
				DeviceState = GET_GAME_STATE (CLEAR_SPINDLE_ON_SHIP);
				break;
			case UMGAH_HYPERWAVE_DEVICE:
				DeviceState = GET_GAME_STATE (UMGAH_BROADCASTERS_ON_SHIP);
				break;
			case TAALO_PROTECTOR_DEVICE:
				DeviceState = GET_GAME_STATE (TAALO_PROTECTOR_ON_SHIP);
				break;
			case EGG_CASING0_DEVICE:
				DeviceState = GET_GAME_STATE (EGG_CASE0_ON_SHIP);
				break;
			case EGG_CASING1_DEVICE:
				DeviceState = GET_GAME_STATE (EGG_CASE1_ON_SHIP);
				break;
			case EGG_CASING2_DEVICE:
				DeviceState = GET_GAME_STATE (EGG_CASE2_ON_SHIP);
				break;
			case SYREEN_SHUTTLE_DEVICE:
				DeviceState = GET_GAME_STATE (SYREEN_SHUTTLE_ON_SHIP);
				break;
			case VUX_BEAST_DEVICE:
				DeviceState = GET_GAME_STATE (VUX_BEAST_ON_SHIP);
				break;
			case DESTRUCT_CODE_DEVICE:
#ifdef NEVER
				DeviceState = GET_GAME_STATE (DESTRUCT_CODE_ON_SHIP);
#endif /* NEVER */
				break;
			case PORTAL_SPAWNER_DEVICE:
				DeviceState = GET_GAME_STATE (PORTAL_SPAWNER_ON_SHIP);
				break;
			case URQUAN_WARP_DEVICE:
				DeviceState = GET_GAME_STATE (PORTAL_KEY_ON_SHIP);
				break;
			case BURVIX_HYPERWAVE_DEVICE:
				DeviceState = GET_GAME_STATE (BURV_BROADCASTERS_ON_SHIP);
				break;
			case LUNAR_BASE_DEVICE:
				DeviceState = GET_GAME_STATE (MOONBASE_ON_SHIP);
				break;
		}

#ifndef DEBUG_DEVICES
		if (DeviceState)
#endif /* DEBUG_DEVICES */
		{
			*pDeviceMap++ = i;
			++DevicesOnBoard;
			--Size;
		}
	}
	
	return DevicesOnBoard;
}

BOOLEAN
DevicesMenu (void)
{
	MENU_STATE MenuState;
	DEVICES_STATE DevicesState;

	memset (&MenuState, 0, sizeof MenuState);
	MenuState.privData = &DevicesState;

	memset (&DevicesState, 0, sizeof DevicesState);

	DevicesState.count = InventoryDevices (DevicesState.list, NUM_DEVICES);
	if (!DevicesState.count)
		return FALSE;

	DrawDevices (&DevicesState, (COUNT)~0, MenuState.CurState);

	SetMenuSounds (MENU_SOUND_ARROWS | MENU_SOUND_PAGEUP | MENU_SOUND_PAGEDOWN,
			MENU_SOUND_SELECT);

	MenuState.InputFunc = DoManipulateDevices;
	DoInput (&MenuState, TRUE);

	SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT);

	if (GLOBAL_SIS (CrewEnlisted) != (COUNT)~0
			&& !(GLOBAL (CurrentActivity) & CHECK_ABORT))
	{
		ClearSISRect (DRAW_SIS_DISPLAY);

		if (!GET_GAME_STATE (PORTAL_COUNTER)
				&& !(GLOBAL (CurrentActivity) & START_ENCOUNTER)
				&& GLOBAL_SIS (CrewEnlisted) != (COUNT)~0)
			return TRUE;
	}
	
	return FALSE;
}

