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

#include "common/stack.h"

#include "gob/util.h"

namespace Gob {

class Font;
class Script;

class Hotspots {
public:
	static const int kHotspotCount = 250;

	enum Type {
		kTypeNone              =  0,
		kTypeMove              =  1,
		kTypeClick             =  2,
		kTypeInput1NoLeave     =  3,
		kTypeInput1Leave       =  4,
		kTypeInput2NoLeave     =  5,
		kTypeInput2Leave       =  6,
		kTypeInput3NoLeave     =  7,
		kTypeInput3Leave       =  8,
		kTypeInputFloatNoLeave =  9,
		kTypeInputFloatLeave   = 10,
		kTypeEnable2           = 11,
		kTypeEnable1           = 12,
		kTypeClickEnter        = 21
	};

	enum State {
		kStateFilledDisabled = 0xC,
		kStateFilled         = 0x8,
		kStateDisabled       = 0x4,
		kStateType2          = 0x2,
		kStateType1          = 0x1
	};

	Hotspots(GobEngine *vm);
	~Hotspots();

	/** Remove all hotspots. */
	void clear();

	/** Add a hotspot, returning the new index. */
	uint16 add(uint16 id,
			uint16 left,  uint16 top, uint16 right, uint16 bottom,
			uint16 flags, uint16 key,
			uint16 funcEnter, uint16 funcLeave, uint16 funcPos);

	/** Remove a specific hotspot. */
	void remove(uint16 id);
	/** Remove all hotspots in this state. */
	void removeState(uint8 state);

	/** Push the current hotspots onto the stack.
	 *
	 *  @param all   0: Don't push global ones; 1: Push all; 2: Push only the disabled ones
	 *  @param force Force a push although _shouldPush is false
	 */
	void push(uint8 all, bool force = false);
	/** Pop hotspots from the stack. */
	void pop();

	/** Check the current hotspot. */
	uint16 check(uint8 handleMouse, int16 delay, uint16 &id, uint16 &index);
	/** Check the current hotspot. */
	uint16 check(uint8 handleMouse, int16 delay);

	/** Evaluate hotspot changes. */
	void evaluate();

	/** Return the cursor found in the hotspot to the coordinates. */
	int16 findCursor(uint16 x, uint16 y) const;

	/** implementation of oPlaytoons_F_1B code*/
	void oPlaytoons_F_1B();

private:
	struct Hotspot {
		uint16  id;
		uint16  left;
		uint16  top;
		uint16  right;
		uint16  bottom;
		uint16  flags;
		uint16  key;
		uint16  funcEnter;
		uint16  funcLeave;
		uint16  funcPos;
		Script *script;

		Hotspot();
		Hotspot(uint16 i,
				uint16 l, uint16 t, uint16 r, uint16 b, uint16 f, uint16 k,
				uint16 enter, uint16 leave, uint16 pos);

		void clear();

		Type         getType  () const;
		MouseButtons getButton() const;
		uint16       getWindow() const;
		uint8        getCursor() const;
		uint8        getState () const;

		/** Is this hotspot the block end marker? */
		bool isEnd() const;

		bool isInput      () const;
		bool isActiveInput() const;
		bool isInputLeave () const;

		bool isFilled       () const;
		bool isFilledEnabled() const;
		bool isFilledNew    () const;
		bool isDisabled     () const;

		/** Are the specified coordinates in the hotspot? */
		bool isIn(uint16 x, uint16 y) const;
		/** Does the specified button trigger the hotspot? */
		bool buttonMatch(MouseButtons button) const;

		static uint8 getState(uint16 id);

		void disable();
		void enable ();
	};

	struct StackEntry {
		bool     shouldPush;
		Hotspot *hotspots;
		uint32   size;
		uint32   key;
		uint32   id;
		uint32   index;
		uint16   x;
		uint16   y;
	};

	struct InputDesc {
		uint16 fontIndex;
		uint16 backColor;
		uint16 frontColor;
		uint16 length;
		const char *str;
	};

	GobEngine *_vm;

	Hotspot *_hotspots;
	Common::Stack<StackEntry> _stack;

	bool _shouldPush;

	uint16 _currentKey;
	uint16 _currentIndex;
	uint16 _currentId;
	uint16 _currentX;
	uint16 _currentY;

	/** Add a hotspot, returning the new index. */
	uint16 add(const Hotspot &hotspot);

	/** Recalculate all hotspot parameters
	 *
	 *  @param force Force recalculation of all hotspots, including global ones.
	 */
	void recalculate(bool force);

	/** Is this a valid hotspot? */
	bool isValid(uint16 key, uint16 id, uint16 index) const;

	/** Call a hotspot subroutine. */
	void call(uint16 offset);
	/** Handling hotspot enter events. */
	void enter(uint16 index);
	/** Handling hotspot leave events. */
	void leave(uint16 index);

	/** Check whether a specific part of the window forces a certain cursor. */
	int16 windowCursor(int16 &dx, int16 &dy) const;

	/** Which hotspot is the mouse cursor currently at? */
	uint16 checkMouse(Type type, uint16 &id, uint16 &index) const;

	/** Did the current hotspot change in the meantime? */
	bool checkHotspotChanged();

	/** Update events from a specific input. */
	uint16 updateInput(uint16 xPos, uint16 yPos, uint16 width, uint16 height,
			uint16 backColor, uint16 frontColor, char *str, uint16 fontIndex,
			Type type, int16 &duration, uint16 &id, uint16 &index);

	/** Handle all inputs we currently manage. */
	uint16 handleInputs(int16 time, uint16 inputCount, uint16 &curInput,
			InputDesc *inputs, uint16 &id, uint16 &index);

	/** Evaluate adding new hotspots script commands. */
	void evaluateNew(uint16 i, uint16 *ids, InputDesc *inputs,
			uint16 &inputId, bool &hasInput, uint16 &inputCount);
	/** Find the hotspot requested by script commands. */
	bool evaluateFind(uint16 key, int16 timeVal, const uint16 *ids,
			uint16 leaveWindowIndex, uint16 hotspotIndex1, uint16 hotspotIndex2,
			uint16 endIndex, int16 &duration, uint16 &id, uint16 &index, bool &finished);

	// Finding specific hotspots
	/** Find the hotspot index that corresponds to the input index. */
	uint16 inputToHotspot(uint16 input) const;
	/** Find the input index that corresponds to the hotspot index. */
	uint16 hotspotToInput(uint16 hotspot) const;
	/** Find the input that was clicked on. */
	uint16 findClickedInput(uint16 index) const;
	/** Find the first input hotspot with a leave function. */
	bool findFirstInputLeave(uint16 &id, uint16 &inputId, uint16 &index) const;
	/** Find the hotspot with the matching key, case sensitively. */
	bool findKey(uint16 key, uint16 &id, uint16 &index) const;
	/** Find the hotspot with the matching key, case insensitively. */
	bool findKeyCaseInsensitive(uint16 key, uint16 &id, uint16 &index) const;
	/** Find the nth plain (without Type1 or Type2 state) hotspot. */
	bool findNthPlain(uint16 n, uint16 startIndex, uint16 &id, uint16 &index) const;

	/** Leave the nth plain (without Type1 or Type2 state) hotspot. */
	bool leaveNthPlain(uint16 n, uint16 startIndex, int16 timeVal, const uint16 *ids,
			uint16 &id, uint16 &index, int16 &duration);

	// Hotspot ID variable access
	void setCurrentHotspot(const uint16 *ids, uint16 id) const;
	uint32 getCurrentHotspot() const;

	// String input functions
	void cleanFloatString(const Hotspot &spot) const;
	void checkStringMatch(const Hotspot &spot, const InputDesc &input,
			uint16 inputPos) const;
	void matchInputStrings(const InputDesc *inputs) const;

	uint16 convertSpecialKey(uint16 key) const;

	/** Calculate the graphical cursor position. */
	void getTextCursorPos(const Font &font, const char *str,
			uint32 pos, uint16 x, uint16 y, uint16 width, uint16 height,
			uint16 &cursorX, uint16 &cursorY, uint16 &cursorWidth, uint16 &cursorHeight) const;

	/** Fill that rectangle with the color. */
	void fillRect(uint16 x, uint16 y, uint16 width, uint16 height, uint16 color) const;
	/** Print the given text. */
	void printText(uint16 x, uint16 y, const char *str, uint16 fontIndex, uint16 color) const;

	/** Go through all inputs we manage and redraw their texts. */
	void updateAllTexts(const InputDesc *inputs) const;
};

} // End of namespace Gob

#endif // GOB_HOTSPOTS_H