/* 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 QUEEN_STRUCTS_H
#define QUEEN_STRUCTS_H

#include "queen/defs.h"
#include "common/endian.h"

namespace Queen {

struct Box {
	int16 x1, y1, x2, y2;

	Box()
		: x1(0), y1(0), x2(0), y2(0) {
	}

	Box(int16 xx1, int16 yy1, int16 xx2, int16 yy2)
		: x1(xx1), y1(yy1), x2(xx2), y2(yy2) {
	}

	void readFromBE(byte *&ptr) {
		x1 = (int16)READ_BE_UINT16(ptr); ptr += 2;
		y1 = (int16)READ_BE_UINT16(ptr); ptr += 2;
		x2 = (int16)READ_BE_UINT16(ptr); ptr += 2;
		y2 = (int16)READ_BE_UINT16(ptr); ptr += 2;
	}

	void writeToBE(byte *&ptr) {
		WRITE_BE_UINT16(ptr, x1); ptr += 2;
		WRITE_BE_UINT16(ptr, y1); ptr += 2;
		WRITE_BE_UINT16(ptr, x2); ptr += 2;
		WRITE_BE_UINT16(ptr, y2); ptr += 2;
	}

	int16 xDiff() const {
		return x2 - x1;
	}

	int16 yDiff() const {
		return y2 - y1;
	}

	bool intersects(int16 x, int16 y, uint16 w, uint16 h) const {
		return (x + w > x1) && (y + h > y1) && (x <= x2) && (y <= y2);
	}

	bool contains(int16 x, int16 y) const {
		return (x >= x1) && (x <= x2) && (y >= y1) && (y <= y2);
	}

	bool operator==(const Box &b) const {
		return (x1 == b.x1) && (x2 == b.x2) && (y1 == b.y1) && (y2 == b.y2);
	}
};


struct Area {
	//! bitmask of connected areas
	int16 mapNeighbors;
	//! coordinates defining area limits
	Box box;
	//! scaling factors for bobs actors
	uint16 bottomScaleFactor, topScaleFactor;
	//! entry in ObjectData, object lying in this area
	uint16 object;

	void readFromBE(byte *&ptr) {
		mapNeighbors = (int16)READ_BE_UINT16(ptr); ptr += 2;
		box.readFromBE(ptr);
		bottomScaleFactor = READ_BE_UINT16(ptr); ptr += 2;
		topScaleFactor = READ_BE_UINT16(ptr); ptr += 2;
		object = READ_BE_UINT16(ptr); ptr += 2;
	}

	void writeToBE(byte *&ptr) {
		WRITE_BE_UINT16(ptr, mapNeighbors); ptr += 2;
		box.writeToBE(ptr);
		WRITE_BE_UINT16(ptr, bottomScaleFactor); ptr += 2;
		WRITE_BE_UINT16(ptr, topScaleFactor); ptr += 2;
		WRITE_BE_UINT16(ptr, object); ptr += 2;
	}

	uint16 calcScale(int16 y) const {
		uint16 dy = box.yDiff();
		int16 ds = scaleDiff();
		uint16 scale = 0;

		if (dy)	// Prevent division-by-zero
			scale = ((((y - box.y1) * 100) / dy) * ds) / 100 + bottomScaleFactor;

		if (scale == 0)
			scale = 100;

		return scale;
	}

	int16 scaleDiff() const {
		return (int16)(topScaleFactor - bottomScaleFactor);
	}
};


struct WalkOffData {
	//! entry in ObjectData
	int16 entryObj;
	//! coordinates to reach
	uint16 x, y;

	void readFromBE(byte *&ptr) {
		entryObj = (int16)READ_BE_UINT16(ptr); ptr += 2;
		x = READ_BE_UINT16(ptr); ptr += 2;
		y = READ_BE_UINT16(ptr); ptr += 2;
	}

	void writeToBE(byte *&ptr) {
		WRITE_BE_UINT16(ptr, entryObj); ptr += 2;
		WRITE_BE_UINT16(ptr, x); ptr += 2;
		WRITE_BE_UINT16(ptr, y); ptr += 2;
	}
};


struct GraphicData {
	//! coordinates of object
	uint16 x, y;
	//! bank bobframes
	/*!
		<table>
			<tr>
				<td>lastFrame == 0</td>
				<td>non-animated bob (one frame)</td>
			</tr>
			<tr>
				<td>lastFrame < 0</td>
				<td>rebound animation</td>
			</tr>
			<tr>
				<td>firstFrame < 0</td>
				<td>BobSlot::animString (animation is described by a string)</td>
			</tr>
			<tr>
				<td>firstFrame > 0</td>
				<td>BobSlot::animNormal (animation is a sequence of frames)</td>
			</tr>
		</table>
	*/
	int16 firstFrame, lastFrame;
	//! moving speed of object
	uint16 speed;

	void readFromBE(byte *&ptr) {
		x = READ_BE_UINT16(ptr); ptr += 2;
		y = READ_BE_UINT16(ptr); ptr += 2;
		firstFrame = (int16)READ_BE_UINT16(ptr); ptr += 2;
		lastFrame = (int16)READ_BE_UINT16(ptr); ptr += 2;
		speed = READ_BE_UINT16(ptr); ptr += 2;
	}
};


struct ObjectData {
	//! entry in OBJECT_NAME (<0: object is hidden, 0: object has been deleted)
	int16 name;
	//! coordinates of object
	uint16 x, y;
	//! entry in OBJECT_DESCR
	uint16 description;
	//! associated object
	int16 entryObj;
	//! room in which this object is available
	uint16 room;
	//! state of the object (grab direction, on/off, default command...)
	uint16 state;
	//! entry in GraphicData
	/*!
		<table>
			<tr>
				<td>value</td>
				<td>description</td>
			</tr>
			<tr>
				<td>]-4000..-10]</td>
				<td>graphic image turned off</td>
			</tr>
			<tr>
				<td>-4</td>
				<td>person object (right facing)</td>
			</tr>
			<tr>
				<td>-3</td>
				<td>person object (left facing)</td>
			</tr>
			<tr>
				<td>-2</td>
				<td>animated bob (off)</td>
			</tr>
			<tr>
				<td>-1</td>
				<td>static bob (off)</td>
			</tr>
			<tr>
				<td>0</td>
				<td>object deleted</td>
			</tr>
			<tr>
				<td>]0..5000]</td>
				<td>static or animated bob (on)</td>
			</tr>
			<tr>
				<td>]5000.. [</td>
				<td>'paste down' bob</td>
			</tr>
		</table>
	*/
	int16 image;

	void readFromBE(byte *&ptr) {
		name = (int16)READ_BE_UINT16(ptr); ptr += 2;
		x = READ_BE_UINT16(ptr); ptr += 2;
		y = READ_BE_UINT16(ptr); ptr += 2;
		description = READ_BE_UINT16(ptr); ptr += 2;
		entryObj = (int16)READ_BE_UINT16(ptr); ptr += 2;
		room = READ_BE_UINT16(ptr); ptr += 2;
		state = READ_BE_UINT16(ptr); ptr += 2;
		image = (int16)READ_BE_UINT16(ptr); ptr += 2;
	}

	void writeToBE(byte *&ptr) {
		WRITE_BE_UINT16(ptr, name); ptr += 2;
		WRITE_BE_UINT16(ptr, x); ptr += 2;
		WRITE_BE_UINT16(ptr, y); ptr += 2;
		WRITE_BE_UINT16(ptr, description); ptr += 2;
		WRITE_BE_UINT16(ptr, entryObj); ptr += 2;
		WRITE_BE_UINT16(ptr, room); ptr += 2;
		WRITE_BE_UINT16(ptr, state); ptr += 2;
		WRITE_BE_UINT16(ptr, image); ptr += 2;
	}
};


struct ObjectDescription {
	//! entry in ObjectData or ItemData
	uint16 object;
	//! type of the description
	/*!
		refer to select.c l.75-101
		<table>
			<tr>
				<td>value</td>
				<td>description</td>
			</tr>
			<tr>
				<td>0</td>
				<td>random but starts at first description</td>
			<tr>
				<td>1</td>
				<td>random</td>
			</tr>
			<tr>
				<td>2</td>
				<td>sequential with loop</td>
			</tr>
			<tr>
				<td>3</td>
				<td>sequential and set description to last</td>
			</tr>
		</table>
	*/
	uint16 type;
	//! last entry possible in OBJECT_DESCR for this object
	uint16 lastDescription;
	//! last description number used (in order to avoid re-using it)
	uint16 lastSeenNumber;

	void readFromBE(byte *&ptr) {
		object = READ_BE_UINT16(ptr); ptr += 2;
		type = READ_BE_UINT16(ptr); ptr += 2;
		lastDescription = READ_BE_UINT16(ptr); ptr += 2;
		lastSeenNumber = READ_BE_UINT16(ptr); ptr += 2;
	}

	void writeToBE(byte *&ptr) {
		WRITE_BE_UINT16(ptr, object); ptr += 2;
		WRITE_BE_UINT16(ptr, type); ptr += 2;
		WRITE_BE_UINT16(ptr, lastDescription); ptr += 2;
		WRITE_BE_UINT16(ptr, lastSeenNumber); ptr += 2;
	}
};


struct ItemData {
	//! entry in OBJECT_NAME
	int16 name;
	//! entry in OBJECT_DESCR
	uint16 description;
	//! state of the object
	uint16 state;
	//! bank bobframe
	uint16 frame;
	//! entry in OBJECT_DESCR (>0 if available)
	int16 sfxDescription;

	void readFromBE(byte *&ptr) {
		name = (int16)READ_BE_UINT16(ptr); ptr += 2;
		description = READ_BE_UINT16(ptr); ptr += 2;
		state = READ_BE_UINT16(ptr); ptr += 2;
		frame = READ_BE_UINT16(ptr); ptr += 2;
		sfxDescription = (int16)READ_BE_UINT16(ptr); ptr += 2;
	}

	void writeToBE(byte *&ptr) {
		WRITE_BE_UINT16(ptr, name); ptr += 2;
		WRITE_BE_UINT16(ptr, description); ptr += 2;
		WRITE_BE_UINT16(ptr, state); ptr += 2;
		WRITE_BE_UINT16(ptr, frame); ptr += 2;
		WRITE_BE_UINT16(ptr, sfxDescription); ptr += 2;
	}
};


struct ActorData {
	//! room in which the actor is
	int16 room;
	//! bob number associated to this actor
	int16 bobNum;
	//! entry in ACTOR_NAME
	uint16 name;
	//! gamestate entry/value, actor is valid if GAMESTATE[slot] == value
	int16 gsSlot, gsValue;
	//! spoken text color
	uint16 color;
	//! bank bobframe for standing position of the actor
	uint16 bobFrameStanding;
	//! initial coordinates in the room
	uint16 x, y;
	//! entry in ACTOR_ANIM
	uint16 anim;
	//! bank to use to load the actor file
	uint16 bankNum;
	//! entry in ACTOR_FILE
	uint16 file;

	void readFromBE(byte *&ptr) {
		room = (int16)READ_BE_UINT16(ptr); ptr += 2;
		bobNum = (int16)READ_BE_UINT16(ptr); ptr += 2;
		name = READ_BE_UINT16(ptr); ptr += 2;
		gsSlot = (int16)READ_BE_UINT16(ptr); ptr += 2;
		gsValue = (int16)READ_BE_UINT16(ptr); ptr += 2;
		color = READ_BE_UINT16(ptr); ptr += 2;
		bobFrameStanding = READ_BE_UINT16(ptr); ptr += 2;
		x = READ_BE_UINT16(ptr); ptr += 2;
		y = READ_BE_UINT16(ptr); ptr += 2;
		anim = READ_BE_UINT16(ptr); ptr += 2;
		bankNum = READ_BE_UINT16(ptr); ptr += 2;
		file = READ_BE_UINT16(ptr); ptr += 2;
		// Fix the actor data (see queen.c - l.1518-1519). When there is no
		// valid actor file, we must load the data from the objects room bank.
		// This bank has number 15 (not 10 as in the data files).
		if (file == 0) {
			bankNum = 15;
		}
	}
};


struct CmdListData {
	//! action to perform
	Verb verb;
	//! first object used in the action
	int16 nounObj1;
	//! second object used in the action
	int16 nounObj2;
	//! song to play (>0: playbefore, <0: playafter)
	int16 song;
	//! if set, P2_SET_AREAS must be called (using CmdArea)
	bool setAreas;
	//! if set, P3_SET_OBJECTS must be called (using CmdObject)
	bool setObjects;
	//! if set, P4_SET_ITEMS must be called (using CmdInventory)
	bool setItems;
	//! if set, P1_SET_CONDITIONS must be called (using CmdGameState)
	bool setConditions;
	//! graphic image order
	int16 imageOrder;
	//! special section to execute (refer to execute.c l.423-451)
	int16 specialSection;

	void readFromBE(byte *&ptr) {
		verb = (Verb)READ_BE_UINT16(ptr); ptr += 2;
		nounObj1 = (int16)READ_BE_UINT16(ptr); ptr += 2;
		nounObj2 = (int16)READ_BE_UINT16(ptr); ptr += 2;
		song = (int16)READ_BE_UINT16(ptr); ptr += 2;
		setAreas = READ_BE_UINT16(ptr) != 0; ptr += 2;
		setObjects = READ_BE_UINT16(ptr) != 0; ptr += 2;
		setItems = READ_BE_UINT16(ptr) != 0; ptr += 2;
		setConditions = READ_BE_UINT16(ptr) != 0; ptr += 2;
		imageOrder = (int16)READ_BE_UINT16(ptr); ptr += 2;
		specialSection = (int16)READ_BE_UINT16(ptr); ptr += 2;
	}

	bool match(const Verb& v, int16 obj1, int16 obj2) const {
		return verb == v && nounObj1 == obj1 && nounObj2 == obj2;
	}
};


struct CmdArea {
	//! CmdListData number
	int16 id;
	//! area to turn off/on (<0: off, >0: on)
	int16 area;
	//! room in which the area must be changed
	uint16 room;

	void readFromBE(byte *&ptr) {
		id = (int16)READ_BE_UINT16(ptr); ptr += 2;
		area = (int16)READ_BE_UINT16(ptr); ptr += 2;
		room = READ_BE_UINT16(ptr); ptr += 2;
	}
};


struct CmdObject {
	//! CmdListData number
	int16 id;
	//! >0: show, <0: hide
	int16 dstObj;
	//! >0: copy from srcObj, 0: nothing, -1: delete dstObj
	int16 srcObj;

	void readFromBE(byte *&ptr) {
		id = (int16)READ_BE_UINT16(ptr); ptr += 2;
		dstObj = (int16)READ_BE_UINT16(ptr); ptr += 2;
		srcObj = (int16)READ_BE_UINT16(ptr); ptr += 2;
	}
};


struct CmdInventory {
	//! CmdListData number
	int16 id;
	//! <0: delete, >0: add
	int16 dstItem;
	//! >0: valid
	int16 srcItem;

	void readFromBE(byte *&ptr) {
		id = (int16)READ_BE_UINT16(ptr); ptr += 2;
		dstItem = (int16)READ_BE_UINT16(ptr); ptr += 2;
		srcItem = (int16)READ_BE_UINT16(ptr); ptr += 2;
	}
};


struct CmdGameState {
	//! CmdListData number
	int16 id;
	int16 gameStateSlot;
	int16 gameStateValue;
	uint16 speakValue;

	void readFromBE(byte *&ptr) {
		id = (int16)READ_BE_UINT16(ptr); ptr += 2;
		gameStateSlot = (int16)READ_BE_UINT16(ptr); ptr += 2;
		gameStateValue = (int16)READ_BE_UINT16(ptr); ptr += 2;
		speakValue = READ_BE_UINT16(ptr); ptr += 2;
	}
};


struct FurnitureData {
	//! room in which the furniture are
	int16 room;
	//! furniture object number
	/*!
		<table>
			<tr>
				<td>range</td>
				<td>type</td>
			</tr>
			<tr>
				<td>]0..5000]</td>
				<td>static or animated</td>
			</tr>
			<tr>
				<td>]5000..[</td>
				<td>paste down</td>
			</tr>
		</table>
	*/
	int16 objNum;

	void readFromBE(byte *&ptr) {
		room = (int16)READ_BE_UINT16(ptr); ptr += 2;
		objNum = (int16)READ_BE_UINT16(ptr); ptr += 2;
	}
};


struct GraphicAnim {
	int16 keyFrame;
	int16 frame;
	uint16 speed;

	void readFromBE(byte *&ptr) {
		keyFrame = (int16)READ_BE_UINT16(ptr); ptr += 2;
		frame = (int16)READ_BE_UINT16(ptr); ptr += 2;
		speed = READ_BE_UINT16(ptr); ptr += 2;
	}
};


struct AnimFrame {
	uint16 frame;
	uint16 speed;
};


struct Person {
	//! actor settings to use
	const ActorData *actor;
	//! actor name
	const char *name;
	//! animation string
	const char *anim;
	//! current frame
	uint16 bobFrame;
};


struct TalkSelected {
	bool hasTalkedTo;
	int16 values[4];

	void readFromBE(byte *&ptr) {
		hasTalkedTo = READ_BE_UINT16(ptr) != 0; ptr += 2;
		for (int i = 0; i < 4; i++) {
			values[i] = (int16)READ_BE_UINT16(ptr); ptr += 2;
		}
	}

	void writeToBE(byte *&ptr) {
		WRITE_BE_UINT16(ptr, (uint16)hasTalkedTo); ptr += 2;
		for (int i = 0; i < 4; i++) {
			WRITE_BE_UINT16(ptr, values[i]); ptr += 2;
		}
	}
};


struct BobFrame {
	uint16 width, height;
	uint16 xhotspot, yhotspot;
	uint8 *data;
};


} // End of namespace Queen

#endif
