/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 */

#ifndef STARTREK_H
#define STARTREK_H

#include "common/events.h"
#include "common/list.h"
#include "common/ptr.h"
#include "common/random.h"
#include "common/rect.h"
#include "common/scummsys.h"
#include "common/serializer.h"
#include "common/sinetables.h"
#include "common/str.h"
#include "common/stream.h"
#include "common/system.h"
#include "common/util.h"

#include "gui/saveload-dialog.h"

#include "engines/engine.h"

#include "startrek/action.h"
#include "startrek/awaymission.h"
#include "startrek/graphics.h"
#include "startrek/items.h"
#include "startrek/object.h"
#include "startrek/sound.h"
#include "startrek/space.h"


using Common::SharedPtr;
using Common::String;

namespace Common {
class MacResManager;
}

namespace StarTrek {

class StarTrekEngine;
class Room;
class Console;

typedef String(StarTrekEngine::*TextGetterFunc)(int, uintptr, String *);
// FIXME: Eventually get rid of Common::SharedPtr and dispose of file streams properly
typedef Common::SharedPtr<Common::MemoryReadStreamEndian> FileStream;

const int SAVEGAME_DESCRIPTION_LEN = 30;

struct SavegameMetadata {
	uint32 version;
	char description[SAVEGAME_DESCRIPTION_LEN + 1];

	uint32 saveDate;
	uint16 saveTime;
	byte saveTimeSecs;
	uint32 playTime;

	::Graphics::Surface *thumbnail;

	void setSaveTimeAndDate(TimeDate time) {
		saveDate = ((time.tm_mday & 0xFF) << 24) | (((time.tm_mon + 1) & 0xFF) << 16) | ((time.tm_year + 1900) & 0xFFFF);
		saveTime = ((time.tm_hour & 0xFF) << 8) | ((time.tm_min) & 0xFF);
		saveTimeSecs = time.tm_sec & 0xFF;
	}

	int getDay() {
		return (saveDate >> 24) & 0xFF;
	}
	int getMonth() {
		return (saveDate >> 16) & 0xFF;
	}
	int getYear() {
		return saveDate & 0xFFFF;
	}
	int getHour() {
		return (saveTime >> 8) & 0xFF;
	}
	int getMinute() {
		return saveTime & 0xFF;
	}
};


const int MAX_MENUBUTTONS = 32;
const int TEXTBOX_WIDTH = 26;
const int TEXT_CHARS_PER_LINE = TEXTBOX_WIDTH - 2;
const int MAX_TEXTBOX_LINES = 12;

const int TEXT_INPUT_BUFFER_SIZE = 134;
const int MAX_TEXT_INPUT_LEN = 20;

const int MAX_BUFFERED_WALK_ACTIONS = 32;

const int MAX_BAN_FILES = 16;


enum StarTrekGameType {
	GType_ST25 = 1,
	GType_STJR = 2
};

enum StarTrekGameFeatures {
	GF_DEMO  = (1 << 0),
	GF_CDROM = (1 << 1)
};

enum kDebugLevels {
	kDebugSound =     1 << 0,
	kDebugGraphics =  1 << 1,
	kDebugSavegame =  1 << 2,
	kDebugSpace =     1 << 3,
	kDebugGeneral =   1 << 4
};

enum GameMode {
	GAMEMODE_START = 0,
	GAMEMODE_BRIDGE,
	GAMEMODE_AWAYMISSION,
	GAMEMODE_BEAMDOWN,
	GAMEMODE_BEAMUP
};

enum TextDisplayMode {
	TEXTDISPLAY_WAIT = 0,  // Wait for input before closing text
	TEXTDISPLAY_SUBTITLES, // Automatically continue when speech is done
	TEXTDISPLAY_NONE       // No text displayed
};

enum TextColor {
	TEXTCOLOR_GREY   = 0x88,
	TEXTCOLOR_RED    = 0xa1,
	TEXTCOLOR_YELLOW = 0xb0,
	TEXTCOLOR_BLUE   = 0xc0
};

// Keeps track of data for a list of buttons making up a menu
struct Menu {
	Sprite sprites[MAX_MENUBUTTONS];
	uint16 retvals[MAX_MENUBUTTONS];
	uint32 disabledButtons;
	uint16 numButtons;
	int16 selectedButton;
	Menu *nextMenu;
};

// Special events that can be returned by handleMenuEvents.
// (Normally it returns the "retval" of a pressed button, which is positive.)
enum MenuEvent {
	MENUEVENT_RCLICK_OFFBUTTON = -4,
	MENUEVENT_ENABLEINPUT,          // Makes buttons selectable (occurs after a delay)
	MENUEVENT_RCLICK_ONBUTTON,
	MENUEVENT_LCLICK_OFFBUTTON
};

// Buttons for standard text display
enum TextButtons {
	TEXTBUTTON_CONFIRM = 0,
	TEXTBUTTON_SCROLLUP,
	TEXTBUTTON_SCROLLDOWN,
	TEXTBUTTON_PREVCHOICE,
	TEXTBUTTON_NEXTCHOICE,
	TEXTBUTTON_SCROLLUP_ONELINE,
	TEXTBUTTON_SCROLLDOWN_ONELINE,
	TEXTBUTTON_GOTO_TOP,
	TEXTBUTTON_GOTO_BOTTOM,
	TEXTBUTTON_SPEECH_DONE // "Virtual" button?
};

// Buttons for option menu (corresponding to button indices, not button retvals, which are
// different for some reason)
enum OptionMenuButtons {
	OPTIONBUTTON_TEXT,
	OPTIONBUTTON_SAVE,
	OPTIONBUTTON_LOAD,
	OPTIONBUTTON_ENABLEMUSIC,
	OPTIONBUTTON_DISABLEMUSIC,
	OPTIONBUTTON_ENABLESFX,
	OPTIONBUTTON_DISABLESFX,
	OPTIONBUTTON_QUIT
};

enum TrekEventType {
	TREKEVENT_TICK = 0, // DOS clock changes
	TREKEVENT_LBUTTONDOWN = 1,
	TREKEVENT_MOUSEMOVE = 2,
	TREKEVENT_LBUTTONUP = 3,
	TREKEVENT_RBUTTONDOWN = 4,
	TREKEVENT_RBUTTONUP = 5,
	TREKEVENT_KEYDOWN = 6
};

struct TrekEvent {
	TrekEventType type;
	Common::KeyState kbd;
	Common::Point mouse;
	uint32 tick;
};


struct StarTrekGameDescription;
class Graphics;
class IWFile;
class Sound;

class StarTrekEngine : public ::Engine {
protected:
	// startrek.cpp
public:
	StarTrekEngine(OSystem *syst, const StarTrekGameDescription *gamedesc);
	virtual ~StarTrekEngine();

	friend class Console;

	Common::Error run();
	Common::Error runGameMode(int mode, bool resume);

	// Transporter room
	void runTransportSequence(const Common::String &name);

	// Bridge
	void initBridge(bool b) {}; // TODO
	void cleanupBridge() {}; // TODO

	// Running the game
	void playSoundEffectIndex(int index);
	void playMidiMusicTracks(int startTrack, int loopTrack);
	void playSpeech(const Common::String &filename);
	void stopPlayingSpeech();

	Common::MemoryReadStreamEndian *loadFile(Common::String filename, int fileIndex = 0);
	/**
	 * TODO: Figure out what the extra parameters are, and if they're important.
	 */
	Common::MemoryReadStreamEndian *loadFileWithParams(Common::String filename, bool unk1, bool unk2, bool unk3);

	void playMovie(Common::String filename);
	void playMovieMac(Common::String filename);

	uint16 getRandomWord();
	/**
	 * ".txt" files are just lists of strings. This traverses the file to get a particular
	 * string index.
	 */
	Common::String getLoadedText(int textIndex);


	// math.cpp
	/**
	 * Unit of the angle is "quadrants" (90 degrees = 1.0)
	 */
	Fixed14 sin(Angle angle);
	Fixed14 cos(Angle angle);
	Angle atan2(int32 deltaX, int32 deltaZ);

	// awaymission.cpp
	void initAwayMission();
	void runAwayMission();
	void cleanupAwayMission();
	void loadRoom(const Common::String &missionName, int roomIndex);
	void initAwayCrewPositions(int warpEntryIndex);
	void handleAwayMissionEvents();
	void awayMissionLeftClick();
	/**
	 * Called on right-click (select action), or when certain actions are otherwise
	 * selected (checks whether to show inventory icon, etc)
	 */
	void awayMissionSelectAction(bool openActionMenu);
	void awayMissionUseObject(int16 clickedObject);
	void awayMissionGetLookOrTalk(int16 clickedObject);
	void unloadRoom();
	/**
	 * Similar to loadActorAnim, but scale is determined by the y-position in the room. The
	 * further up (away) the object is, the smaller it is.
	 */
	int loadActorAnimWithRoomScaling(int actorIndex, const Common::String &animName, int16 x, int16 y);
	Fixed8 getActorScaleAtPosition(int16 y);
	void addAction(const Action &action);
	void addAction(byte type, byte b1, byte b2, byte b3);
	bool checkItemInteractionExists(int action, int activeItem, int passiveItem, int16 arg6);
	void handleAwayMissionAction();

	/**
	 * Returns true if the given position is contained in a polygon.
	 *
	 * The data passed contains the following words in this order:
	 *   * Index of polygon (unused here)
	 *   * Number of vertices in polygon
	 *   * For each vertex: x and y coordinates.
	 */
	bool isPointInPolygon(int16 *data, int16 x, int16 y);
	void checkTouchedLoadingZone(int16 x, int16 y);
	/**
	 * Updates any nonzero away mission timers, and invokes ACTION_TIMER_EXPIRED when any one
	 * reaches 0.
	 */
	void updateAwayMissionTimers();
	/**
	 * Returns true if the given position in the room is solid (not walkable).
	 * Reads from a ".map" file which has a bit for each position in the room, which is true
	 * when that position is solid.
	 */
	bool isPositionSolid(int16 x, int16 y);
	void loadRoomIndex(int roomIndex, int spawnIndex);

	Room *getRoom();

	// intro.cpp
private:
	void playIntro();
	/**
	 * Initializes an object to spawn at one position and move toward another position.
	 * @param ticks The number of ticks it should take for the object to reach the destination
	 */
	void initIntroR3ObjectToMove(R3 *r3, int16 srcAngle, int16 srcDepth, int16 destAngle, int16 destDepth, int16 ticks);
	void loadSubtitleSprite(int index, Sprite *sprite);

	// space.cpp (pseudo-3d)
	void initStarfieldPosition();
	void initStarfield(int16 x, int16 y, int16 width, int16 height, int16 arg8);
	void addR3(R3 *r3);
	void delR3(R3 *r3);
	void clearStarfieldPixels();
	void drawStarfield();
	void updateStarfieldAndShips(bool arg0);
	R3 *sub_19f24(R3 *r3);
	void drawR3Shape(R3 *r3);
	bool sub_1c022(R3 *r3);

	Point3 constructPoint3ForStarfield(int16 x, int16 y, int16 z);
	Point3 matrixMult(const Matrix &weight, const Point3 &point);
	Point3 matrixMult(const Point3 &point, const Matrix &weight);
	int32 scaleSpacePosition(int32 x, int32 z);

	/**
	 * Creates an identity matrix
	 */
	Matrix initMatrix();
	Matrix initSpeedMatrixForXZMovement(Angle angle, const Matrix &matrix);


	// actors.cpp (handles actors and animations)
public:
	void initActors();
	/**
	 * Set an actor's animation, position, and scale.
	 */
	int loadActorAnim(int actorIndex, const Common::String &animName, int16 x, int16 y, Fixed8 scale);
	void loadBanFile(const Common::String &name);
	/**
	 * Tries to make an actor walk to a position.
	 * Returns true if successful in initiating the walk.
	 */
	bool actorWalkToPosition(int actorIndex, const Common::String &animFile, int16 srcX, int16 srcY, int16 destX, int16 destY);
	void updateActorAnimations();

	/**
	 * ".BAN" files relate to drawing background animations, ie. flashing computer lights.
	 * "renderBanBelowSprites()" does the work of drawing it, while
	 * "renderBanAboveSprites()" redraws sprites above them if necessary.
	 */
	void renderBanBelowSprites();
	void renderBan(byte *pixelDest, FileStream file);
	void renderBanAboveSprites();
	void removeActorFromScreen(int actorIndex);
	void actorFunc1();
	void drawActorToScreen(Actor *actor, const Common::String &animName, int16 x, int16 y, Fixed8 scale, bool addSprite);
	void releaseAnim(Actor *actor);
	void initStandAnim(int actorIndex);
	void updateActorPositionWhileWalking(Actor *actor, int16 x, int16 y);
	/**
	 * Chooses a value for the actor's speed and direction, based on a source position and
	 * a destination position it's walking to.
	 */
	void chooseActorDirectionForWalking(Actor *actor, int16 srcX, int16 srcY, int16 destX, int16 destY);
	/**
	 * Returns true if an actor can walk directly from a source position to a destination
	 * position without running into unwalkable terrain.
	 */
	bool directPathExists(int16 srcX, int16 srcY, int16 destX, int16 destY);

	int findObjectAt(int x, int y);
	int findObjectAt(Common::Point p) {
		return findObjectAt(p.x, p.y);
	}
	/**
	 * Loads a bitmap for the animation frame with the given scale.
	 */
	SharedPtr<Bitmap> loadAnimationFrame(const Common::String &filename, Fixed8 scale);

	/**
	 * Called when the "get" action is first selected. Returns a selected object.
	 * This behaves like other menus in that it holds game execution, but no actual menu pops
	 * up; it just waits for the player to select something on the screen.
	 */
	int selectObjectForUseAction();
	Common::String getCrewmanAnimFilename(int actorIndex, const Common::String &basename);
	/**
	 * Checks whether to change the mouse bitmap to have the red outline.
	 */
	void updateMouseBitmap();
	/**
	 * Checks whether to walk a crewman to a hotspot (the last one obtained from
	 * a "findObjectAt" call).
	 */
	bool walkActiveObjectToHotspot();
	/**
	 * Return true if an object is unselectable with the given action?
	 */
	bool isObjectUnusable(int objectIndex, int action);
	/**
	 * When a crewman is collapsed, they get once a timer reaches 0.
	 */
	void updateCrewmanGetupTimers();
	void showInventoryIcons(bool showItem);
	void hideInventoryIcons();
	int showInventoryMenu(int x, int y, bool restoreMouse);
	void initStarfieldSprite(Sprite *sprite, SharedPtr<Bitmap> bitmap, const Common::Rect &rect);
	SharedPtr<Bitmap> scaleBitmap(SharedPtr<Bitmap> bitmap, Fixed8 scale);
	/**
	 * This takes a row of an unscaled bitmap, and copies it to a row of a scaled bitmap.
	 * This was heavily optimized in the original game (manually constructed an unrolled
	 * loop).
	 */
	void scaleBitmapRow(byte *src, byte *dest, uint16 origWidth, uint16 scaledWidth);

	// events.cpp
public:
	/**
	 * Checks for all events, and updates Star Trek's event queue if queueEvents is set.
	 * This does not account for "tick" events (getNextEvent/popNextEvent handle that).
	 */
	void pollEvents(bool queueEvents = true);
	void waitForNextTick(bool queueEvents = true);
	void initializeEventsAndMouse();
	/**
	 * Returns false if there is no event waiting. If "poll" is true, this always returns
	 * something (waits until an event occurs if necessary).
	 */
	bool getNextEvent(TrekEvent *e, bool poll = true);
	void removeNextEvent();
	bool popNextEvent(TrekEvent *e, bool poll = true);
	void addEventToQueue(const TrekEvent &e);
	void clearEventBuffer();
	void updateEvents();
	void updateTimerEvent();
	void updateMouseEvents();
	void updateKeyboardEvents();
	void updateClockTicks();
	bool checkKeyPressed();

	Common::EventManager *getEventMan() {
		return _eventMan;
	}

private:
	Common::List<TrekEvent> _eventQueue;
	bool _mouseMoveEventInQueue;
	bool _tickEventInQueue;
	uint32 _frameStartMillis;


	// textbox.cpp
public:
	/**
	 * Gets one line of text (does not include words that won't fit).
	 * Returns position of text to continue from, or nullptr if done.
	 */
	const char *getNextTextLine(const char *text, char *line, int lineWidth);
	/**
	 * Draw a line of text to a standard bitmap (NOT a "TextBitmap", whose pixel array is
	 * an array of characters, but an actual standard bitmap).
	 */
	void drawTextLineToBitmap(const char *text, int textLen, int x, int y, SharedPtr<Bitmap> bitmap);

	String centerTextboxHeader(String headerText);
	void getTextboxHeader(String *headerTextOutput, String speakerText, int choiceIndex);
	/**
	 * Text getter for showText which reads from an rdf file.
	 * Not really used, since it would require hardcoding text locations in RDF files.
	 * "readTextFromArrayWithChoices" replaces this.
	 */
	String readTextFromRdf(int choiceIndex, uintptr data, String *headerTextOutput);
	String readTextFromBuffer(int choiceIndex, uintptr data, String *headerTextOutput);

	/**
	 * Shows text with the given header and main text.
	 */
	void showTextbox(String headerText, const String &mainText, int xoffset, int yoffset, byte textColor, int maxTextLines); // TODO: better name. (return type?)

	String skipTextAudioPrompt(const String &str);
	/**
	 * Plays an audio prompt, if it exists, and returns the string starting at the end of the
	 * prompt.
	 */
	String playTextAudio(const String &str);

	/**
	 * @param rclickCancelsChoice   If true, right-clicks return "-1" as choice instead of
	 *                              whatever was selected.
	 */
	int showText(TextGetterFunc textGetter, uintptr var, int xoffset, int yoffset, int textColor, bool loopChoices, int maxTextLines, bool rclickCancelsChoice);

	/**
	 * Returns the number of lines this string will take up in a textbox.
	 */
	int getNumTextboxLines(const String &str);
	String putTextIntoLines(const String &text);

	/**
	 * Creates a blank textbox in a TextBitmap, and initializes a sprite to use it.
	 */
	SharedPtr<TextBitmap> initTextSprite(int *xoffsetPtr, int *yoffsetPtr, byte textColor, int numTextLines, bool withHeader, Sprite *sprite);
	/**
	 * Draws the "main" text (everything but the header at the top) to a TextBitmap.
	 */
	void drawMainText(SharedPtr<TextBitmap> bitmap, int numTextLines, int numTextboxLines, const String &text, bool withHeader);

	String readLineFormattedText(TextGetterFunc textGetter, uintptr var, int choiceIndex, SharedPtr<TextBitmap> textBitmap, int numTextboxLines, int *numLines);

	/**
	 * Text getter for showText which reads choices from an array of pointers.
	 * Last element in the array must be an empty string.
	 */
	String readTextFromArray(int choiceIndex, uintptr data, String *headerTextOutput);
	/**
	 * Similar to above, but shows the choice index when multiple choices are present.
	 * Effectively replaces the "readTextFromRdf" function.
	 */
	String readTextFromArrayWithChoices(int choiceIndex, uintptr data, String *headerTextOutput);

	Common::String showCodeInputBox();
	void redrawTextInput();
	void addCharToTextInputBuffer(char c);
	/**
	 * Shows a textbox that the player can type a string into.
	 */
	Common::String showTextInputBox(int16 arg0, int16 arg2, const Common::String &headerText);
	void initTextInputSprite(int16 arg0, int16 arg2, const Common::String &headerText);
	void cleanupTextInputSprite();

private:
	char _textInputBuffer[TEXT_INPUT_BUFFER_SIZE];
	int16 _textInputCursorPos;
	char _textInputCursorChar;
	SharedPtr<Bitmap> _textInputBitmapSkeleton;
	SharedPtr<Bitmap> _textInputBitmap;
	Sprite _textInputSprite;

	// menu.cpp
public:
	/**
	 * Returns the index of the button at the given position, or -1 if none.
	 */
	int getMenuButtonAt(Sprite *sprites, int numSprites, int x, int y);
	/**
	 * This chooses a sprite from the list to place the mouse cursor at. The sprite it chooses
	 * may be, for example, the top-leftmost one in the list. Exact behaviour is determined by
	 * the "mode" parameter.
	 *
	 * If "containMouseSprite" is a valid index, it's ensured that the mouse is contained
	 * within it. "mode" should be -1 in this case.
	 */
	void chooseMousePositionFromSprites(Sprite *sprites, int numSprites, int spriteIndex, int mode);
	/**
	 * Draws or removes the outline on menu buttons when the cursor hovers on them, or leaves
	 * them.
	 */
	void drawMenuButtonOutline(SharedPtr<Bitmap> bitmap, byte color);
	void showOptionsMenu(int x, int y);
	/**
	 * Show the "action selection" menu, ie. look, talk, etc.
	 */
	int showActionMenu();
	/**
	 * Loads a .MNU file, which is a list of buttons to display.
	 */
	void loadMenuButtons(String mnuFilename, int xpos, int ypos);
	/**
	 * Sets which buttons are visible based on the given bitmask.
	 */
	void setVisibleMenuButtons(uint32 bits);
	/**
	 * Disables the given bitmask of buttons.
	 */
	void disableMenuButtons(uint32 bits);
	void enableMenuButtons(uint32 bits);
	/**
	 * This returns either a special menu event (negative number) or the retval of the button
	 * clicked (usually an index, always positive).
	 */
	int handleMenuEvents(uint32 ticksUntilClickingEnabled, bool inTextbox);
	void unloadMenuButtons();

	/**
	 * Sets the mouse bitmap based on which action is selected.
	 */
	void chooseMouseBitmapForAction(int action, bool withRedOutline);
	void showQuitGamePrompt(int x, int y);
	void showGameOverMenu();
	/**
	 * This can be called from startup or from the options menu.
	 * On startup, this tries to load the setting without user input.
	 */
	void showTextConfigurationMenu(bool fromOptionMenu);

	int loadTextDisplayMode();
	void saveTextDisplayMode(int value);

	/**
	 * Show the republic map, only used in mission 7.
	 */
	void showRepublicMap(int16 arg0, int16 turbolift);
	/**
	 * Checks the mouse position to return and index for the area selected.
	 */
	int getRepublicMapAreaAtMouse();
	/**
	 * Same as above, but returns 6 or 7 as error conditions (can't reach due to radiation
	 * or wrong turbolift).
	 */
	int getRepublicMapAreaOrFailure(int16 turbolift);


private:
	int16 _textDisplayMode;
	uint32 _textboxVar2;
	uint16 _textboxVar6;
	bool _textboxHasMultipleChoices;
	Menu *_activeMenu;
	// Saved value of StarTrekEngine::_keyboardControlsMouse when menus are up
	bool _keyboardControlsMouseOutsideMenu;

	// saveload.cpp
public:
	bool showSaveMenu();
	bool showLoadMenu();

	bool saveGame(int slot, Common::String desc);
	bool loadGame(int slot);

	/**
	 * Call this after loading "saveOrLoadMetadata" to load all the data pertaining to game
	 * execution.
	 */
	bool saveOrLoadGameData(Common::SeekableReadStream *in, Common::WriteStream *out, SavegameMetadata *meta);

	Common::String getSavegameFilename(int slotId) const;

	// detection.cpp
public:
	const StarTrekGameDescription *_gameDescription;
	uint32 getFeatures() const;
	Common::Platform getPlatform() const;
	uint8 getGameType() const;
	Common::Language getLanguage() const;
	
	// _screenName = _missionName + _roomIndex
	Common::String getScreenName() const {
		return _missionName + (char)(_roomIndex + '0');
	}

	// Variables
public:
	int _gameMode;
	int _lastGameMode;
	bool _resetGameMode;

	// NOTE: this has a different meaning than the original game. When non-empty, a new
	// room load is triggered, as opposed to original behaviour where this was only read
	// when "loadRoom" was called.
	Common::String _missionToLoad;
	int _roomIndexToLoad;
	int _spawnIndexToLoad;

	Common::String _missionName;
	int _roomIndex;
	Common::MemoryReadStreamEndian *_mapFile;
	Fixed16 _playerActorScale;

	Common::String _txtFilename;
	Common::String _loadedText; // TODO: might be OK to delete this

	// Queue of "actions" (ie. next frame, clicked on object) for away mission or bridge
	Common::Queue<Action> _actionQueue;

	AwayMission _awayMission;
	bool _warpHotspotsActive;
	int16 _activeWarpHotspot;
	int16 _activeDoorWarpHotspot;
	int16 _lookActionBitmapIndex;

	Item _itemList[NUM_OBJECTS];

	Actor _actorList[NUM_ACTORS];
	Actor *const _kirkActor;
	Actor *const _spockActor;
	Actor *const _mccoyActor;
	Actor *const _redshirtActor;

	// ".BAN" files provide extra miscellaneous animations in the room, ie. flashing
	// pixels on computer consoles, or fireflies in front of the screen.
	FileStream _banFiles[MAX_BAN_FILES];
	uint16 _banFileOffsets[MAX_BAN_FILES];

	Sprite _inventoryIconSprite;
	Sprite _itemIconSprite;

	// Certain hotspots store a position value where objects must walk to before
	// interacting with them. After calling "findObjectAt", these values are updated.
	bool _objectHasWalkPosition;
	Common::Point _objectWalkPosition;

	// Actions to perform after a crewman finishes walking to a position.
	// Room-specific code can specify that a specific action of type
	// "ACTION_FINISHED_WALKING" occurs after moving a crewman somewhere.
	Action _actionOnWalkCompletion[MAX_BUFFERED_WALK_ACTIONS];
	bool _actionOnWalkCompletionInUse[MAX_BUFFERED_WALK_ACTIONS];

	// _clockTicks is based on DOS interrupt 1A, AH=0; read system clock counter.
	// Updates 18.206 times every second.
	uint32 _clockTicks;
	uint32 _frameIndex;
	uint32 _roomFrameCounter; // Resets to 0 on loading a room

	bool _musicEnabled;
	bool _sfxEnabled;
	uint16 _word_467a6;
	uint16 _musicWorking;
	bool _sfxWorking;
	bool _finishedPlayingSpeech;

	bool _mouseControllingShip;

	// TODO: make this work.
	// When false, the keyboard generally acts in a more specific way (ie. move mouse
	// between items in a menu).
	bool _keyboardControlsMouse;

	bool _inQuitGameMenu;
	bool _showSubtitles;

	byte _byte_45b3c;

	// Pseudo-3D / starfield stuff
	Sprite _starfieldSprite;
	Star _starList[NUM_STARS];
	Point3 _starfieldPosition;
	int32 _starfieldPointDivisor;
	int16 _starfieldXVar1, _starfieldYVar1;
	int16 _starfieldXVar2, _starfieldYVar2;
	Common::Rect _starfieldRect;
	R3 _enterpriseR3;
	R3 *_r3List[NUM_SPACE_OBJECTS];
	R3 *_orderedR3List[NUM_SPACE_OBJECTS];
	Matrix _starPositionMatrix;
	Matrix _someMatrix;
	float _flt_50898;

	Graphics *_gfx;
	Sound *_sound;
	Console *_console;
	SharedPtr<IWFile> _iwFile;

private:
	Common::RandomSource _randomSource;
	Common::SineTable _sineTable;
	Room *_room;
	Common::MacResManager *_macResFork;
};

// Static function
bool saveOrLoadMetadata(Common::SeekableReadStream *in, Common::WriteStream *out, SavegameMetadata *meta);

} // End of namespace StarTrek

#endif