#ifndef EMOTICONS_H
#define EMOTICONS_H

#include <qmovie.h>
#include <qmap.h>
#include <private/qrichtext_p.h>
#include <qstringlist.h>
#include <qtextedit.h>
#include <qtoolbutton.h>
#include <qvaluelist.h>
#include <qvaluevector.h>

#include "misc.h"

class Chat;
class EmotsWalker;
class QImage;
class QLabel;

enum EmoticonsStyle
{
	EMOTS_NONE,
	EMOTS_STATIC,
	EMOTS_ANIMATED
};

/**
	Menadżer emotikonów
**/
class EmoticonsManager
{
	private:
		QStringList ThemesList;
		struct EmoticonsListItem
		{
			QString alias;
			QString anim;
			QString stat;
		};
		QValueVector<EmoticonsListItem> Aliases;
		QValueList<EmoticonsListItem> Selector;
		EmotsWalker *walker;

		static QStringList getSubDirs(const QString& path);
		static QString getQuoted(const QString& s, unsigned int& pos);
		static QString fixFileName(const QString& path,const QString& fn);
		bool loadGGEmoticonThemePart(QString subdir);
		bool loadGGEmoticonTheme();
	public:
		/**
		Konstruktor tworzy obiekt oraz wyszukuje listę zestawów w katalogu
		$DATADIR/kadu/themes/emoticons
		**/
		EmoticonsManager();
		~EmoticonsManager();
		/**
		Funkcja zwraca listę zestawów
		**/
		const QStringList& themes() const;
		/**
		\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 zwraca ścieżkę do aktualnego zestawu
		**/
		QString themePath() const;
		/**
		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;
};

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:
		Chat *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, Chat *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 AnimTextItem : public QTextCustomItem
{
	private:
		static QImage* SizeCheckImage;
		struct MovieCacheData
		{
			QMovie movie;
			QSize size;
			int count;
		};
		typedef QMap<QString,MovieCacheData> MoviesCache;
		static MoviesCache* Movies;
		QTextEdit* Edit;
		QLabel* 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 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;
};

/**
	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<PrefixNode*> positions;
	QValueVector<int> lengths;
	int amountPositions;

	public:
		EmotsWalker();
		~EmotsWalker();

	private:
		PrefixNode* findChild( 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
