/* 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.
 *
 */

/*
 * This code is based on Broken Sword 2.5 engine
 *
 * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
 *
 * Licensed under GNU GPL v2
 *
 */

/*
    BS_RenderObject
    ---------------
    Dieses ist die Klasse die s�mtliche sichtbaren Objekte beschreibt. Alle anderen sichtbaren Objekte m�ssen von ihr abgeleitet werden.
    Diese Klasse erledigt Aufgaben wie: minimales Neuzeichnen, Renderreihenfolge, Objekthierachie.
    Alle BS_RenderObject Instanzen werden von einem BS_RenderObjectManager in einem Baum verwaltet.

    Autor: Malte Thiesen
*/

#ifndef SWORD25_RENDEROBJECT_H
#define SWORD25_RENDEROBJECT_H

#include "sword25/kernel/common.h"
#include "sword25/kernel/persistable.h"
#include "common/rect.h"
#include "sword25/gfx/renderobjectptr.h"

#include "common/list.h"

namespace Sword25 {

class Kernel;
class RenderObjectManager;
class RenderObjectQueue;
class RectangleList;
class Bitmap;
class Animation;
class AnimationTemplate;
class Panel;
class Text;

// Klassendefinition
/**
    @brief  Dieses ist die Klasse die s�mtliche sichtbaren Objekte beschreibt.

    Alle anderen sichtbaren Objekte m�ssen von ihr abgeleitet werden.
    Diese Klasse erledigt Aufgaben wie: minimales Neuzeichnen, Renderreihenfolge, Objekthierachie.
    Alle BS_RenderObject Instanzen werden von einem BS_RenderObjektManager in einem Baum verwaltet.
 */
class RenderObject {
public:
	// Konstanten
	// ----------
	enum TYPES {
		/// Das Wurzelobjekt. Siehe BS_RenderObjectManager
		TYPE_ROOT,
		/// Ein Image. Siehe BS_Bitmap.
		TYPE_STATICBITMAP,
		TYPE_DYNAMICBITMAP,
		/// Eine Animation. Siehe BS_Animation.
		TYPE_ANIMATION,
		/// Eine farbige Fl�che. Siehe BS_Panel.
		TYPE_PANEL,
		/// Ein Text. Siehe BS_Text.
		TYPE_TEXT,
		/// Ein unbekannter Objekttyp. Diesen Typ sollte kein Renderobjekt annehmen.
		TYPE_UNKNOWN
	};

	// Add-Methoden
	// ------------

	/**
	    @brief Erzeugt ein Bitmap als Kinderobjekt des Renderobjektes.
	    @param FileName der Dateiname der Quellbilddatei
	    @return Gibt einen BS_RenderObjectPtr auf das erzeugte Objekt zur�ck.<br>
	            Falls ein Fehler aufgetreten ist wird ein ung�ltiger BS_RenderObjectPtr zur�ckgegeben.
	*/
	RenderObjectPtr<Bitmap> addBitmap(const Common::String &fileName);
	/**
	    @brief Erzeugt ein ver�nderbares Bitmap als Kinderobjekt des Renderobjektes.
	    @param Width die Breite des Bitmaps
	    @param Height die H�he des Bitmaps
	    @return Gibt einen BS_RenderObjectPtr auf das erzeugte Objekt zur�ck.<br>
	            Falls ein Fehler aufgetreten ist wird ein ung�ltiger BS_RenderObjectPtr zur�ckgegeben.
	*/
	RenderObjectPtr<Bitmap> addDynamicBitmap(uint width, uint height);
	/**
	    @brief Erzeugt eine Animation auf Basis einer Animationsdatei als Kinderobjekt des Renderobjektes.
	    @param FileName der Dateiname der Quelldatei
	    @return Gibt einen BS_RenderObjectPtr auf das erzeugte Objekt zur�ck.<br>
	            Falls ein Fehler aufgetreten ist wird ein ung�ltiger BS_RenderObjectPtr zur�ckgegeben.
	*/
	RenderObjectPtr<Animation> addAnimation(const Common::String &fileName);
	/**
	    @brief Erzeugt eine Animation auf Basis eines Animationstemplate als Kinderobjekt des Renderobjektes.
	    @param pAnimationTemplate ein Pointer auf das Animationstemplate
	    @return Gibt einen BS_RenderObjectPtr auf das erzeugte Objekt zur�ck.<br>
	            Falls ein Fehler aufgetreten ist wird ein ung�ltiger BS_RenderObjectPtr zur�ckgegeben.
	    @remark Das Renderobjekt �bernimmt die Verwaltung des Animationstemplate.
	*/
	RenderObjectPtr<Animation> addAnimation(const AnimationTemplate &animationTemplate);
	/**
	    @brief Erzeugt ein neues Farbpanel als Kinderobjekt des Renderobjektes.
	    @param Width die Breite des Panels
	    @param Height die H�he des Panels
	    @param Color die Farbe des Panels.<br>
	           Der Standardwert ist Schwarz (BS_RGB(0, 0, 0)).
	    @return Gibt einen BS_RenderObjectPtr auf das erzeugte Objekt zur�ck.<br>
	            Falls ein Fehler aufgetreten ist wird ein ung�ltiger BS_RenderObjectPtr zur�ckgegeben.
	*/

	RenderObjectPtr<Panel> addPanel(int width, int height, uint color = 0xff000000);
	/**
	    @brief Erzeugt ein Textobjekt als Kinderobjekt des Renderobjektes.
	    @param Font der Dateiname des zu verwendenen Fonts
	    @param Text der anzuzeigende Text.<br>
	                Der Standardwert ist "".
	    @return Gibt einen BS_RenderObjectPtr auf das erzeugte Objekt zur�ck.<br>
	            Falls ein Fehler aufgetreten ist wird ein ung�ltiger BS_RenderObjectPtr zur�ckgegeben.
	*/
	RenderObjectPtr<Text> addText(const Common::String &font, const Common::String &text = "");

	// Cast-Methoden
	// -------------
	/**
	    @brief Castet das Objekt zu einem BS_Bitmap-Objekt wenn zul�ssig.
	    @return Gibt einen BS_RenderObjectPtr auf das Objekt zur�ck.<br>
	            Falls der Cast nicht zul�ssig ist, wird ein ung�ltiger BS_RenderObjectPtr zur�ckgegeben.
	*/
	RenderObjectPtr<Bitmap> toBitmap() {
		if (_type == TYPE_STATICBITMAP || _type == TYPE_DYNAMICBITMAP)
			return RenderObjectPtr<Bitmap>(this->getHandle());
		else
			return RenderObjectPtr<Bitmap>();
	}
	/**
	    @brief Castet das Objekt zu einem BS_Animation-Objekt wenn zul�ssig.
	    @return Gibt einen BS_RenderObjectPtr auf das Objekt zur�ck.<br>
	            Falls der Cast nicht zul�ssig ist, wird ein ung�ltiger BS_RenderObjectPtr zur�ckgegeben.
	*/
	RenderObjectPtr<Animation> toAnimation() {
		if (_type == TYPE_ANIMATION)
			return RenderObjectPtr<Animation>(this->getHandle());
		else
			return RenderObjectPtr<Animation>();
	}
	/**
	    @brief Castet das Objekt zu einem BS_Panel-Objekt wenn zul�ssig.
	    @return Gibt einen BS_RenderObjectPtr auf das Objekt zur�ck.<br>
	            Falls der Cast nicht zul�ssig ist, wird ein ung�ltiger BS_RenderObjectPtr zur�ckgegeben.
	*/
	RenderObjectPtr<Panel> toPanel() {
		if (_type == TYPE_PANEL)
			return RenderObjectPtr<Panel>(this->getHandle());
		else
			return RenderObjectPtr<Panel>();
	}
	/**
	    @brief Castet das Object zu einem BS_Text-Objekt wenn zul�ssig.
	    @return Gibt einen BS_RenderObjectPtr auf das Objekt zur�ck.<br>
	            Falls der Cast nicht zul�ssig ist, wird ein ung�ltiger BS_RenderObjectPtr zur�ckgegeben.
	*/
	RenderObjectPtr<Text> toText() {
		if (_type == TYPE_TEXT)
			return RenderObjectPtr<Text>(this->getHandle());
		else
			return RenderObjectPtr<Text>();
	}

	// Konstruktor / Desktruktor
	// -------------------------
	/**
	    @brief Erzeugt ein neues BS_RenderObject.
	    @param pKernel ein Pointer auf den Kernel
	    @param pParent ein Pointer auf das Elternobjekt des neuen Objektes im Objektbaum.<br>
	                   Der Pointer darf nicht NULL sein.
	    @param Type der Objekttyp<br>
	                Der Typ BS_RenderObject::TYPE_ROOT ist nicht zul�ssig. Wurzelknoten m�ssen mit dem alternativen Konstruktor erzeugt
	                werden.
	    @param Handle das Handle, welches dem Objekt zugewiesen werden soll.<br>
	                Dieser Parameter erzwingt ein bestimmtes Handle f�r das neue Objekt, oder w�hlt automatisch ein Handle, wenn der Parameter 0 ist.
	                Ist das gew�nschte Handle bereits vergeben, gibt GetInitSuccess() false zur�ck.<br>
	                Der Standardwert ist 0.
	    @remark Nach dem Aufruf des Konstruktors kann �ber die Methode GetInitSuccess() abgefragt werden, ob die Konstruktion erfolgreich war.<br>
	            Es ist nicht notwendig alle BS_RenderObject Instanzen einzeln zu l�schen. Dieses geschiet automatisch beim L�schen eines
	            Vorfahren oder beim L�schen des zust�ndigen BS_RenderObjectManager.
	 */
	RenderObject(RenderObjectPtr<RenderObject> pParent, TYPES type, uint handle = 0);
	virtual ~RenderObject();

	// Interface
	// ---------

	void preRender(RenderObjectQueue *renderQueue);

	/**
	    @brief Rendert des Objekt und alle seine Unterobjekte.
	    @return Gibt false zur�ck, falls beim Rendern ein Fehler aufgetreten ist.
	    @remark Vor jedem Aufruf dieser Methode muss ein Aufruf von UpdateObjectState() erfolgt sein.
	            Dieses kann entweder direkt geschehen oder durch den Aufruf von UpdateObjectState() an einem Vorfahren-Objekt.<br>
	            Diese Methode darf nur von BS_RenderObjectManager aufgerufen werden.
	*/
	bool render(RectangleList *updateRects, const Common::Array<int> &updateRectsMinZ);

	/**
	    @brief Bereitet das Objekt und alle seine Unterobjekte auf einen Rendervorgang vor.
	           Hierbei werden alle Dirty-Rectangles berechnet und die Renderreihenfolge aktualisiert.
	    @return Gibt false zur�ck, falls ein Fehler aufgetreten ist.
	    @remark Diese Methode darf nur von BS_RenderObjectManager aufgerufen werden.
	 */
	bool updateObjectState();
	/**
	    @brief L�scht alle Kinderobjekte.
	*/
	void deleteAllChildren();

	// Accessor-Methoden
	// -----------------
	/**
	    @brief Setzt die Position des Objektes.
	    @param X die neue X-Koordinate des Objektes relativ zum Elternobjekt.
	    @param Y die neue Y-Koordinate des Objektes relativ zum Elternobjekt.
	 */
	virtual void setPos(int x, int y);
	/**
	    @brief Setzt die Position des Objektes auf der X-Achse.
	    @param X die neue X-Koordinate des Objektes relativ zum Elternobjekt.
	 */
	virtual void setX(int x);
	/**
	    @brief Setzt die Position des Objektes auf der Y-Achse.
	    @param Y die neue Y-Koordinate des Objektes relativ zum Elternobjekt.
	 */
	virtual void setY(int y);
	/**
	    @brief Setzt den Z-Wert des Objektes.
	    @param Z der neue Z-Wert des Objektes relativ zum Elternobjekt<br>
	             Negative Z-Werte sind nicht zul�ssig.
	    @remark Der Z-Wert legt die Renderreihenfolge der Objekte fest. Objekte mit niedrigem Z-Wert werden vor Objekten mit h�herem
	            Z-Wert gezeichnet. Je h�her der Z-Wert desto weiter "vorne" liegt ein Objekt also.<br>
	            Wie alle andere Attribute ist auch dieses relativ zum Elternobjekt, ein Kinderobjekt kann also nie unter seinem
	            Elternobjekt liegen, auch wenn es einen Z-Wert von 0 hat.
	 */
	virtual void setZ(int z);
	/**
	    @brief Setzt die Sichtbarkeit eine Objektes.
	    @param Visible der neue Sichtbarkeits-Zustand des Objektes<br>
	                   true entspricht sichtbar, false entspricht unsichtbar.
	 */
	virtual void setVisible(bool visible);
	/**
	    @brief Gibt die Position des Objektes auf der X-Achse relativ zum Elternobjekt zur�ck.
	 */
	virtual int getX() const {
		return _x;
	}
	/**
	    @brief Gibt die Position des Objektes auf der Y-Achse relativ zum Elternobjekt zur�ck.
	 */
	virtual int getY() const {
		return _y;
	}
	/**
	    @brief Gibt die absolute Position des Objektes auf der X-Achse zur�ck.
	*/
	virtual int getAbsoluteX() const {
		return _absoluteX;
	}
	/**
	    @brief Gibt die absolute Position des Objektes auf der Y-Achse zur�ck.
	*/
	virtual int getAbsoluteY() const {
		return _absoluteY;
	}
	/**
	    @brief Gibt den Z-Wert des Objektes relativ zum Elternobjekt zur�ck.
	    @remark Der Z-Wert legt die Renderreihenfolge der Objekte fest. Objekte mit niedrigem Z-Wert werden vor Objekten mit h�herem
	            Z-Wert gezeichnet. Je h�her der Z-Wert desto weiter "vorne" liegt ein Objekt also.<br>
	            Wie alle andere Attribute ist auch dieses relativ zum Elternobjekt, ein Kinderobjekt kann also nie unter seinem
	            Elternobjekt liegen, auch wenn es einen Z-Wert von 0 hat.
	 */
	int         getZ() const {
		return _z;
	}

	int getAbsoluteZ() const {
		return _absoluteZ;
	}

	/**
	    @brief Gibt die Breite des Objektes zur�ck.
	 */
	int         getWidth() const {
		return _width;
	}
	/**
	    @brief Gibt die H�he des Objektes zur�ck.
	 */
	int         getHeight() const {
		return _height;
	}
	/**
	    @brief Gibt den Sichtbarkeitszustand des Objektes zur�ck.
	    @return Gibt den Sichtbarkeitszustand des Objektes zur�ck.<br>
	            true entspricht sichtbar, false entspricht unsichtbar.
	 */
	bool        isVisible() const {
		return _visible;
	}
	/**
	    @brief Gibt den Typ des Objektes zur�ck.
	 */
	TYPES       getType() const {
		return _type;
	}
	/**
	    @brief Gibt zur�ck, ob das Objekt erfolgreich initialisiert wurde.
	    @remark H�sslicher Workaround um das Problem, dass Konstruktoren keine R�ckgabewerte haben.
	 */
	bool        getInitSuccess() const {
		return _initSuccess;
	}
	/**
	    @brief Gibt die Bounding-Box des Objektes zur�ck.
	    @remark Diese Angabe erfolgt ausnahmsweise in Bildschirmkoordianten und nicht relativ zum Elternobjekt.
	 */
	const Common::Rect  &getBbox() const {
		return _bbox;
	}
	/**
	    @brief Stellt sicher, dass das Objekt im n�chsten Frame neu gezeichnet wird.
	*/
	void forceRefresh() {
		_refreshForced = true;
	}
	/**
	    @brief Gibt das Handle des Objekte zur�ck.
	*/
	uint32 getHandle() const {
		return _handle;
	}

	// Get the RenderObjects current version
	int getVersion() const {
		return _version;
	}

	bool isSolid() const {
		return _isSolid;
	}

	// Persistenz-Methoden
	// -------------------
	virtual bool persist(OutputPersistenceBlock &writer);
	virtual bool unpersist(InputPersistenceBlock &reader);
	// TODO: Evtl. protected
	bool persistChildren(OutputPersistenceBlock &writer);
	bool unpersistChildren(InputPersistenceBlock &reader);
	// TODO: Evtl. private
	RenderObjectPtr<RenderObject> recreatePersistedRenderObject(InputPersistenceBlock &reader);

protected:
	// Typen
	// -----
	typedef Common::List<RenderObjectPtr<RenderObject> >          RENDEROBJECT_LIST;
	typedef Common::List<RenderObjectPtr<RenderObject> >::iterator    RENDEROBJECT_ITER;

	int32       _x;            ///< Die X-Position des Objektes relativ zum Eltern-Objekt
	int32       _y;            ///< Die Y-Position des Objektes relativ zum Eltern-Objekt
	int32       _z;            ///< Der Z-Wert des Objektes relativ zum Eltern-Objekt
	int32       _absoluteX;    ///< Die absolute X-Position des Objektes
	int32       _absoluteY;    ///< Die absolute Y-Position des Objektes
	int32		_absoluteZ;
	int32       _width;        ///< Die Breite des Objektes
	int32       _height;       ///< Die H�he des Objektes
	bool        _visible;      ///< Ist true, wenn das Objekt sichtbar ist
	bool        _childChanged; ///< Ist true, wenn sich ein Kinderobjekt ver�ndert hat
	TYPES       _type;         ///< Der Objekttyp
	bool        _initSuccess;  ///< Ist true, wenn Objekt erfolgreich intialisiert werden konnte
	Common::Rect _bbox;         ///< Die Bounding-Box des Objektes in Bildschirmkoordinaten

	// Kopien der Variablen, die f�r die Errechnung des Dirty-Rects und zur Bestimmung der Objektver�nderung notwendig sind
	Common::Rect     _oldBbox;
	int32       _oldX;
	int32       _oldY;
	int32       _oldZ;
	bool        _oldVisible;

	static int _nextGlobalVersion;

	int32 _version;

	// This should be set to true if the RenderObject is NOT alpha-blended to optimize drawing
	bool _isSolid;

	/// Ein Pointer auf den BS_RenderObjektManager, der das Objekt verwaltet.
	RenderObjectManager *_managerPtr;

	// Render-Methode
	// --------------
	/**
	    @brief Einschubmethode, die den tats�chlichen Redervorgang durchf�hrt.

	    Diese Methode wird von Render() aufgerufen um das Objekt darzustellen.
	    Diese Methode sollte von allen Klassen implementiert werden, die von BS_RederObject erben, um das Zeichnen umzusetzen.

	    @return Gibt false zur�ck, falls das Rendern fehlgeschlagen ist.
	    @remark
	 */
	virtual bool doRender(RectangleList *updateRects) = 0; // { return true; }

	// RenderObject-Baum Variablen
	// ---------------------------
	// Der Baum legt die hierachische Ordnung der BS_RenderObjects fest.
	// Alle Eigenschaften wie X, Y, Z und Visible eines BS_RenderObjects sind relativ zu denen seines Vaters.
	// Au�erdem sind die Objekte von links nach rechts in ihrer Renderreihenfolge sortiert.
	// Das prim�re Sortierkriterium ist hierbei der Z-Wert und das sekund�re der Y-Wert (von oben nach unten).
	// Beispiel:
	//                  Screen
	//                 /  |   \.
	//               /    |     \.
	//             /      |       \.
	//           /        |         \.
	//     Background Interface   Maus
	//      /   \      /  |   \.
	//    /      \    /   |    \.
	// George   T�r Icn1 Icn2 Icn3
	//
	// Wenn jetzt das Interface mit SetVisible() ausgeblendet w�rde, verschwinden auch die Icons, die sich im Interface
	// befinden.
	// Wenn der Hintergrund bewegt wird (Scrolling), bewegen sich auch die darauf befindlichen Gegenst�nde und Personen.

	/// Ein Pointer auf das Elternobjekt.
	RenderObjectPtr<RenderObject> _parentPtr;
	/// Die Liste der Kinderobjekte nach der Renderreihenfolge geordnet
	RENDEROBJECT_LIST           _children;

	/**
	    @brief Gibt einen Pointer auf den BS_RenderObjektManager zur�ck, der das Objekt verwaltet.
	 */
	RenderObjectManager *getManager() const {
		return _managerPtr;
	}
	/**
	    @brief F�gt dem Objekt ein neues Kinderobjekt hinzu.
	    @param pObject ein Pointer auf das einzuf�gende Objekt
	    @return Gibt false zur�ck, falls das Objekt nicht eingef�gt werden konnte.
	*/
	bool addObject(RenderObjectPtr<RenderObject> pObject);

private:
	/// Ist true, wenn das Objekt in n�chsten Frame neu gezeichnet werden soll
	bool _refreshForced;

	uint32 _handle;

	/**
	    @brief Entfernt ein Objekt aus der Kinderliste.
	    @param pObject ein Pointer auf das zu entfernende Objekt
	    @return Gibt false zur�ck, falls das zu entfernende Objekt nicht in der Liste gefunden werden konnte.
	 */
	bool detatchChildren(RenderObjectPtr<RenderObject> pObject);
	/**
	    @brief Berechnet die Bounding-Box und registriert das Dirty-Rect beim BS_RenderObjectManager.
	 */
	void updateBoxes();
	/**
	    @brief Berechnet die Bounding-Box des Objektes.
	    @return Gibt die Bounding-Box des Objektes in Bildschirmkoordinaten zur�ck.
	*/
	Common::Rect calcBoundingBox() const;
	/**
	    @brief Berechnet das Dirty-Rectangle des Objektes.
	    @return Gibt das Dirty-Rectangle des Objektes in Bildschirmkoordinaten zur�ck.
	*/
	Common::Rect calcDirtyRect() const;
	/**
	    @brief Berechnet die absolute Position des Objektes.
	*/
	void calcAbsolutePos(int32 &x, int32 &y, int32 &z) const;
	/**
	    @brief Berechnet die absolute Position des Objektes auf der X-Achse.
	*/
	int32 calcAbsoluteX() const;
	/**
	    @brief Berechnet die absolute Position des Objektes.
	*/
	int32 calcAbsoluteY() const;

	int32 calcAbsoluteZ() const;

	/**
	    @brief Sortiert alle Kinderobjekte nach ihrem Renderang.
	 */
	void sortRenderObjects();
	/**
	    @brief Validiert den Zustand eines Objektes nachdem die durch die Ver�nderung verursachten Folgen abgearbeitet wurden.
	 */
	void validateObject();
	/**
	    @brief Berechnet die absolute Position des Objektes und aller seiner Kinderobjekte neu.

	    Diese Methode muss aufgerufen werden, wann immer sich die Position des Objektes ver�ndert. Damit die Kinderobjekte immer die
	    richtige absolute Position haben.
	*/
	void updateAbsolutePos();
	/**
	    @brief Teilt dem Objekt mit, dass sich eines seiner Kinderobjekte dahingehend ver�ndert hat, die eine erneute Bestimmung der
	           Rendereihenfolge verlangt.
	*/
	void signalChildChange() {
		_childChanged = true;
	}
	/**
	    @brief Berechnet des Schnittrechteck der Bounding-Box des Objektes mit einem anderen Objekt.
	    @param pObjekt ein Pointer auf das Objekt mit dem geschnitten werden soll
	    @param Result das Ergebnisrechteck
	    @return Gibt false zur�ck, falls sich die Objekte gar nicht schneiden.
	 */
	bool getObjectIntersection(RenderObjectPtr<RenderObject> pObject, Common::Rect &result);
	/**
	    @brief Vergleichsoperator der auf Objektpointern basiert statt auf Objekten.
	    @remark Diese Methode wird f�rs Sortieren der Kinderliste nach der Rendereihenfolge benutzt.
	*/
	static bool greater(const RenderObjectPtr<RenderObject> lhs, const RenderObjectPtr<RenderObject> rhs);
};

} // End of namespace Sword25

#endif