#ifndef KADU_USERBOX_H
#define KADU_USERBOX_H

#include <qlistbox.h>
#include <qpixmap.h>
#include <qpopupmenu.h>
#include <qtimer.h>

#include <vector>

#include "gadu.h"
#include "misc.h"
#include "userlistelement.h"

class QFontMetrics;
class UserBoxSlots;
class ULEComparer;

/**
	Klasa reprezentująca kontakt wizualnie na liście kontaktów. Opisuje ona ikonę kontaktu,
	jego wyświetlaną nazwę oraz ewentualny opis.
	\class KaduListBoxPixmap
	\brief Klasa reprezentująca kontakt wizualnie.
**/
class KaduListBoxPixmap : public QListBoxItem
{
	public:
		/**
			\fn KaduListBoxPixmap(const QPixmap &pix, UserListElement user, bool bold = false)
			Konstruktor dodający kontakt do listy z podaną ikoną.
			\param pix ikona wyświetlana obok kontaktu.
			\param user kontakt, z którego będą pobierane dane do wyświetlenia
			\param bold wartość logiczna informująca o tym, czy nazwa kontaktu
				ma być wyświetlana pogrubioną czcionką.
		**/
		KaduListBoxPixmap(const QPixmap &pix, UserListElement user, bool bold = false);

		/**
			\fn bool isBold() const
			Zwraca informację, czy nazwa kontaktu jest wyświetlana pogrubioną czcionką, czy nie.
		**/
		bool isBold() const { return bold; }

		/**
			\fn int height(const QListBox *lb) const
			Zwraca wysokość elementu reprezentującego wyświetlany kontakt w pikselach.
			\param lb obiekt reprezentujący wyświetlany kontakt.
		**/
		int height(const QListBox *lb) const;

		/**
			\fn int width(const QListBox *lb) const
			Zwraca szerokość elementu reprezentującego wyświetlany kontakt w pikselach.
			\param lb obiekt reprezentujący wyświetlany kontakt.
		**/
		int width(const QListBox *lb) const;

		const UserListElement User;
	protected:
		/**
			\fn int lineHeight(const QListBox *lb) const
			Zwraca wysokość samej czcionki dla elementu reprezentującego wyświetlany kontakt w pikselach.
			\param lb obiekt reprezentujący wyświetlany kontakt.
		**/
		int lineHeight(const QListBox *lb) const;

		/**
			\fn void paint(QPainter *painter)
			Rysuje wyświetlany kontakt na liście.
			\param painter wskaźnik do obiektu rysującego.
		**/
		void paint(QPainter *painter);

		/**
			\fn void setBold(bool b)
			Ustawia stan pogrubienia czcionki wyświetlanego kontaktu.
			\param b wartość logiczna określająca, czy czcionka ma być pogrubiona czy nie.
		**/
		void setBold(bool b) { bold = b; }

		/**
			\fn void calculateSize(const QString &text, int width, QStringList &out, int &height) const
			Oblicza odpowiednią wysokość elementu listy kontaktów tak, aby pomieścić
			opis podzielony na niezbędną liczbę linii.
			\param[in] text wyświetlany opis.
			\param[in] width szerokość kolumny listy kontaktów.
			\param[out] out lista łańcuchów znaków, zawierająca kolejne linie opisu powstałe
				w wyniku podzielenia opisu tak, aby zmieścił się w danej szerokości kolumny.
			\param[out] height wysokość elementu listy kontaktów potrzebna aby pomieścić opis
				podzielony w liniach.
		**/
		void calculateSize(const QString &text, int width, QStringList &out, int &height) const;

		/**
			\fn void changeText(const QString &text)
			Zmienia nazwę wyświetlaną dla kontaktu na liście na podaną.
			\param text nowa nazwa do wyświetlenia.
		**/
		void changeText(const QString &text);
		friend class UserBox;
		friend class UserBoxSlots;

		/*funkcje wprowadzone żeby zaoszczędzić na odwołaniach do pliku konfiguracyjnego*/
		/**
			\fn static void setFont(const QFont &f)
			Ustawia czcionkę dla tego elementu.
			\param f czcionka
		**/
		static void setFont(const QFont &f);

		/**
			\fn static void setMyUIN(UinType u)
			Ustawia UIN lokalnego użytkownika Kadu dla tego elementu, aby było wiadomo,
			że ten element jego reprezentuje (ponieważ do pobierania informacji o statusie i opisie
			używane są różne metody dla kontaktu i lokalnego użytkownika).
			\param u numer UIN.
		**/
		static void setMyUIN(UinType u);

		/**
			\fn static void setShowDesc(bool sd)
			Ustawia stan wyświetlania opisów na liście kontaktów.
			\param sd wartość logiczna informująca o tym, czy opisy mają być wyświetlane na liście kontatów.
		**/
		static void setShowDesc(bool sd);

		/**
			\fn static void setAlignTop(bool at)
			Ustawia stan wyrównywania do górnego brzegu elementu.
			\param at wartość logiczna informująca o tym, czy wyświetlana nazwa kontaktu ma być wyrównywana
				do górnej krawędzi elementu.
		**/
		static void setAlignTop(bool at);

		/**
			\fn static void setShowMultilineDesc(bool m)
			Ustawia stan wyświetlania opisów wieloliniowych.
			\param m wartość logiczna informująca o tym, czy opis tego elementu może być wyświetlany
				w wielu liniach, jeśli zawiera znaki nowej linii.
		**/
		static void setShowMultilineDesc(bool m);

		/**
			\fn static void setMultiColumn(bool m)
			Ustawia stan wyświetlania listy kontaktów w wielu kolumnach.
			\param m wartość logiczna informująca o tym, czy lista ma być wyświetlana w wielu kolumnach.
		**/
		static void setMultiColumn(bool m);

		/**
			\fn static void setMultiColumnWidth(int w)
			Ustawia szerokość jednej kolumny dla wyświetlania listy w trybie wielu kolumn.
			\param w szerokość kolumny w pikselach.
		**/
		static void setMultiColumnWidth(int w);

		/**
			\fn static void setDescriptionColor(const QColor &col)
			Ustawia kolor wyświetlanego opisu.
			\param col kolor
		**/
		static void setDescriptionColor(const QColor &col);

	private:
		QPixmap pm;
		bool bold;
		static QFontMetrics *descriptionFontMetrics;

		static UinType myUIN;
		static bool ShowDesc;
		static bool AlignUserboxIconsTop;
		static bool ShowMultilineDesc;
		static bool MultiColumn;
		static int  MultiColumnWidth;
		static QColor descColor;

		mutable QString buf_text;
		mutable int buf_width;
		mutable QStringList buf_out;
		mutable int buf_height;
};

/**
	Klasa reprezentująca menu podręczne listy kontaktów - czyli to, które dostajemy klikając
	na którymkolwiek kontakcie prawym przyciskiem myszy.
	\class UserBoxMenu
	\brief Menu podręczne listy kontaktów.
**/
class UserBoxMenu : public QPopupMenu
{
	Q_OBJECT

	private:
		QValueList<QPair<QString, QString> > iconNames;
	private slots:
		void restoreLook();

	public:
		/**
			\fn UserBoxMenu(QWidget *parent=0, const char* name=0)
			Standardowy konstruktor.
			\param parent wskaźnik na obiekt kontrolki-rodzica.
			\param name nazwa kontrolki.
		**/
		UserBoxMenu(QWidget *parent=0, const char* name=0);

		/**
			\fn int getItem(const QString &caption) const
			Zwraca unikalną liczbę identyfikującą element menu z podanym napisem.
			\param caption napis elementu.
		**/
		int getItem(const QString &caption) const;

	public slots:
		/**
			\fn void show(QListBoxItem *item)
			Pokazuje menu dla podanego kontaktu.
			\param item element wyświetlanej listy kontaktów reprezentujący rządany kontakt.
		**/
		void show(QListBoxItem *item);

		/**
			\fn int addItem(const QString &text, const QObject* receiver, const char* member, const QKeySequence accel= 0, int id= -1)
			Dodaje nowy element do menu.
			\param text napis dla nowego elementu.
			\param receiver obiekt odbierający sygnał wybrania elementu z menu.
			\param member SLOT obiektu \a receiver który zostanie wykonany po wybraniu elementu z menu.
			\param accel skrót klawiaturowy dla tego elementu. Domyślnie brak.
			\param id Unikatowa liczba identyfikująca nowy element. Domyślnie pierwsza wolna.
		**/
		int addItem(const QString &text, const QObject* receiver, const char* member, const QKeySequence accel= 0, int id= -1);

		/**
			\fn int addItem(const QString &iconname, const QString &text, const QObject* receiver, const char* member, const QKeySequence accel= 0, int id= -1)
			Dodaje nowy element do menu.
			\param iconname nazwa ikony z zestawu lub sciezka do pliku
			\param text napis dla nowego elementu.
			\param receiver obiekt odbierający sygnał wybrania elementu z menu.
			\param member SLOT obiektu \a receiver który zostanie wykonany po wybraniu elementu z menu.
			\param accel skrót klawiaturowy dla tego elementu. Domyślnie brak.
			\param id Unikatowa liczba identyfikująca nowy element. Domyślnie pierwsza wolna.
		**/
		int addItem(const QString &iconname, const QString &text, const QObject* receiver, const char* member, const QKeySequence accel= 0, int id= -1);

		/**
			\fn int addItemAtPos(int index,const QString &iconname, const QString &text, const QObject* receiver, const char* member, const QKeySequence accel= 0, int id= -1)
			Dodaje nowy element do menu.
			\param index pozycja (licząc od 0) na której znaleźć ma się nowy element.
			\param iconname nazwa ikony z zestawu lub sciezka do pliku
			\param text napis dla nowego elementu.
			\param receiver obiekt odbierający sygnał wybrania elementu z menu.
			\param member SLOT obiektu \a receiver który zostanie wykonany po wybraniu elementu z menu.
			\param accel skrót klawiaturowy dla tego elementu. Domyślnie brak.
			\param id Unikatowa liczba identyfikująca nowy element. Domyślnie pierwsza wolna.
		**/
		int addItemAtPos(int index,const QString &iconname, const QString &text, const QObject* receiver, const char* member, const QKeySequence accel= 0, int id= -1);

		/**
			\fn void refreshIcons()
			Przeładowuje wszystkie ikony w tym menu dodane przy pomocy powyższych funkcji
		**/
		void refreshIcons();
	signals:
		/**
			\fn void popup()
			Sygnał emitowany, gdy menu jest wywoływane.
		**/
		void popup();
};


/**
	Klasa reprezentująca listę kontaktów wraz z ikonkami stanów.
	\class UserBox
	\brief Wyświetlana lista kontaktów.
**/
class UserBox : public QListBox
{
	Q_OBJECT

	public:
		/**
			\fn UserBox(UserGroup *group = userlist, QWidget* parent = 0, const char* name = 0, WFlags f = 0)
			Standardowy konstruktor tworzący listę kontaktów.
			\param group grupa kontaktów, która będzie wyświetlana
			\param parent rodzic kontrolki. Domyślnie 0.
			\param name nazwa kontrolki. Domyślnie 0.
			\param f flagi kontrolki. Domyślnie 0.
		**/
		UserBox(UserGroup *group = userlist, QWidget* parent = 0, const char* name = 0, WFlags f = 0);

		virtual ~UserBox();

		/**
			\var static UserBoxMenu *userboxmenu
			Wskaźnik do menu kontekstowego listy kontaktów.
		**/
		static UserBoxMenu *userboxmenu;

		static void setColorsOrBackgrounds();

		/**
			\fn static void initModule()
			Inicjalizuje zmienne niezbędne do działania UserBox. Funkcja ta jest
			wywoływana przy starcie Kadu przez rdzeń Kadu.
		**/
		static void initModule();

		static void closeModule();

		/**
			\fn UserListElements getSelectedUsers() const
			Funkcja zwraca listę zaznaczonych uzytkowników.
			\return UserListElements z zaznaczonymi użytkownikami.
		**/
		UserListElements selectedUsers() const;

		/**
			\fn const UserGroup *visibleUsers() const
			zwraca listę kontaktów, które są obecnie na liście
			\return grupa kontaktów
		**/
		const UserGroup *visibleUsers() const;

		/**
			\fn QValueList<UserGroup *> filters() const
			Zwraca listę filtrów "pozytywnych"
			\return lista filtrów
		**/
		QValueList<UserGroup *> filters() const;

		/**
			\fn QValueList<UserGroup *> negativeFilters() const
			Zwraca listę filtrów "negatywnych"
			\return lista filtrów
		**/
		QValueList<UserGroup *> negativeFilters() const;

		/**
			\fn bool currentUserExists() const
			\return informacja o istnieniu bieżącego kontaktu
		**/
		bool currentUserExists() const;

		/**
			\fn UserListElement currentUser() const
			\attention {przed wywołaniem tej metody należy sprawdzić, czy istnieje bieżący kontakt!}
			\return bieżący kontakt
			\see UserBox::currentUserExists()
		**/
		UserListElement currentUser() const;

		/**
			\fn static UserBox* activeUserBox()
			Funkcja znajdujaca aktywny UserBox.
			\return wskaźnik do aktywnego UserBox'a, jeśli taki nie istnieje zwracana jest wartość NULL.
		**/
		static UserBox* activeUserBox();

		class CmpFuncDesc
		{
			public:
 				CmpFuncDesc(QString i, QString d, int (*f)(const UserListElement &, const UserListElement &))
 					: id(i), description(d), func(f) {}
 				CmpFuncDesc() : id(), description(), func(0){}

				QString id;
				QString description;
				int (*func)(const UserListElement &, const UserListElement &);
		};

		/**
			\fn QValueList<UserBox::CmpFuncDesc> compareFunctions() const
			\return lista obiektów opisujących funkcje porównujące
		**/
 		QValueList<UserBox::CmpFuncDesc> compareFunctions() const;

		/**
			\fn void addCompareFunction(const QString &id, const QString &trDescription, int (*cmp)(const UserListElement &, const UserListElement &))
			Dodaje funkcję porównującą do bieżącego UserBoksa.
			Funkcja cmp powinna zwracać wartość:
				< 0 gdy pierwszy kontakt powinien znaleźć się przed drugim
				= 0 gdy kolejność kontaktów nie ma znaczenia
				> 0 gdy pierwszy kontakt powinien znaleźć się za drugim
			\param id krótki identyfikator funkcji
			\param trDescription pzetłumaczony opis funkcji (w zamierzeniu do wyświetlenia w konfiguracji)
			\param cmp funkcja porównująca
		**/
		void addCompareFunction(const QString &id, const QString &trDescription,
					int (*cmp)(const UserListElement &, const UserListElement &));

		/**
			\fn static void refreshAll()
			Odświeża wszystkie UserBoksy.
		**/
		static void refreshAll();

		/**
			\fn static void refreshAllLater()
			Odświeża wszystkie UserBoksy, ale dopiero po powrocie do pętli zdarzeń Qt.
		**/
		static void refreshAllLater();

		static const QValueList<UserBox *> &userboxes() {return UserBoxes;}

		static CreateNotifier createNotifier;
	public slots:

		/**
			\fn void applyFilter(UserGroup *group)
			Nakłada na bieżący UserBox filtr "pozytywny" - wyświetlane będą
			tylko te kontakty, które należą do group.
			\param group filtr
		**/
		void applyFilter(UserGroup *group);

		/**
			\fn void removeFilter(UserGroup *group)
			Usuwa z bieżącego UserBoksa wskazany filtr "pozytywny".
			\param group filtr
		**/
		void removeFilter(UserGroup *group);

		/**
			\fn void applyNegativeFilter(UserGroup *group)
			Nakłada na bieżący UserBox filtr "negatywny" - wyświetlane będą
			tylko te kontakty, które nie należą do group.
			\param group filtr
		**/
		void applyNegativeFilter(UserGroup *group);

		/**
			\fn void removeNegativeFilter(UserGroup *group)
			Usuwa z bieżącego UserBoksa wskazany filtr "negatywny".
			\param group filtr
		**/
		void removeNegativeFilter(UserGroup *group);

		/**
			\fn void removeCompareFunction(const QString &id)
			Usuwa funkcję porównującą o identyfikatorze id.
			\param id identyfikator funkcji porównującej
		**/
		void removeCompareFunction(const QString &id);

		/**
			\fn void moveUpCompareFunction(const QString &id)
			Przesuwa funkcję porównującą o identyfikatorze id wyżej w kolejności sprawdzania.
			\param id identyfikator funkcji porównującej
		**/
		bool moveUpCompareFunction(const QString &id);

		/**
			\fn void moveDownCompareFunction(const QString &id)
			Przesuwa funkcję porównującą o identyfikatorze id niżej w kolejności sprawdzania.
			\param id identyfikator funkcji porównującej
		**/
 		bool moveDownCompareFunction(const QString &id);

		/**
			\fn void refresh()
			Odświeża bieżący UserBox. Nie należy tej funkcji nadużywać,
			bo większość (90%) sytuacji jest wykrywanych przez samą klasę.
		**/
		void refresh();

		void refreshLater();
	signals:
		/**
			\fn void doubleClicked(UserListElement user)
			Sygnał jest emitowany, gdy na którymś z kontaktów kliknięto dwukrotnie.
			\param user kontakt, na którym kilknięto dwukrotnie
			\warning Używaj tego sygnału zamiast QListBox::doubleClicked(QListBoxItem *) !!!
			Tamten ze względu na odświeżanie listy w jednym ze slotów podłączonych
			do tego sygnału czasami przekazuje wskaźnik do elementu, który już NIE ISTNIEJE.
		**/
		void doubleClicked(UserListElement user);

		/**
			\fn void returnPressed(UserListElement user)
			Sygnał jest emitowany, gdy wciśnięto klawisz enter na wybranym kontakcie.
			\param user kontakt, na którym wciśnięto enter
			\warning Używaj tego sygnału zamiast QListBox::returnPressed(QListBoxItem *) !!!
			Tamten ze względu na odświeżanie listy w jednym ze slotów podłączonych
			do tego sygnału czasami przekazuje wskaźnik do elementu, który już NIE ISTNIEJE.
		**/
		void returnPressed(UserListElement user);

		/**
			\fn void currentChanged(UserListElement user)
			Sygnał jest emitowany, gdy zmienił się bieżący kontakt.
			\attention {raczej należu używać tego sygnału zamiast QListBox::currentChaned(QListBoxItem *)}
			\param user obecnie bieżący kontakt
		**/
		void currentChanged(UserListElement user);

		void changeToolTip(const QPoint &point, UserListElement user, bool show);

	private slots:
		void doubleClickedSlot(QListBoxItem *item);
		void returnPressedSlot(QListBoxItem *item);
		void currentChangedSlot(QListBoxItem *item);

		void userAddedToVisible(UserListElement elem, bool massively, bool last);
		void userRemovedFromVisible(UserListElement elem, bool massively, bool last);

		void userAddedToGroup(UserListElement elem, bool massively, bool last);
		void userRemovedFromGroup(UserListElement elem, bool massively, bool last);

		void userDataChanged(UserListElement elem, QString name, QVariant oldValue,
							QVariant currentValue, bool massively, bool last);
		void protocolUserDataChanged(QString protocolName, UserListElement elem,
							QString name, QVariant oldValue, QVariant currentValue,
							bool massively, bool last);
		void removingProtocol(UserListElement elem, QString protocolName, bool massively, bool last);

		void tipTimeout();
		void restartTip(const QPoint &p);
		void hideTip();
		void resetVerticalPosition();
		void rememberVerticalPosition();

		void messageFromUserAdded(UserListElement elem);
		void messageFromUserDeleted(UserListElement elem);

	private:
		static QValueList<UserBox*> UserBoxes;
		static UserBoxSlots *userboxslots;

		UserGroup *VisibleUsers;
		QValueList<UserGroup *> Filters;
		QValueList<UserGroup *> NegativeFilters;
		std::vector<UserListElement> sortHelper;
		std::vector<UserListElement> toRemove;
		QMap<const UserGroup *, UserListElements> AppendProxy;
		QMap<const UserGroup *, UserListElements> RemoveProxy;

		ULEComparer *comparer;
		void sort();
		QTimer refreshTimer;

		UserListElement lastMouseStopUser;
		static UserListElement nullElement;
		QPoint lastMouseStop;
		bool tipAlive;
		QTimer tipTimer;

		QTimer verticalPositionTimer;
		int lastVerticalPosition;

		friend class UserBoxSlots;

	protected:
		virtual void wheelEvent(QWheelEvent *e);
		virtual void enterEvent(QEvent *);
		virtual void leaveEvent(QEvent *);

		/**
			\fn virtual void mousePressEvent(QMouseEvent *e)
			Wciśnięto któryś z przycisków myszki na liście kontaktów.
			\param e wskaźnik obiektu opisującego to zdarzenie.
		**/
		virtual void mousePressEvent(QMouseEvent *e);

		virtual void mouseReleaseEvent(QMouseEvent *e);

		/**
			\fn virtual void mouseMoveEvent(QMouseEvent* e)
			Poruszono myszką nad listą kontaktów.
			\param e wskaźnik obiektu opisującego to zdarzenie.
		**/
		virtual void mouseMoveEvent(QMouseEvent* e);

		/**
			\fn virtual void keyPressEvent(QKeyEvent *e)
			Wciśnięto któryś z klawiszów w aktywnej liście kontaktów.
			\param e wskaźnik obiektu opisującego to zdarzenie.
		**/
		virtual void keyPressEvent(QKeyEvent *e);

		/**
			\fn virtual void resizeEvent(QResizeEvent *)
			Lista kontaktów zmieniła swój rozmiar.
			\param e wskaźnik obiektu opisującego to zdarzenie.
		**/
		virtual void resizeEvent(QResizeEvent *);
};

/**
	Klasa ta pozwala na podgląd wprowadzanych zmian konfiguracyjnych wobec UserBox
	w oknie konfiguracji, zanim zaaplikuje się je do Kadu.
	\class UserBoxSlots
	\brief Obsługa UserBox w konfiguracji.
**/
class UserBoxSlots : public QObject
{
	Q_OBJECT
	public slots:
		/**
			\fn void onCreateTabLook()
			Obsługuje sekcję UserBox podczas otwierania okna konfiguracji.
		**/
		void onCreateTabLook();

		/**
			\fn void onDestroyConfigDialog()
			Obsługuje sekcję UserBox podczas zamykania okna konfiguracji.
		**/
		void onApplyTabLook();

		/**
			\fn void chooseColor(const char *name, const QColor& color)
			Odświeża podgląd wybranego koloru.
			\param name nazwa elementu, dla którego wybrano kolor.
				\arg \c userbox_bg_color oznacza kolor tła.
				\arg \c userbox_font_color oznacz kolor czcionki.
			\param color wybrany kolor.
		**/
		void chooseColor(const char *name, const QColor& color);

		/**
			\fn void chooseFont(const char *name, const QFont& font)
			Odświeża podgląd wybranej czcionki.
			\param name nazwa elementu, dla którego wybrano czcionkę.
				\arg \c userbox_font_box oznacza ogólną czcionkę listy kontaktów.
			\param font wybrana czcionka.
		**/
		void chooseFont(const char *name, const QFont& font);

		/**
			\fn void onMultiColumnUserbox(bool toggled)
			Włącza bądź wyłącza kontrolkę szerokości kolumny listy kontaktów, w zależności od podanego argumentu.
			\param togglet wartość logiczna informująca, czy kontrolka ma być włączona czy wyłączona.
		**/
		void onMultiColumnUserbox(bool toggled);

		/**
			\fn void updatePreview()
			Odświeża podgląd wszystkich elementów UserBox'a.
		**/
		void updatePreview();

		void chooseBackgroundFile();
		void userboxBackgroundMove(bool toggled);
	private slots:
		void backgroundFileChanged(const QString &text);
};

/**
	\fn int compareAltNick(const UserListElement &u1, const UserListElement &u2)
	Funkcja porównująca AltNicki metodą QString::localeAwareCompare()
**/
int compareAltNick(const UserListElement &u1, const UserListElement &u2);

/**
	\fn int compareAltNickCaseInsensitive(const UserListElement &u1, const UserListElement &u2)
	Funkcja porównująca AltNicki metodą QString::localeAwareCompare(), wcześniej robiąca lower(),
	a więc wolniejsza od compareAltNick
**/
int compareAltNickCaseInsensitive(const UserListElement &u1, const UserListElement &u2);

/**
	\fn int compareStatus(const UserListElement &u1, const UserListElement &u2)
	Funkcja porównująca statusy w protokole Gadu-Gadu. Uważa status "dostępny" i "zajęty" za równoważne.
**/
int compareStatus(const UserListElement &u1, const UserListElement &u2);

#endif
