#ifndef EMOTICONS_H
#define EMOTICONS_H

#include <qcolor.h>
#include <qmovie.h>
#include <private/qrichtext_p.h>
#include <qstringlist.h>
#include <qtoolbutton.h>
#include <qvaluevector.h>
#include <qlabel.h>

#include "config_file.h"
#include "configuration_aware_object.h"
#include "html_document.h"
#include "themes.h"

class ChatWidget;
class EmotsWalker;
class QImage;
class QLabel;
class QTextEdit;
class QScrollView;

enum EmoticonsStyle
{
	EMOTS_NONE,
	EMOTS_STATIC,
	EMOTS_ANIMATED
};

/**
	Menadżer emotikonów
**/
class EmoticonsManager : public Themes, ConfigurationAwareObject
{
	private:
		struct EmoticonsListItem
		{
			QString alias;
			QString anim;
			QString stat;
			EmoticonsListItem();
		};
		QValueVector<EmoticonsListItem> Aliases;
		QValueList<EmoticonsListItem> Selector;
		EmotsWalker *walker;

		static QString getQuoted(const QString& s, unsigned int& pos);

		bool loadGGEmoticonThemePart(QString subdir);
		bool loadGGEmoticonTheme();
	public:
		static void initModule();
		static void closeModule();

		/**
		Konstruktor tworzy obiekt oraz wyszukuje listę zestawów w katalogu
		$DATADIR/kadu/themes/emoticons
		**/
		EmoticonsManager(const QString& name, const QString& configname);
		~EmoticonsManager();

		/**
		\brief Funkcja ustawia zestaw emotikonek na theme
		\param theme nazwa zestawu
		Funkcja ustawi zestaw jeśli taki istnieje w przeciwnym wypadku
		ustawionym zestawem będzie "gadu-gadu"
		**/
		void setEmoticonsTheme(const QString& theme);

		/**
		Funkcja wstawia w text zamiast tagów emotikonek odpowiednie emotikonki
		\param text dokument w którym mają być zamienione tagi
		\param bgcolor kolor tła emotikonki
		\param style styl emotikonki jaki ma być wstawiony - domyślnie jest do
		 aktualny zestaw
		**/
		void expandEmoticons(HtmlDocument& text, const QColor& bgcolor,
			EmoticonsStyle style=(EmoticonsStyle) config_file.readNumEntry("Chat", "EmoticonsStyle"));
		/**
		Funkcja zwraca ilość emotikonek w zestawie
		**/
		int selectorCount() const;
		/**
		Funkcja zwraca alias odpowiedniej emotikonki
		\param emot_num nr emotikonki z listy
		\return Zwracany jest pusty ciąg jeśli nie znaleziono emotikonki w przeciwnym razie zwracany jest alias.
		**/
		QString selectorString(int emot_num) const;
		/**
		Funkcja zwraca ścieżkę do odpowiedniej animowanej emotikonki
		\param emot_num nr emotikonki z listy
		\return Zwracany jest pusty ciąg jeśli nie znaleziono emotikonki w przeciwnym razie zwracana jest ścieżka do emotikonki
		**/
		QString selectorAnimPath(int emot_num) const;
		/**
		Funkcja zwraca ścieżkę do odpowiedniej statycznej emotikonki
		\param emot_num nr emotikonki z listy
		\return Zwracany jest pusty ciąg jeśli nie znaleziono emotikonki w przeciwnym razie zwracana jest ścieżka do emotikonki
		**/
		QString selectorStaticPath(int emot_num) const;

		virtual void configurationUpdated();
};

extern EmoticonsManager *emoticons;

/**
	Klasa służąca do wyboru emotikonki z zestawu
**/
class EmoticonSelectorButton : public QToolButton
{
	Q_OBJECT
	private:
		QString EmoticonString;
		QString AnimPath;
		QString StaticPath;
		QMovie* Movie;
	private slots:
		void buttonClicked();
		void movieUpdate();
	protected:
		/**
		Funkcja obsługująca najechanie kursorem myszki na daną emotikonkę.
		**/
		void enterEvent(QEvent* e);
		/**
		Funkcja obsługująca opuszczenie obszaru wyświetlania emotikonki.
		**/
		void leaveEvent(QEvent* e);
	public:
		/**
		Konstruktor tworzący przycisk z emotikonką
		\param parent widget na którym osadzona ma być przycisk z emotikonką
		\param emoticon_string nazwa która ma być wyświetlana po najechaniu na
		przycisk
		\param static_path ścieżka do statycznej emotikonki
		\param anim_path ścieżka do animowanej emotikonki
		**/
		EmoticonSelectorButton(
			QWidget* parent,const QString& emoticon_string,
			const QString& static_path,const QString& anim_path);
		~EmoticonSelectorButton();
	signals:
		/**
		Sygnał emitowany po naciśnięciu przycisku z emotikonką
		przekazywana jest wartość emoticon_string (przeważnie jest to tag)
		**/
		void clicked(const QString& emoticon_string);
};
/**
	Klasa wyświetlająca listę emotikonek z aktualnego zestawu.
**/
class EmoticonSelector : public QWidget
{
	Q_OBJECT
	private:
		ChatWidget *callingwidget;
	private slots:
		void iconClicked(const QString& emoticon_string);
	protected:
		/**
		Funkcja obsługująca zamknięcie listy.
		**/
		void closeEvent(QCloseEvent *e);
	public:
		/**
		Konstruktor tworzący listę emotikonek.
		\param parent rodzic na którym ma być wyświetlona lista
		\param name nazwa obiektu
		\param caller okno chat do ktorego ma być wpisana wybrana emotikonka
		**/
		EmoticonSelector(QWidget* parent = 0, const char *name = 0, ChatWidget *caller = 0);
	public slots:
		/**
		Slot obsługujący poprawne wyświetlenie listy emotikonek, wyrównanie do
		okna wywołującego.
		**/
		void alignTo(QWidget* w);
};

/* Klasa wycięta za źródeł Qt i przystosowana na potrzeby Kadu (oryginalnie nazywała się QTextImage) */
class StaticTextItem : public QTextCustomItem
{
	public:
		StaticTextItem(QTextDocument *p, const QMap<QString, QString> &attr, const QString& context,
			QMimeSourceFactory &factory );
		virtual ~StaticTextItem();

		Placement placement() const { return place; }
		void adjustToPainter( QPainter* );
		int minimumWidth() const { return width; }

		QString richText() const;
		void draw(QPainter* p, int x, int y, int cx, int cy, int cw, int ch, const QColorGroup& cg, bool selected);
	private:
		QRegion* reg;
		QPixmap pm;
		Placement place;
		int tmpwidth, tmpheight;
		QMap<QString, QString> attributes;
		QString imgId;
};

class AnimatedLabel;

class AnimTextItem : public QTextCustomItem
{
	public:
		struct MovieCacheData
		{
			QMovie movie;
			QSize size;
			int count;
			int runCount;
			MovieCacheData(const QString &fileName);
		};
	private:
		static QImage* SizeCheckImage;
		typedef QMap<QString,MovieCacheData*> MoviesCache;
		static MoviesCache* Movies;
		QTextEdit* Edit;
		AnimatedLabel* Label;
		QSize EditSize;
		QString text;
		QString FileName;
	public:
		AnimTextItem(
			QTextDocument *p, QTextEdit* edit,
			const QString& filename, const QColor& bgcolor, const QString &tip);
		~AnimTextItem();
		void draw(
			QPainter* p, int x, int y, int cx, int cy,
			int cw, int ch, const QColorGroup& cg,
			bool selected );
		virtual QString richText() const;

};

class AnimatedLabel : public QLabel
{
	Q_OBJECT
	public:
	AnimTextItem::MovieCacheData *movieData;
	QScrollView *scrollView;
	QString tip;
	static bool mustPause;
	bool imageBackground;
	bool paused;
	int lastX, lastY;
	bool trueTransparency;
	public:
		AnimatedLabel(AnimTextItem::MovieCacheData *data, const QString &tip, bool imageBackground,
						QScrollView *view, bool trueTransparency = false, const char *name = 0);
		~AnimatedLabel();
	public slots:
		void unpauseMovie();
		void pauseMovie();
	protected:
		virtual void paintEvent(QPaintEvent *e);
};

class AnimStyleSheet : public QStyleSheet
{
	private:
		QString Path;
	public:
		AnimStyleSheet(QTextEdit* parent, const QString& path, const char* name = 0);
		QTextCustomItem* tag(
			const QString& name, const QMap<QString,QString>& attr,
			const QString& context, const QMimeSourceFactory& factory,
			bool emptyTag, QTextDocument* doc) const;
};

class StaticStyleSheet : public QStyleSheet
{
	private:
		QString Path;
	public:
		StaticStyleSheet(QTextEdit* parent, const QString& path, const char* name = 0);
		QTextCustomItem* tag(
			const QString& name, const QMap<QString,QString>& attr,
			const QString& context, const QMimeSourceFactory& factory,
			bool emptyTag, QTextDocument* doc) const;
};



struct PrefixNode
{
	int emotIndex;
	QValueVector<QPair<QChar, PrefixNode*> > childs;
	PrefixNode();
};

/**
	this class serves as dictionary of emots, allowing easy
	finding of their occurrences in text;
	new search is initialized by calling 'initWalking()'
	then characters are put into analysis by 'checkEmotOccurrence(c)'
*/
class EmotsWalker
{
	/** dictionary is based on prefix tree */
	PrefixNode* root;
	QPair<QChar, PrefixNode*> myPair;
	/** positions in prefix tree, representing current analysis of text */
	QValueVector<const PrefixNode*> positions;
	QValueVector<int> lengths;
	unsigned int amountPositions;

	public:
		EmotsWalker();
		~EmotsWalker();

	private:
		PrefixNode* findChild( const PrefixNode* node, const QChar& c );
		PrefixNode* insertChild( PrefixNode* node, const QChar& c );
		void removeChilds( PrefixNode* node );

	public:
		/**
			adds given string (emot) to dictionary of emots, giving it
			number, which will be used later to notify occurrences of
			emot in analyzed text
		*/
		void insertString( const QString& str, int num );

		/**
			return number of emot, which occurre in analyzed text just
			after adding given character (thus ending on this character)
			beginning of text analysis is turned on by 'initWalking()'
			if no emot occures, -1 is returned
		*/
		int checkEmotOccurrence( const QChar& c );

		/**
			clear internal structures responsible for analyzing text, it allows
			begin of new text analysis
		*/
		void initWalking();
};

#endif
