#ifndef KADU_USERGROUP_H
#define KADU_USERGROUP_H

#include <qmap.h>
#include <qobject.h>
#include <qshared.h>
#include <qstring.h>
#include <qvaluelist.h>
#include <qvariant.h>

#include "userlistelement.h"

class UserGroupData;
class UserListElements;

enum BehaviourForAnonymous {TrueForAnonymous, FalseForAnonymous};

/**
	\class UserGroup
	Klasa odpowiedzialna za przechowywanie grup kontaktów. Daje możliwość
	bardzo szybkiego wyszukiwania oraz informowania o zdarzeniach związanych
	z kontaktami (zmiana statusu, dodanie, usunięcie, zmiana danych).
	Patrz: UserList, GroupsManager, UsersWithDescription, OnlineUsers,
	       BlockedUsers, BlockingUsers, AnonymousUsers
	Jeżeli nie potrzebujesz informacji o zdarzeniach, lepiej użyć jest klasy
	UserListElements.

	Parametr "last" w sygnałach dostępnych w tej klasie, powinien być używany
	tylko gdy mamy absolutną pewność, że zmiana danych nastąpiła dla wszystkich
	kontaktów z tej grupy (i ani jednego więcej). Dlatego też bezpieczne jest
	używanie tego parametru tylko w slotach podłączonych do klasy UserList.
	Jeżeli mimo to potrzebujemy skorzystać z parametru massively w celu wykonania
	jakiejś czynności dopiero po zakończeniu zmian, możemy posłużyć się QTimerem
	z czasem 0 (czyli po oddaniu sterowania pętli zdarzeń Qt) i jednokrotnym
	uruchomieniem. Przykład:
	<code>
	class MojaKlasa
	{
		...
		QTimer refreshTimer;
		...
	};
	MojaKlasa::MojaKlasa()
	{
		...
		connect(&refreshTimer, SIGNAL(timeout()), this, SLOT(refresh()));
		...
	}
	inline void MojaKlasa::refreshLater()
	{
		refreshTimer.start(0, true);
	}

	void MojaKlasa::refresh()
	{
		...//bardzo kosztowne odświeżanie
	}

	void MojaKlasa::userAdded(UserListElement elem, bool massively, bool last)
	{
		if (massively)
		{
			akcja1; //np jakieś buforowanie
			...
			refreshLater();
		}
		else
		{
			akcja2;
			...
			refresh();
		}
	}
	</code>
**/
class UserGroup : public QObject
{
	Q_OBJECT

	public:
		typedef QValueListConstIterator<UserListElement> const_iterator;
		typedef QValueListIterator<UserListElement> iterator;
		typedef size_t size_type;

		/**
			\fn UserGroup(int size, const char *name = 0)
			\param size spodziewana ilość elementów * 2
			\param name nazwa grupy
		**/
		UserGroup(int size, const char *name = 0);

		/**
			\fn UserGroup(const QValueList<UserListElement> &group, const char *name = 0)
			\param group
			\param name nazwa grupy
		**/
		UserGroup(const QValueList<UserListElement> &group, const char *name = 0);

		/**
			\fn virtual ~UserGroup()
			Zwalnia pamięć związaną z grupą
		**/
		virtual ~UserGroup();

		/**
			\fn bool equals(UserListElements users) const
			\param users
			Złożoność O(users.count()).
		**/
		bool equals(UserListElements users) const;

		/**
			\fn bool equals(UserListElements users) const
			\param users
			Złożoność O(users.count()).
		**/
		bool equals(const UserGroup *group) const;

		/**
			\fn UserListElement byID(const QString &protocolName, const QString &id)
			\param protocolName identyfikator protokołu
			\param id identyfikator kontaktu
			Zwraca element listy kontaktów identyfikowany przez nazwę
			protokołu i identyfikator w tymże protokole.
			Jeżeli nie znajdzie się taki kontakt, to tworzony jest nowy
			anonimowy kontakt i dodawany do listy.
			Złożoność O(count()), ale w przyszłości będzie optymalizowana.
		**/
		UserListElement byID(const QString &protocolName, const QString &id);

		/**
			\fn UserListElement byAltNick(const QString &altnick)
			\param altnick wyświetlany pseudonim wg. którego następuje wyszukiwanie.
			\return obiekt reprezentujący kontakt o podanym pseudonimie wyświetlanym.
			Wyszukuje kontakt po wyświetlanym pseudonimie.
			Jeżeli nie znajdzie się taki kontakt, to tworzony jest nowy
			anonimowy kontakt i dodawany do listy.
			Złożoność O(count()), ale w przyszłości będzie optymalizowana do O(1).
		**/
		UserListElement byAltNick(const QString &altnick);

		/**
			\fn bool contains(const QString &protocolName, const QString &id, BehaviourForAnonymous beh = TrueForAnonymous) const
			\param protocolName identyfikator protokołu
			\param id identyfikator kontaktu
			\param beh sposób zachowania dla anonimów
			Sprawdza czy grupa zawiera kontakt o danym identyfikatorze we wskazanym protokole.
			W przypadku kontaktów anonimowych zachowanie tej funkcji określa parametr beh. Gdy jest utawiony na:
				TrueForAnonymous (domyślnie), to zwraca prawdę
				FalseForAnonymous, to zwraca fałsz
			Złożoność O(count()), ale w przyszłości będzie optymalizowana.
		**/
		bool contains(const QString &protocolName, const QString &id, BehaviourForAnonymous beh = TrueForAnonymous) const;

		/**
			\fn bool contains(UserListElement elem, BehaviourForAnonymous beh = TrueForAnonymous) const
			\param elem sprawdzany kontakt
			\param beh sposób zachowania dla anonimów
			Sprawdza czy grupa zawiera wskazany kontakt.
			W przypadku kontaktów anonimowych zachowanie tej funkcji określa parametr beh. Gdy jest utawiony na:
				TrueForAnonymous (domyślnie), to  zwraca prawdę
				FalseForAnonymous, to zwraca fałsz
			Złożoność O(1).
		**/
		bool contains(UserListElement elem, BehaviourForAnonymous beh = TrueForAnonymous) const;

		/**
			\fn bool containsAltNick(const QString &altnick, BehaviourForAnonymous beh = TrueForAnonymous) const
			\param altnick wyświetlany pseudonim
			\param beh sposób zachowania dla anonimów
			Sprawdza czy grupa zawiera kontakt o danym pseudonimie wyświetlanym.
			W przypadku kontaktów anonimowych zachowanie tej funkcji określa parametr beh. Gdy jest utawiony na:
				TrueForAnonymous (domyślnie), to  zwraca prawdę
				FalseForAnonymous, to zwraca fałsz
			Złożoność O(count()), ale w przyszłości będzie optymalizowana do O(1).
		**/
		bool containsAltNick(const QString &altnick, BehaviourForAnonymous beh = TrueForAnonymous) const;

		/**
			\fn size_type count() const
			Zwraca ilość kontaktów.
		**/
		size_type count() const;

		/**
			\fn const_iterator constBegin () const
			Zwraca iterator początkowy.
		**/
		const_iterator constBegin () const;

		/**
			\fn const_iterator constEnd () const
			Zwraca iterator końcowy.
		**/
		const_iterator constEnd () const;

		/**
			\fn const_iterator begin () const
			Zwraca iterator początkowy.
		**/
		iterator begin () const;

		/**
			\fn const_iterator end () const
			Zwraca iterator końcowy.
		**/
		iterator end () const;

		/**
			\fn UserListElements toUserListElements() const
			Zwraca listę kontaktów w postaci obiektu klasy UserListElements
		**/
		UserListElements toUserListElements() const;

		/**
			\fn void resize(int size)
			\param size rozmiar bufora * 2
			Zmienia wewnętrzny rozmiar buforów dla klas słownikowych (Q*Dict).
		**/
		void resize(int size);

		QStringList altNicks() const;
		
		void clear();
	public slots:
		/**
			\fn void addUser(UserListElement ule, bool massively = false, bool last = false)
			\param ule obiekt reprezentujący kontakt, który chcemy dodać.
			\param massively true, gdy jest to cześć większego dodawania
			\param last true, gdy massively == true i jest to ostatnie dodanie
			Dodaje do listy podany kontakt.
		**/
		void addUser(UserListElement ule, bool massively = false, bool last = false);

		/**
			\fn void removeUser(UserListElement ule, bool massively = false, bool last = false)
			\param ule obiekt reprezentujący kontakt, który chcemy usunąć.
			\param massively true, gdy jest to cześć większego usuwania
			\param last true, gdy massively == true i jest to ostatnie usunięcie
			Usuwa podany kontakt z listy.
		**/
		void removeUser(UserListElement ule, bool massively = false, bool last = false);

		/**
			\fn UserListElement addAnonymous(const QString &protocolName, const QString &id, bool massively = false, bool last = false)
			\param protocolName identyfikator protokołu
			\param id identyfikator kontaktu
			\param massively true, gdy jest to cześć większego dodawania
			\param last true, gdy massively == true i jest to ostatnie dodanie
			Dodaje do listy anonimowy kontakt i zwraca go.
		**/
		UserListElement addAnonymous(const QString &protocolName, const QString &id, bool massively = false, bool last = false);

		/**
			\fn void addUsers(const UserGroup *group)
			Dodaje do listy wszystkie kontakty należące do grupy group
			\param group grupa kontaktów
		**/
		void addUsers(const UserGroup *group);

		/**
			\fn void addUsers(QValueList<UserListElement> users)
			Dodaje do listy wszystkie kontakty należące do listy users
			\param users kontakty
		**/
		void addUsers(QValueList<UserListElement> users);

		/**
			\fn void removeUsers(const UserGroup *group)
			Usuwa z listy wszystkie kontakty należące do grupy group
			\param group grupa kontaktów
		**/
		void removeUsers(const UserGroup *group);

		/**
			\fn void removeUsers(QValueList<UserListElement> users)
			Usuwa z listy wszystkie kontakty należące do listy users
			\param users kontakty
		**/
		void removeUsers(QValueList<UserListElement> users);

	signals:
		/**
			\fn void modified()
			Sygnał generowany po zakończeniu zmian w liście	kontaktów.
			\todo sprawdzić czy zawsze dzieje się to po zakończeniu zmian
		**/
		void modified();

		/**
			\fn void userDataChanged(UserListElement elem, QString name, QVariant oldValue, QVariant currentValue, bool massively, bool last)
			\param elem kontakt, którego dane się zmieniły
			\param name nazwa własności
			\param oldValue stara wartość
			\param currentValue nowa (bieżąca) wartość
			\param massively true, gdy jest to cześć większych zmian
			\param last true, gdy massively == true i jest to ostatnia zmiana
			Sygnał generowany gdy dla kontaktu elem zmieni się własność o nazwie name.
			Przekazuje także starą i nową wartość.
		**/
		void userDataChanged(UserListElement elem, QString name, QVariant oldValue,
							QVariant currentValue, bool massively, bool last);

		void usersDataChanged(QString name);

		/**
			\fn void protocolUserDataChanged(QString protocolName, UserListElement elem, QString name, QVariant oldValue, QVariant currentValue, bool massively, bool last)
			\param protocolName identyfikator protokołu
			\param elem kontakt, którego dane się zmieniły
			\param name nazwa własności
			\param oldValue stara wartość
			\param currentValue nowa (bieżąca) wartość
			\param massively true, gdy jest to cześć większych zmian
			\param last true, gdy massively == true i jest to ostatnia zmiana
			Sygnał generowany gdy dla kontaktu elem zmieni się własność związana z protokołem
			protocolName o nazwie name. Przekazuje także starą i nową wartość.
		**/
		void protocolUserDataChanged(QString protocolName, UserListElement elem,
							QString name, QVariant oldValue, QVariant currentValue,
							bool massively, bool last);

		void protocolUsersDataChanged(QString protocolName, QString name);

		/**
			\fn void userAdded(UserListElement elem, bool massively, bool last)
			\param elem dodany kontakt
			\param massively czy jest to fragment większych zmian
			\param last jeżeli massively == true, to last == true dla ostatniego kontaktu
			Sygnał generowany po dodaniu kontaktu elem do listy
		**/
		void userAdded(UserListElement elem, bool massively, bool last);

		/**
			\fn void addingUser(UserListElement elem, bool massively, bool last)
			\param elem dodany kontakt
			\param massively czy jest to fragment większych zmian
			\param last jeżeli massively == true, to last == true dla ostatniego kontaktu
			Sygnał generowany przed dodaniem kontaktu elem do listy
		**/
		void addingUser(UserListElement elem, bool massively, bool last);

		void usersAdded();

		/**
			\fn void removingUser(UserListElement elem, bool massively, bool last)
			\param elem usuwany kontakt
			\param massively czy jest to fragment większych zmian
			\param last jeżeli massively == true, to last == true dla ostatniego kontaktu
			Sygnał generowany przed usunięciem kontaktu z listy.
		**/
		void removingUser(UserListElement elem, bool massively, bool last);

		/**
			\fn void userRemoved(UserListElement elem, bool massively, bool last)
			\param elem usuwany kontakt
			\param massively czy jest to fragment większych zmian
			\param last jeżeli massively == true, to last == true dla ostatniego kontaktu
			Sygnał generowany po usunięciu kontaktu z listy.
		**/
		void userRemoved(UserListElement elem, bool massively, bool last);

		void usersRemoved();

		/**
			\fn void protocolAdded(UserListElement elem, QString protocolName, bool massively, bool last)
			\param elem kontakt, dla którego dodany został protokół
			\param protocolName identyfikator protokołu
			\param massively czy jest to fragment większych zmian
			\param last jeżeli massively == true, to last == true dla ostatniego kontaktu
			Sygnał generowany po dodaniu protokołu protocolName kontaktowi elem.
		**/
		void protocolAdded(UserListElement elem, QString protocolName, bool massively, bool last);

		/**
			\fn void removingProtocol(UserListElement elem, QString protocolName, bool massively, bool last)
			\param elem kontakt, dla którego usunięty został protokół
			\param protocolName identyfikator protokołu
			\param massively czy jest to fragment większych zmian
			\param last jeżeli massively == true, to last == true dla ostatniego kontaktu
			Sygnał generowany przed usunięciem protokołu protocolName kontaktu elem.
		**/
		void removingProtocol(UserListElement elem, QString protocolName, bool massively, bool last);

		/**
			\fn void statusChanged(UserListElement elem, QString protocolName, const UserStatus &oldStatus, bool massively, bool last)
			\param elem kontakt, dla którego zmienił się status
			\param protocolName identyfikator protokołu
			\param oldStatus poprzedni status
			\param massively czy jest to fragment większych zmian
			\param last jeżeli massively == true, to last == true dla ostatniego kontaktu
		**/
		void statusChanged(UserListElement elem, QString protocolName,
							const UserStatus &oldStatus, bool massively, bool last);

		void usersStatusChanged(QString protocolName);

	protected:
		UserGroupData *d;
		friend class UserListElement;
		friend class ULEPrivate;

		/**
			\fn UserListElement byKey(UserListKey key)
			\param key klucz
			Zwraca element listy kontaktów identyfikowany przez klucz.
		**/
		UserListElement byKey(UserListKey key);

	private:
		/* standardowy konstruktor kopiujący zepsułby obiekt źródłowy */
		UserGroup(const UserGroup &) : QObject(0,0), d(0) {}

		/* standardowy operator kopiowania zepsułby obiekt źródłowy */
 		virtual UserGroup &operator = (const UserGroup &) {return *this;}

		/* nie da się porównywać obiektów tej klasy */
		bool operator==(const UserGroup &) const {return false;}

};

/**
	\class UserListElements
	Prosta lista użytkowników z kilkoma ułatwiaczami.
**/
class UserListElements : public QValueList<UserListElement>
{
	public:
		/**
			\fn UserListElements(UserListElement)
			Konstruktor dodający od razu wskazany kontakt
		**/
		UserListElements(UserListElement);

		/**
			\fn UserListElements(const UserListElements &)
			Konstruktor kopiujący
		**/
		UserListElements(const UserListElements &);

		/**
			\fn UserListElements(const QValueList<UserListElement> &)
			Konstruktor inicjujący się wskazaną listą
		**/
		UserListElements(const QValueList<UserListElement> &);

		/**
			\fn UserListElements()
			Standardowy konstruktor.
		**/
		UserListElements();

		/**
			\fn bool equals(const UserListElements &elems) const
			Sprawdza czy bieżąca lista zaiwera te same elemnty co wskazana.
			\attention {Złożoność O(count()*elems.count()).}
		**/
		bool equals(const UserListElements &elems) const;

		/**
			\fn bool equals(const UserGroup *group) const
			Sprawdza czy bieżąca lista zaiwera te same elemnty co wskazana.
			\attention {Złożoność O(count()).}
		**/
		bool equals(const UserGroup *group) const;

		/**
			\fn void sort()
			Sortuje listę w sposób jednoznaczny.
		**/
		void sort();

		/**
			\fn bool contains(QString protocol, QString id) const
			Zwraca informację o tym czy lista zawiera kontakt o wskazanym protokole i identyfiaktorze.
			(Złożoność O(n))
		**/
		bool contains(QString protocol, QString id) const;

		/**
			\fn bool contains(UserListElement e) const
			Zwraca informację o tym czy lista zawiera wskazany kontakt.
			(Złożoność O(n))
		**/
		bool contains(UserListElement e) const { return QValueList<UserListElement>::contains(e) > 0;}

		QStringList altNicks() const;
};

#endif
