#ifndef KADU_USERLIST_H
#define KADU_USERLIST_H

#include <qdns.h>
#include <qhostaddress.h>
#include <qobject.h>

#include "gadu.h"

class UserList;
class UserListElement;

/**
	Tłumaczy adres IP na nazwę domeny (DNS). Kiedy proces zostanie zakończony,
	emituje odpowiedni sygnał zawierający informację o nazwie domeny.
	\class DnsHandler
	\brief Klasa tłumacząca adres IP na nazwę domeny.
**/
class DnsHandler : public QObject
{
	Q_OBJECT

	public:
		/**
			\fn DnsHandler(const QHostAddress &addr);
			Konstruktor wywołujący zapytanie o domenę dla danego adresu IP.
			\param addr adres IP
		**/
		DnsHandler(const QHostAddress &addr);
		~DnsHandler();

		static int counter; /*!< licznik obiektów tej klasy */

	private:
		QDns DnsResolver; /*!< obiekt zajmujący się translacją adresu na nazwę domeny */

	private slots:
		/**
			\fn void resultsReady()
			Funkcja wywoływana, gdy proces tłumaczenia został zakończony.
		**/
		void resultsReady();

	signals:
		/**
			\fn void result(const QString &hostname)
			Sygnał emitowany, gdy proces tłumaczenia został zakończony.
			\param hostname nazwa domeny odpowiadająca adresowi IP o który pytano
		**/
		void result(const QString &hostname);
};

/**
	Typ opisujący stan powiadamiania.
	\enum NotifyType
	\brief Typ opisujący stan powiadamiania.
**/
enum NotifyType {
	GLOBAL = 0, /*!< powiadomienia zależne od globalnych ustawień */
	OFF = 1, /*!< powiadomienia wyłączone */
	OWN = 2 /*!< indywidualne ustawienie powiadamiania */
};

/**
	Klasa reprezentująca kontakt (użytkownika na liście kontaktów).
	\class UserListElement
	\brief Klasa reprezentująca kontakt.
**/
class UserListElement : public QObject
{
	Q_OBJECT
	private:
		QString Group; /*!< Grupa do której przynależy kontakt
						(jeśli więcej niż jedna, to oddzielone są przecinkami). */
		QString FirstName; /*!< Imię kontaktu. */
		QString LastName; /*!< Nazwisko kontaktu. */
		QString NickName; /*!< Pseudonim kontaktu. */
		QString AltNick; /*!< Pseudonim kontaktu, który jest wyświetlany na liście. */
		QString Mobile; /*!< Numer telefonu kontaktu. */
		QString Email; /*!< E-Mail kontaktu. */
		UinType Uin; /*!< UIN kontaktu. */
		UserStatus *Stat; /*!< Aktualny status kontaktu. */
		int MaxImageSize; /*!< Maksymalny rozmiar obrazka, jaką może przyjąć kontakt. */
		bool Anonymous; /*!< Informuje, czy kontakt jest anonimowy czy nie. */
		QHostAddress Ip; /*!< Adres IP kontaktu (jeśli wykryty). */
		QString DnsName; /*!< Nazwa domenu kontaktu (jeśli znaleziona). */
		short Port; /*!< Port kontaktu (jeśli wykryty). */
		int Version; /*!< Wersja protokołu używanego przez kontakt. */
		bool Blocking; /*!< Informuje czy blokujemy kontakt, czy nie. */
		bool OfflineTo; /*!< Informuje czy mamy włączony tryb "niedostępny dla kontaktu" dla tego kontaktu. */
		bool Notify; /*!< Informuje czy mamy włączone powiadamianie o kontakcie. */
		
		NotifyType AliveSound; /*!< Przechowuje informację o sposobie powiadamiania
								o zmianie statusu kontaku dźwiękiem. */
		QString OwnAliveSound; /*!< Jeśli sposób powiadamiania o zmianie statusu kontaktu ma wartość OWN,
								to ta zmienna przechowuje nazwę pliku dźwiękowego do odtworzenia. */

		NotifyType MessageSound; /*!< Przechowuje informację o sposobie powiadamiania
								o nowej wiadomości od kontaktu dźwiękiem. */
		QString OwnMessageSound; /*!< Jeśli sposób powiadamiania o nowej wiadomości od kontaktu ma wartość OWN,
								to ta zmienna przechowuje nazwę pliku dźwiękowego do odtworzenia. */
		
		QString HomePhone; /*!< Numer telefonu domowego kontaktu. */

		UserList *Parent; /*!< Wskaźnik do listy kontaktów, na której znajduje się ten kontakt. */
		friend class UserList;
	public slots:
		/**
			\fn void setGroup(const QString& group)
			Ustawia grupę (lub grupy, oddzielone przecinkami) dla kontaktu.
			\param group grupa (grupy) do której zostanie przydzielony kontakt.
		**/
		void setGroup(const QString& group);
		
		/**
			\fn void setFirstName(const QString &firstName)
			Ustawia imię dla kontaktu.
			\param firstName imię, które zostanie przydzielone kontaktowi.
		**/
		void setFirstName(const QString &firstName);
		
		/**
			\fn void setLastName(const QString &lastName)
			Ustawia nazwisko dla kontaktu.
			\param lastName nazwisko, które zostanie przydzielone kontaktowi.
		**/
		void setLastName(const QString &lastName);
		
		/**
			\fn void setNickName(const QString &nickName)
			Ustawia pseudonim dla kontaktu.
			\param nickName pseudonim, który zostanie przydzielony kontaktowi.
		**/
		void setNickName(const QString &nickName);
		
		/**
			\fn void setAltNick(const QString &altNick)
			Ustawia wyświetlany pseudonim dla kontaktu.
			\param altNick wyświetlany pseudonim, który zostanie przydzielony kontaktowi.
		**/
		void setAltNick(const QString &altNick);
		
		/**
			\fn void setMobile(const QString &mobile)
			Ustawia numer telefonu dla kontaktu.
			\param mobile numer telefonu, który zostanie przydzielony kontaktowi.
		**/
		void setMobile(const QString &mobile);
		
		/**
			\fn void setEmail(const QString &email)
			Ustawia adres E-Mail dla kontaktu.
			\param email adres, który zostanie przydzielony kontaktowi.
		**/
		void setEmail(const QString &email);
		
		/**
			\fn void setUin(const UinType &uin)
			Ustawia UIN dla kontaktu.
			\param uin numer UIN, który zostanie przydzielony kontaktowi.
		**/
		void setUin(const UinType &uin);
		
		/**
			\fn void setMaxImageSize(const int maxImageSize)
			Ustawia maksymalny rozmiar obrazka dla kontaktu.
			\param maxImageSize rozmiar obrazka w bajtach, który zostanie przydzielony kontaktowi.
		**/
		void setMaxImageSize(const int maxImageSize);
		
		/**
			\fn void setAnonymous(const bool anonymous)
			Ustawia stan anonimowości kontaktu.
			\param anonymous wartość logiczna informująca, że kontakt jest anonimowy, lub nie.
		**/
		void setAnonymous(const bool anonymous);
		
		/**
			\fn void setDnsName(const QString &dnsName)
			Ustawia nazwę domeny dla kontaktu.
			\param dnsName nazwa domenu, która zostanie przydzielona kontaktowi.
		**/
		void setDnsName(const QString &dnsName);
		
		/**
			\fn void setPort(short port)
			Ustawia numer portu dla kontaktu.
			\param port numer portu, który zostanie przydzielony kontaktowi.
		**/
		void setPort(short port);
		
		/**
			\fn void setVersion(const int version)
			Ustawia wersję protokołu dla kontaktu.
			\param version wersja protokołu, która zostanie przydzielona kontaktowi.
		**/
		void setVersion(const int version);
		
		/**
			\fn void setBlocking(const bool blocking)
			Ustawia stan blokowania kontaktu.
			\param blocking wartość logiczna informująca, czy kontakt ma być blokowany.
		**/
		void setBlocking(const bool blocking);
		
		/**
			\fn void setOfflineTo(const bool offlineTo)
			Ustawia stan niedostępności dla kontaktu.
			\param offlineTo wartość logiczna informująca, czy użytkownik ma być "niedostępny dla kontaktu".
		**/
		void setOfflineTo(const bool offlineTo);
		
		/**
			\fn void setNotify(const bool notify)
			Ustawia stan powiadamiania o kontakcie.
			\param notify wartość logiczna informująca, czy użytkownik ma być powiadamiany o kontakcie.
		**/
		void setNotify(const bool notify);

		/**
			\fn void setHomePhone(const QString &phone)
			Ustawia numer telefonu domowego dla kontaktu.
			\param phone numer telefonu, który zostanie przydzielony kontaktowi.
		**/
		void setHomePhone(const QString &phone);
		
		/**
			\fn void setAliveSound(NotifyType type, const QString &file="")
			Ustawia sposób powiadamiania dźwiękiem o zmianie statusu przez kontakt.
			\param type sposób powiadamiania.
			\arg \c GLOBAL powiadomienia zależne od globalnych ustawień.
			\arg \c OFF powiadomienia wyłączone.
			\arg \c OWN indywidualne ustawienie powiadamiania/

			\param file plik dźwiękowy, wymagany dla indywidualnego ustawienia powiadomień.
		**/
		void setAliveSound(NotifyType type, const QString &file="");
		
		/**
			\fn void setMessageSound(NotifyType type, const QString &file="")
			Ustawia sposób powiadamiania dźwiękiem o nowej wiadomości od kontaktu.
			\param type sposób powiadamiania.
			\arg \c GLOBAL powiadomienia zależne od globalnych ustawień.
			\arg \c OFF powiadomienia wyłączone.
			\arg \c OWN indywidualne ustawienie powiadamiania/

			\param file plik dźwiękowy, wymagany dla indywidualnego ustawienia powiadomień.
		**/
		void setMessageSound(NotifyType type, const QString &file="");

	public:
		/**
			\fn UserListElement(UserList* parent)
			Konstruktor dodający kontakt do wskazanej listy kontaktów.
			\param parent wskaźnik listy kontaktów, do której zostanie dodany kontakt.
		**/
		UserListElement(UserList* parent);

		/**
			\fn UserListElement(const UserListElement &copyMe)
			Konstruktor kopiujący.
		**/
		UserListElement(const UserListElement &copyMe);

		/**
			\fn UserListElement()
			Domyślny konstruktor tworzący kontakt o nie wypełnionych jeszcze danych.
		**/
		UserListElement();
		virtual ~UserListElement();

		/**
			\fn void refreshDnsName()
			Wywołuje zapytanie o nazwę domeny dla kontaktu oraz wypełnia odpowiednie pole kontaktu,
			gdy domena zostanie odnaleziona.
		**/
		void refreshDnsName();

		/**
			\fn void operator = (const UserListElement &copyMe)
			Operator kopiujący.
			\param copyMe obiekt klasy UserListElement który będzie skopiowany.
		**/
		void operator = (const UserListElement &copyMe);

		/**
			\fn const QString &group() const
			Zwraca grupę (lub wiele grup oddzielonych przecinkami) do której należy kontakt.
		**/
		const QString &group() const;
		
		/**
			\fn const QString &firstName() const
			Zwraca imię kontaktu.
		**/
		const QString &firstName() const;
		
		/**
			\fn const QString &lastName() const
			Zwraca nazwisko kontaktu.
		**/
		const QString &lastName() const;
		
		/**
			\fn const QString &nickName() const
			Zwraca pseudonim kontaktu.
		**/
		const QString &nickName() const;
		
		/**
			\fn const QString &altNick() const
			Zwraca wyświetlany pseudonim kontaktu.
		**/
		const QString &altNick() const;
		
		/**
			\fn const QString &mobile() const
			Zwraca numer telefonu kontaktu.
		**/
		const QString &mobile() const;
		
		/**
			\fn const QString &email() const
			Zwraca adres E-Mail kontaktu.
		**/
		const QString &email() const;
		
		/**
			\fn UinType uin() const
			Zwraca numer UIN kontaktu.
		**/
		UinType uin() const;
		
		/**
			\fn const QString &homePhone() const
			Zwraca numer telefonu domowego kontaktu.
		**/
		const QString &homePhone() const;
		
		/**
			\fn const QString &aliveSound(NotifyType &type) const
			Zwraca sposób powiadamiania dźwiękiem o zmianach statusu kontaktu.
		**/
		const QString &aliveSound(NotifyType &type) const;
		
		/**
			\fn const QString &messageSound(NotifyType &type) const
			Zwraca sposób powiadamiania dźwiękiem o wiadomościach od kontaktu.
		**/
		const QString &messageSound(NotifyType &type) const;

		/**
			\fn const UserStatus & status() const
			Zwraca aktualny status kontaktu.
		**/
		const UserStatus & status() const;
		
		/**
			\overload UserStatus & status()
		**/
		UserStatus & status();

		/**
			\fn int maxImageSize() const
			Zwraca maksymalny rozmiar obrazka dla kontaktu.
		**/
		int maxImageSize() const;
		
		/**
			\fn bool isAnonymous() const
			Zwraca informację, czy kontakt jest anonimowy czy nie.
		**/
		bool isAnonymous() const;

		/**
			\fn const QHostAddress & ip() const
			Zwraca adres IP kontaktu (jeśli wykryty).
		**/
		const QHostAddress & ip() const;
		
		/**
			\overload QHostAddress & ip()
		**/
		QHostAddress & ip();
		
		/**
			\fn const QString &dnsName() const
			Zwraca nazwę domeny kontaktu (jeśli znaleziona).
		**/
		const QString &dnsName() const;
		
		/**
			\fn short port() const
			Zwraca numer portu kontaktu (jeśli znaleziony).
		**/
		short port() const;
		
		/**
			\fn int version() const
			Zwraca wersję protokołu, jakiej używa kontakt.
		**/
		int version() const;
		
		/**
			\fn bool blocking() const
			Zwraca informację, czy kontakt jest blokowany czy nie.
		**/
		bool blocking() const;
		
		/**
			\fn bool offlineTo() const
			Zwraca informację, czy użytkownik jest "niedostępny dla kontaktu" czy nie.
		**/
		bool offlineTo() const;
		
		/**
			\fn bool notify() const
			Zwraca informację, czy użytkownik jest powiadamiany o kontakcie czy nie.
		**/
		bool notify() const;
};

/**
	Indeksami są altnicki (wyświetlane pseudonimy) konwertowane tak,
	aby wszystkie litery były małe.
	Dzięki temu nie mogą istnieć dwa kontakty
	o tych samych pseudonimach wyświetlanych nawet, jeśli
	różnią się wielkościami liter.
	Pole Altnick w UserListElement zawiera natomiast
	oryginalny pseudonim wyświetlany.
	
	\warning Używanie metod klasy QMap nie jest zalecane,
	trzeba wtedy pamiętać, że jako klucza używamy pseudonimów wyświetlanych
	odpowiednio skonwertowanych, że zmiany powinny generować
	odpowiednie sygnały, itd. Dużo lepiej używać specjalnie
	przygotowanych metod klasy UserList.
	\class UserList
	\brief Klasa reprezentująca listę kontaktów.
	
	TODO: Zgodnie z tym co powyżej z QMap powinniśmy dziedziczyć
	prywatnie albo niech to będzie pole wewnątrz klasy. W tej
	chwili nie chcę łamać api freeze w 0.4.0.
**/
class UserList : public QObject, public QMap<QString,UserListElement>
{
	Q_OBJECT

	protected:
		friend class UserListElement;

	public:
		/**
			\fn UserList()
			Konstruktor standardowy.
		**/
		UserList();

		/**
			\fn UserList(const UserList& source)
			Konstruktor kopiujący.
			\param source lista kontaktów, którą należy skopiować.
		**/
		UserList(const UserList& source);
		~UserList();

		/**
			\fn UserList& operator=(const UserList& userlist)
			Operator kopiujący.
			\param userlist lista kontaktów, którą należy skopiować.
		**/
		UserList& operator=(const UserList& userlist);

		/**
			\fn UserListElement& byUin(UinType uin)
			Wyszukuje kontakt z numerem \a uin.
			\param uin numer UIN wg. którego następuje wyszukiwanie.
			\return referencję do obiektu reprezentującego kontakt o podanym UIN'ie.
		**/
		UserListElement& byUin(UinType uin);
		
		/**
			\fn UserListElement& byNick(const QString& nickname);
			Wyszukuje kontakt po pseudonimie. Małe i
			duże litery nie mają znaczenia.
			\param nickname pseudonim wg. którego następuje wyszukiwanie.
			\return referencję do obiektu reprezentującego kontakt o podanym pseudonimie.
		**/
		UserListElement& byNick(const QString& nickname);

		/**
			\fn UserListElement& byAltNick(const QString& altnick)
			Wyszukuje kontakt po wyświetlanym pseudonimie. Małe i
			duże litery nie mają znaczenia.
			\param altnick wyświetlany pseudonim wg. którego następuje wyszukiwanie.
			\return referencję do obiektu reprezentującego kontakt o podanym pseudonimie wyświetlanym.
		**/
		UserListElement& byAltNick(const QString& altnick);

		/**
			\fn UserListElement byUinValue(UinType uin)
			Wyszukuje kontakt po numerze \a uin.
			\param uin UIN wg. którego następuje wyszukiwanie.
			\return obiekt reprezentujący kontakt z podanym UIN'em. Jezeli nie znajdzie, zwracany jest UserListElement zawierający tylko UIN i pseudonim wyświetlany (identyczny z numerem UIN).
		**/
		UserListElement byUinValue(UinType uin);

		/**
			\fn bool containsUin(UinType uin) const
			Sprawdza czy istnieje kontakt o danym numerze uin.
			\param uin sprawdzany UIN.
		**/
		bool containsUin(UinType uin) const;

		/**
			\fn bool containsAltNick(const QString& altnick) const;
			Sprawdza czy istnieje kontakt o danym pseudonimie wyświetlanym.
			Małe i duże litery nie mają znaczenia.
			\param altnick Sprawdzany pseudonim wyświetlany.
		**/
		bool containsAltNick(const QString& altnick) const;

	public slots:
		/**
			\fn void addUser(const UserListElement& ule)
			Dodaje do listy kontakt o podanych danych.
			Generuje następnie sygnały userDataChanged()
			i modified().
			\param ule obiekt reprezentujący kontakt, który chcemy dodać.
		**/
		void addUser(const UserListElement& ule);

		/**
			\fn void addAnonymous(UinType uin)
			Dodaje do listy "anonimowy" kontakt o podanym
			numerze UIN. Numer ten wykorzystywany jest jako
			pseudonim wyświetlany. Wywołuje metodę addUser(), więc
			generowane są sygnały userDataChanged() i modified().
			\param uin UIN kontaktu, który chcemy dodać.
		**/
		void addAnonymous(UinType uin);

		/**
			\fn void removeUser(const QString& altnick)
			Usuwa kontakt o podanym pseudonimie wyświetlanym. Małe i
			duże litery nie mają znaczenia.
			Tuż przed usunięciem kontaktu z listy generuje
			sygnał userDataChanged(). Po usunięciu generowany
			jest sygnał modified().
			\param altnick pseudonim wyświetlany kontaktu, który chcemy usunąć.
		**/
		void removeUser(const QString& altnick);

		/**
			\fn void changeUserInfo(const QString& old_altnick, const UserListElement& new_data)
			Zmienia dane kontaktu o podanym pseudonimie wyświetlanym. Małe i
			duże litery w zmiennej \a old_altnick nie mają znaczenia.
			Po wykonaniu zmian wymusza ich uwzględnienie przez
			wszystkie obiekty klasy UserBox wywołując metodę
			UserBox::all_renameUser() i UserBox::all_refresh().
			Na koniec generuje sygnały userDataChanged() i modified().
			\param old_altnick dotychczasowy pseudonim wyświetlany kontaktu.
			\param new_data obiekt reprezentujący nowe dane kontaktu.
		**/
		void changeUserInfo(const QString& old_altnick, const UserListElement& new_data);

		/**
			\fn bool writeToFile(QString filename = "")
			Zapisuje listę kontaktów do pliku o podanej nazwie
			(wraz ze ścieżką). Domyślnie jest to plik "userlist"
			w katalogu z ustawieniami.
			Utrzymywana jest kompatybilność z programem EKG,
			z tej przyczyny dodatkowe informacje jak to czy
			blokujemy użytkownika, czy chcemy być powiadamiani
			o zmianach jego statusu itp. są zapisywane do
			oddzielnego pliku "userattribs" w katalogu z
			ustawieniami.
			Kontakty "anonimowe" są pomijane.
			\param filename plik do którego zostanie zapisana lista.
			\return FALSE, gdy wystąpiły błędy, lub TRUE w przeciwnym wypadku.
			
			TODO: To gdzie zapisujemy dodatkowe atrybutami też
			powinno być chyba podawane jako argument? Inaczej
			jeśli chcemy zapisać listę gdzieś indziej to nadpiszą
			nam się atrybuty :/
		**/
		bool writeToFile(QString filename = "");

		/**
			\fn bool readFromFile()
			Wczytuje listę kontaktów z plików "userlist"
			i "userattribs" z katalogu z ustawieniami.
			Metoda ta jest przeznaczona do przywracania
			danych zapisanych za pomocą writeToFile().
			\return FALSE, gdy wystąpiły błędy, lub TRUE w przeciwnym wypadku.
		**/
		bool readFromFile();

		/**
			\fn void merge(const UserList &userlist)
			Scala listę kontaktów z podaną listą kontaktów \a userlist.
			\param userlist lista kontaktów z którą należy scalić
		**/
		void merge(const UserList &userlist);

	signals:
		/**
			\fn void modified()
			Sygnał generowany po zakończeniu zmian w liście
			kontaktów.
		**/
		void modified();

		/**
			\fn void dnsNameReady(UinType)
			Sygnał generowany, gdy adres IP kontaktu o danym UIN'ie zostanie przetłumaczony
			na nazwę domeny.
			\param uin UIN kontaktu, dla którego przygotowano nazwę domeny.
		**/
		void dnsNameReady(UinType uin);

		/**
			\fn void userDataChanged(const UserListElement* const oldData, const UserListElement* const newData, bool massively = false)
			Sygnał jest generowany jeśli cokolwiek zmieniło
			się w danych któregoś z kontaktów na liście
			(wtedy przekazywane są argumenty \a oldData i \a newData).
			Zmiana może nastąpić przez bezpośrednie użycie którejś
			z metod klasy UserListElement.
			Sygnał przekazuje również informację o tym, że nowy
			kontakt pojawił się na liście,
			lub że został z listy usunięty.
			Jeżeli ta zmiana jest częścią czegoś większego, to \a massively == true
			i emitowany jest sygnał UserList::allNewContacts.
			
			\param oldData obiekt reprezentujący stare dane kontaktu. Jeśli ten parametr ma wartość NULL,
				to znaczy, że kontakt właśnie został dodany do listy.
			\param newData obiekt reprezentujący nowe dane kontaktu. Jeśli ten parametr ma wartość NULL,
				to znaczy, że kontakt został właśnie usunięty z listy.
		**/
		void userDataChanged(const UserListElement* const oldData, const UserListElement* const newData, bool massively = false);

		/**
			\fn void allNewContacts(const UserList &ulist)
			Sygnał jest emitowany, kiedy powstaje zupełnie nowa lista kontaktów.
		**/
		void allNewContacts(const UserList &ulist);
};

extern UserList userlist;

#endif

