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

#include "graphics/surface.h"
#include "toltecs/toltecs.h"

namespace Toltecs {

struct DrawRequest {
	int16 x, y;
	int16 resIndex;
	uint16 flags;
	int16 baseColor;
	int8 scaling;
};

struct SpriteDrawItem {
	int16 x, y;
	int16 width, height;
	int16 origWidth, origHeight;
	int16 resIndex, frameNum;
	uint32 offset;
	int16 xdelta, ydelta;
	uint16 flags;
	int16 skipX, yerror;
	int16 priority;
	int16 baseColor;
};

struct SpriteFrameEntry {
	int16 y, x, h, w;
	uint32 offset;
	SpriteFrameEntry() {
	}
	SpriteFrameEntry(byte *data) {
		y = READ_LE_UINT16(data + 0);
		x = READ_LE_UINT16(data + 2);
		h = READ_LE_UINT16(data + 4);
		w = READ_LE_UINT16(data + 6);
		offset = READ_LE_UINT32(data + 8);
	}
};

class Font {
public:
	Font(byte *fontData) : _fontData(fontData) {
	}
	~Font() {
	}
	int16 getSpacing() const {
		return _fontData[1];
	}
	int16 getHeight() const {
		return _fontData[2];
	}
	int16 getWidth() const {
		return _fontData[3];
	}
	int16 getCharWidth(byte ch) const {
		return _fontData[4 + (ch - 0x21)];
	}
	byte *getCharData(byte ch) const {
		return _fontData + 0x298 + READ_LE_UINT16(&_fontData[0xE0 + (ch - 0x21) * 2]);
	}
	int16 getTextWidth(const byte *text) {
		int16 width = 0;
		while (*text && *text < 0xF0) {
			byte ch = *text++;
			if (ch <= 0x20) {
				width += getWidth();
			} else {
				width += getCharWidth(ch) + getSpacing() - 1;
			}
		}
		return width;
	}

protected:
	byte *_fontData;
};

struct PixelPacket {
	byte count;
	byte pixel;
};

enum SpriteReaderStatus {
	kSrsPixelsLeft,
	kSrsEndOfLine,
	kSrsEndOfSprite
};

class SpriteFilter {
public:
	SpriteFilter(const SpriteDrawItem &sprite) : _sprite(&sprite) {
	}
	virtual ~SpriteFilter() {}
	virtual SpriteReaderStatus readPacket(PixelPacket &packet) = 0;
protected:
	const SpriteDrawItem *_sprite;
};

struct TextRect {
	int16 x, y;
	int16 width, length;
};

struct TalkTextItem {
	int16 duration;
	int16 slotIndex;
	int16 slotOffset;
	int16 fontNum;
	byte color;
	byte lineCount;
	TextRect lines[15];
	bool alwaysDisplayed;
};

struct GuiTextWrapState {
	int16 len1, len2;
	byte *sourceString;
	byte *destString;
	int16 width;
	byte textBuffer[100];
};

class RenderQueue;

class Screen {
public:
	Screen(ToltecsEngine *vm);
	~Screen();

	void unpackRle(byte *source, byte *dest, uint16 width, uint16 height);

	void loadMouseCursor(uint resIndex);

	void drawGuiImage(int16 x, int16 y, uint resIndex);

	void startShakeScreen(int16 shakeCounter);
	void stopShakeScreen();
	bool updateShakeScreen();

	// Sprite list
	void addStaticSprite(byte *spriteItem);
	void addAnimatedSprite(int16 x, int16 y, int16 fragmentId, byte *data, int16 *spriteArray, bool loop, int mode);

	// Sprite drawing
	void drawSprite(const SpriteDrawItem &sprite);
	void drawSpriteCore(byte *dest, SpriteFilter &reader, const SpriteDrawItem &sprite);
	void blastSprite(int16 x, int16 y, int16 fragmentId, int16 resIndex, uint16 flags);

	// Verb line
	void updateVerbLine(int16 slotIndex, int16 slotOffset);

	// Talk text
	void updateTalkText(int16 slotIndex, int16 slotOffset, bool alwaysDisplayed);
	void addTalkTextRect(Font &font, int16 x, int16 &y, int16 length, int16 width, TalkTextItem *item);
	void addTalkTextItemsToRenderQueue();
	int16 getTalkTextDuration();
	bool isTalkTextActive(int16 slotIndex);
	void finishTalkTextItem(int16 slotIndex);
	void finishTalkTextItems();
	void keepTalkTextItemsAlive();

	// Font/text
	void registerFont(uint fontIndex, uint resIndex);
	void drawGuiTextMulti(byte *textData);
	int16 wrapGuiText(uint fontResIndex, int maxWidth, GuiTextWrapState &wrapState);
	void drawGuiText(int16 x, int16 y, byte fontColor1, byte fontColor2, uint fontResIndex, GuiTextWrapState &wrapState);

	int16 drawString(int16 x, int16 y, byte color, uint fontResIndex, const byte *text, int len = -1, int16 *ywobble = NULL, bool outline = false);
	void drawChar(const Font &font, byte *dest, int16 x, int16 y, byte ch, byte color, bool outline);

	void drawSurface(int16 x, int16 y, Graphics::Surface *surface);

	void saveState(Common::WriteStream *out);
	void loadState(Common::ReadStream *in);

	uint getFontResIndex(int fontNum) const { return _fontResIndexArray[fontNum]; }

//protected:
public:

	struct VerbLineItem {
		int16 slotIndex;
		int16 slotOffset;
	};

	struct Rect {
		int16 x, y, width, height;
	};

	ToltecsEngine *_vm;

	byte *_frontScreen, *_backScreen;

	uint _fontResIndexArray[10];
	byte _fontColor1, _fontColor2;

	// Screen shaking
	bool _shakeActive;
	uint32 _shakeTime;
	int16 _shakeCounterInit, _shakeCounter;
	int _shakePos;

	// Verb line
	int16 _verbLineNum;
	VerbLineItem _verbLineItems[8];
	int16 _verbLineX, _verbLineY, _verbLineWidth;
	int16 _verbLineCount;

	// Talk text
	int16 _talkTextX, _talkTextY;
	int16 _talkTextMaxWidth;
	byte _talkTextFontColor;
	int16 _talkTextItemNum;
	TalkTextItem _talkTextItems[5];

	RenderQueue *_renderQueue;
	bool _fullRefresh;
	bool _guiRefresh;

	bool createSpriteDrawItem(const DrawRequest &drawRequest, SpriteDrawItem &sprite);
	void addDrawRequest(const DrawRequest &drawRequest);

};

} // End of namespace Toltecs

#endif /* TOLTECS_SCREEN_H */
