#ifndef GADU_H
#define GADU_H

#include <qdatetime.h>
#include <qhostaddress.h>
#include <qobject.h>
#include <qstring.h>
#include <qtextstream.h>
#include <qtimer.h>
#include <qvaluelist.h>

#include "libgadu.h"
#include "status.h"

typedef uin_t UinType;

class QSocketNotifier;
class UserList;
class UserListElement;

// ------------------------------------
//            UinsList
// ------------------------------------

class UinsList : public QValueList<UinType>
{
	public:
		/**
			konstruuje obiekt UinsList
		**/
		UinsList();

		/**
			konstruuje obiekt UinsList, inicjując go uinem
		**/
		UinsList(UinType uin);
		
		/**
			konstruuje obiekt UinsList na podstawie łańcucha "uins" składającego
			się z oddzielonych przecinkami Uinów
		**/
		UinsList(const QString &uins);

		/**
			konstruuje obiekt UinsList na podstawie "list"
			przekształacając każdy element do typy UinType
		**/
		UinsList(const QStringList &list);

		bool equals(const UinsList &uins) const;
		void sort();
		QStringList toStringList() const;
};

// ------------------------------------
//             UserStatus
// ------------------------------------

class GaduStatus : public UserStatus
{
	Q_OBJECT

	public:
		GaduStatus();
		virtual ~GaduStatus();

		void operator = (const UserStatus &copyMe);

		virtual QPixmap pixmap(eUserStatus status, bool has_desc, bool mobile) const;

		int toStatusNumber() const;
		static int toStatusNumber(eUserStatus status, bool has_desc);

		void fromStatusNumber(int statusNumber, const QString &description);
};

// ------------------------------------
//              Search
// ------------------------------------

struct SearchResult
{
	QString Uin;
	QString First;
	QString Last;
	QString Nick;
	QString Born;
	QString City;
	QString FamilyName;
	QString FamilyCity;
	int Gender;
	GaduStatus Stat;

	SearchResult();
	SearchResult(const SearchResult &);
	void setData(const char *uin, const char *first, const char *last, const char *nick, const char *born,
		const char *city, const char *familyName, const char *familyCity, const char *gender, const char *status);
};

typedef QValueList<SearchResult> SearchResults;

struct SearchRecord
{
	int Seq;
	int FromUin;
	QString Uin;
	QString FirstName;
	QString LastName;
	QString NickName;
	QString City;
	QString BirthYearFrom;
	QString BirthYearTo;
	int Gender;
	bool Active;

	SearchRecord();
	virtual ~SearchRecord();

	void reqUin(const QString& uin);
	void reqFirstName(const QString& firstName);
	void reqLastName(const QString& lastName);
	void reqNickName(const QString& nickName);
	void reqCity(const QString& city);
	void reqBirthYear(const QString& birthYearFrom, const QString& birthYearTo);
	void reqGender(bool female);
	void reqActive();

	void clearData();
};

// ------------------------------------
//             Notifiers
// ------------------------------------

class SocketNotifiers : public QObject
{
	Q_OBJECT

	protected:
		int Fd;
		QSocketNotifier *Snr;
		QSocketNotifier *Snw;

		void createSocketNotifiers();
		void deleteSocketNotifiers();
		void recreateSocketNotifiers();
		virtual void socketEvent() = 0;

	protected slots:
		virtual void dataReceived() = 0;
		virtual void dataSent() = 0;

	public:
		SocketNotifiers(int fd, QObject *parent=0, const char *name=0);
		virtual ~SocketNotifiers();
		virtual void start();
		virtual void stop();
};

class PubdirSocketNotifiers : public SocketNotifiers
{
	Q_OBJECT

	private:
		struct gg_http *H;

	protected:
		virtual void socketEvent();

	protected slots:
		virtual void dataReceived();
		virtual void dataSent();

	public:
		PubdirSocketNotifiers(struct gg_http *, QObject *parent=0, const char *name=0);
		virtual ~PubdirSocketNotifiers();

	signals:
		void done(bool ok, struct gg_http *);
};

class TokenSocketNotifiers : public SocketNotifiers
{
	Q_OBJECT

	private:
		struct gg_http *H;

	protected:
		virtual void socketEvent();

	protected slots:
		virtual void dataReceived();
		virtual void dataSent();

	public:
		TokenSocketNotifiers(QObject *parent=0, const char *name=0);
		virtual ~TokenSocketNotifiers();

		virtual void start();

	signals:
		void gotToken(QString, QPixmap);
		void tokenError();
};

enum GaduError
{
	ConnectionServerNotFound,
	ConnectionCannotConnect,
	ConnectionNeedEmail,
	ConnectionInvalidData,
	ConnectionCannotRead,
	ConnectionCannotWrite,
	ConnectionIncorrectPassword,
	ConnectionTlsError,
	ConnectionUnknow,
	ConnectionTimeout,
	Disconnected
};

class GaduSocketNotifiers : public SocketNotifiers
{
	Q_OBJECT

	private:
		gg_session *Sess;
		int socketEventCalls;

		void connectionFailed(int);

	protected:
		virtual void socketEvent();

	public slots:
		virtual void dataReceived();
		virtual void dataSent();

	public:
		GaduSocketNotifiers(QObject *parent=0, const char *name=0);
		virtual ~GaduSocketNotifiers();
		void setSession(gg_session *sess);
		void checkWrite();

	signals:
		void ackReceived(int seq, uin_t uin, int status);
		void connected();
		/**
			Otrzymano wiadomość CTCP.
			Ktoś nas prosi o połączenie dcc, ponieważ
			jesteśmy za NAT-em.
			TODO: zmienić nazwę.
		**/
		void dccConnectionReceived(const UserListElement &);
		void disconnected();
		void error(GaduError);
		void imageReceived(UinType, uint32_t, uint32_t, const QString &filename, const char *data);
		void imageRequestReceived(UinType, uint32_t, uint32_t);
		void messageReceived(int, UinsList, QCString &, time_t, QByteArray &);
		void pubdirReplyReceived(gg_pubdir50_t);
		void systemMessageReceived(QString &, QDateTime &, int, void *);
		void userlistReceived(const struct gg_event *);
		void userlistReplyReceived(char, char *);
		void userStatusChanged(const struct gg_event *);
};

// ------------------------------------
//            GaduProtocol
// ------------------------------------

/**
	Klasa do obsługi protokołu Gadu-Gadu
**/
class GaduProtocol : public QObject
{
	Q_OBJECT

	private:

		/**
			@enum Mode
			@short Tryb pracy obiektu
			@see TokenSocketNotifiers
			@see gotToken
			@see needTokenValue
			@see register
			@see doRegister
			@see unregister
			@see doUnregister
			@see remindPassword
			@see doRemindPassword
			@see changePassword
			@see doChangePassword

			Kilka operacji w protokole Gadu-Gadu wymaga od klienta wysłania do serwera, oprócz
			standardowych pakietów, tak zwanego 'tokena'. Najpierw pobierany jest z serwera identyfikator
			tokena oraz obrazek. Następnie z obrazka odczytywana jest wartośc tokena (w założeniach
			miało to zapobiegać rejestrowaniu nowych użytkowników przez automaty.

			Sloty register, unregister, remindPassword i changePassword inicjują pobieranie tokena
			i ustalają wartość pola Mode. Pobieranie obrazka realizowane jest przez klasę
			TokenSocketNotifiers. Po pobraniu wywoływany jest slot gotToken, który na podstawie wartości
			pola Mode wywołuje jedną z funkcji doRegister, doUnregister, doRemindPassword i doChangePassword.
		**/
		enum
		{
			/** Rejestrowanie nowego użytkownika **/
			Register,
			/** Wyrejestrowywanie istniejącego użytkownika **/
			Unregister,
			/** Przypominanie hasła **/
			RemindPassword,
			/** Zmienianie hasła **/
			ChangePassword
		} Mode;

		/** Identyfikator użytkownika **/
		UinType DataUin;
		/** e-mail użytkownika **/
		QString DataEmail;
		/** stare hasło użytkownika **/
		QString DataPassword;
		/** nowe hasło użytkownika **/
		QString DataNewPassword;
		/** identyfikator tokena **/
		QString TokenId;
		/** wartość tokena **/
		QString TokenValue;

		/** Serwery, z którymi łaczy się obiekt. **/
		static QValueList<QHostAddress> ConfigServers;
		/** Numer serwera, do którego obiekt ostatnio próbował się podłączyć. **/
		unsigned int ServerNr;
		/** Adres serwera, do którego obiekt jest podłączony. **/
		QHostAddress* ActiveServer;

		/** Parametry logowania - wymagane przez bibliotekę libgadu **/
		struct gg_login_params LoginParams;
		/** Sesja połączenia - wymagane przez bibliotekę libgadu **/
		gg_session* Sess;

		/** czy jesteśmy w trakcie łączenia się z serwerem **/
		bool whileConnecting;

		QHostAddress DccExternalIP;

		/**
			Bieżący status. Zmieniany po połączeniu, oraz w przypadku zmiany statusu kiedy połączenie
			jest już zainicjowane.

			@see login
			@see connected
			@see iWantGoOnline
			@see iWantGoBusy
			@see iWantGoInvisible
			@see iWantGoOffline
			@see NextStatus
			@see UserStatus
			@see status
		**/
		GaduStatus* CurrentStatus;

		/**
			Następny status. Ustalany zewnętrznie przy wykorzystaniu metody status i odpowiednich
			slotów klasy UserStatus. Zmiana wywołuje jedną z metod iWantGo... i w konsekwencji zmianę
			statusu (w razie konieczności też zalogowanie).

			@see login
			@see connected
			@see iWantGoOnline
			@see iWantGoBusy
			@see iWantGoInvisible
			@see iWantGoOffline
			@see CurrentStatus
			@see UserStatus
			@see status
		**/
		GaduStatus* NextStatus;

		/**
			Klasa gniazdek łącząca się z serwerem. Wysyła sygnały po wystąpieniu zdarzenia protokołu
			(połączenie, zerwanie połączenia, nowa wiadomość).

			@see GaduSocketNotifiers
		**/
		GaduSocketNotifiers *SocketNotifiers;

		/**
			Zegar pingujący serwer.
		**/
		QTimer* PingTimer;

		/**
			Określa, czy lista użytkowników została już wysłana.

			@todo Wywalić, zamienić na connected
		**/
		bool UserListSent;

		/**
			Zmienna ustawiana w zależności od tego, czy wysyłamy listę kontaktów na serwer
			czy też usuwamy ją z tego serwera. Zakończenie obydwu tych czynności wywołuje
			sygnał podpięty do slotu userListReplyReceived, który w zależności od wartości
			tego pola wywołuje userListCleared albo userListExported.

			@see userListReplyReceived
			@see userListCleared
			@see userListExported
		**/
		bool UserListClear;

		/**
			Lista użytkowników pobrana z serwera w postaci łańcucha. Wartość ustalana w slocie
			userListReplyReceived.

			@see userListReplyReceived
		**/
		QString ImportReply;

		/**
			Ustawianie parametrów połączenia proxy. Metoda wywoływana podczas logowania.

			@see login
		**/
		void setupProxy();

		/**
			Metoda wywoływana w razie rozłączenie się z serwerem. Wywoływana przez iWantGoOffline
			albo przez connectionTimeoutTimerSlot. Wywołuje disconnectedSlot, który z kolei
			emituje sygnał disconnected

			@see disconnected
			@see disconnectedSlot
			@see connectionTimeoutTimerSlot
			@see iWantGoOffline
			@todo Wywalić i zastąpić wywołaniami disconnectedSlot?
		**/
		void logout();

		/**
			Za pomocą klasy TokenSocketNotifiers metoda pobiera z serwera GaduGadu token wraz
			z identyfikatorem. Pobrany token jest obsługiwany za pomocą slota gotToken,
			który pobiera wartość tokena emitując sygnał needTokenValue i następnie wywołując
			jedną z metod  doRegisterAccount, doUnregisterAccount(), doRemindPassword(),
			doChangePassword() na podstawie wartości pola Mode.

			@see TokenSocketNotifiers
			@see gotToken
			@see doRegisterAccount
			@see doUnregisterAccount
			@see doRemindPassword
			@see doChangePassword
			@see Mode
		**/
		void getToken();

		/**
			Rejestruje nowe konto. Wywoływane przez gotToken (które jest wywołane pośrednio przez
			registerAccount). Korzysta z pomocy PubdirSocketNotifiers oraz slotu registerDone,
			który emituje sygnał registered.

			@see registerAccount
			@see registered
			@see registerDone
			@see gotToken
		**/
		void doRegisterAccount();

		/**
			Wyrejestrowuje konto. Wywoływane przez gotToken (które jest wywołane pośrednio przez
			unregisterAccount). Korzysta z pomocy PubdirSocketNotifiers oraz slotu unregisterDone,
			który emituje sygnał unregistered.

			@see unregisterAccount
			@see unregistered
			@see unregisterDone
			@see gotToken
		**/
		void doUnregisterAccount();

		/**
			Przypomina hasło. Wywoływane przez gotToken (które jest wywołane pośrednio przez
			remindPassword). Korzysta z pomocy PubdirSocketNotifiers oraz slotu remindDone,
			który emituje sygnał reminded.

			@see remindPassword
			@see reminded
			@see remindDone
			@see gotToken
		**/
		void doRemindPassword();

		/**
			Zmienia hasło. Wywoływane przez gotToken (które jest wywołane pośrednio przez
			changePassword). Korzysta z pomocy PubdirSocketNotifiers oraz slotu changePasswordDone,
			który emituje sygnał passwordChanged.

			@see changePassword
			@see passwordChanged
			@see changePasswordDone
			@see gotToken
		**/
		void doChangePassword();

	private slots:

		/**
			Loguje się do serwera Gadu-Gadu. Po uruchomieniu emituje sygnał connecting. Parametry
			logowania odczytuje z konfiguracji, status logowania pobiera z pola NextStatus.
			Łącząc się, wybiera kolejne serwery (w przypadku nieudanego połączenia) wykorzystując
			pola ConfigServers i i ServerNr.

			Po poprawnym zalogowaniu wywoływany jest slot connectedSlot, który emituje sygnał
			GaduProtocol::connected

			Metodę można uruchomić pośrednio poprzez wywołanie typu gadu->status().setOnline(),
			które wywoła slot iWantToGoOnline, który z kolei (gdy stwierdzi, że nie jesteśmy zalogowani)
			wywoła procedurę.

			@see connecting
			@see connected
			@see connectedSlot
			@see NextStatus
			@see ConfigServers
			@see ServerNr
		**/
		void login();

		/**
			Wywoływany po zarejestrowaniu konta. Emituje registered/

			@see registerAccount
			@see doRegisterAccount
			@see registered
		**/
		void registerDone(bool ok, struct gg_http *);

		/**
			Wywoływany po wyrejestrowaniu konta. Emituje unregistered.

			@see unregisterAccount
			@see doUnregisterAccount
			@see unregistered
		**/
		void unregisterDone(bool ok, struct gg_http *);

		/**
			Wywoływany po przypomnieniu hasła. Emituje reminded.

			@see remindPassword
			@see doRemindPassword
			@see reminded
		**/
		void remindDone(bool ok, struct gg_http *);

		/**
			Wywoływany po zmianie hasła. Emituje passwordChanged.

			@see changePassword
			@see doChangePassword
			@see passwordChanged
		**/
		void changePasswordDone(bool ok, struct gg_http *);

		/**
			Slot wywoływany, gdy pobieranie tokena się nie udało.

			@see getToken
		**/
		void tokenError();
		/**
			Slot wywoływany, gdy pobieranie tokena się powiodło. Emituje needTokenValue

			@see getToken
			@see needTokenValue
		**/
		void gotToken(QString, QPixmap);

		/**
			Slot wywoływany po połączeniu z serwerem. Emituje connected i włącza pingowanie
			serwera.

			@see connected
		**/
		void connectedSlot();

		/**
			Slot wywoływany po rozłączeniu z serwerem. Emituje disconnected i wyłącza pingowanie
			serwera.

			@see disconnected
		**/
		void disconnectedSlot();

		/**
			Slot wywoływany po przekroczeniu czasu połączenia. Próbuje połaczyć ponownie.
		**/
		void connectionTimeoutTimerSlot();

		/**
			Slot wywoływane po wystąpieniu błędu połączenia. Emituje disconnected i error.

			@see error
			@see disconnected
		**/
		void errorSlot(GaduError);

		/**
			Slot wywoływany po otrzymaniu obrazka od serwera. Emituje imageReceivedAndSaved

			@see imageReceivedAndSaved
		**/
		void imageReceived(UinType sender, uint32_t size, uint32_t crc32,
			const QString &filename, const char *data);

		/**
			Slot wywoływany po otrzymaniu prośby o obrazek od serwera. Wysyła obrazek.
		**/
		void imageRequestReceivedSlot(UinType, uint32_t, uint32_t);

		/**
			Slot wywoływany po otrzymaniu wiadomości od serwera.
		**/
		void messageReceived(int, UinsList, QCString& msg, time_t, QByteArray &formats);

		/**
			Co pewien czas pinguje serwer.
		**/
		void pingNetwork();

		/**
			Obsługuje otrzymanie nowych wyników wyszukiwania z serwera. Emituje newSearchResults.

			@see newSearchResults
		**/
		void newResults(gg_pubdir50_t res);

		/**
			Nowa wiadomość od serwera. Emituje systemMessageReceived

			@see systemMessageReceived
		**/
		void systemMessageReceived(QString &, QDateTime &, int, void *);

		/**
			Pobrano listę użytkowników z serwera. Emituje userStatusChanged dla każdego
			otrzymanego kontaktu oraz userListChanged przed zakończeniem działania.

			@see userStatusChanged
			@see userListChanged
		**/
		void userListReceived(const struct gg_event *);

		/**
			Odpowiedź od serwera na temat operacji na liście użytkowników. Emituje, w zależności
			od trybu działania: userListCleared, userListExported, userListImported.

			@see userListCleared
			@see userListExported
			@see userListImported
		**/
		void userListReplyReceived(char, char *);

		/**
			Informacja o zmianie statusu kontaktu. Emituje userStatusChanged oraz userListChanged.

			@see userStatusChanged
			@see userListChanged
		**/
		void userStatusChanged(const struct gg_event *);

		/**
			Ktoś wykonał gadu.status().setOnline(). Łączymy z serwerem, jeżeli jeszcze tego nie
			zrobiliśmy, i zmieniamy status.

			@see CurrentStatus
			@see NextStatus
			@see login
		**/
		void iWantGoOnline(const QString &);

		/**
			Ktoś wykonał gadu.status().setBusy(). Łączymy z serwerem, jeżeli jeszcze tego nie
			zrobiliśmy, i zmieniamy status.

			@see CurrentStatus
			@see NextStatus
			@see login
		**/
		void iWantGoBusy(const QString &);

		/**
			Ktoś wykonał gadu.status().setInvisible(). Łączymy z serwerem, jeżeli jeszcze tego nie
			zrobiliśmy, i zmieniamy status.

			@see CurrentStatus
			@see NextStatus
			@see login
		**/
		void iWantGoInvisible(const QString &);

		/**
			Ktoś wykonał gadu.status().setOffline(). Rozłączamy się z serwerem i ustawiamy opis (jeżeli
			byliśmy połączeni).

			@see CurrentStatus
			@see NextStatus
			@see logout
		**/
		void iWantGoOffline(const QString &);
		
		/**
			Przyszła informacja o dostarczeniu (lub nie) wiadomości.
			Na podstawie statusu emituje odpowiednie sygnały message*

			@see messageBlocked
			@see messageDelivered
			@see messageQueued
			@see messageBoxFull
			@see messageNotDelivered
			@see messageAccepted
			@see messageRejected
		**/
		void ackReceived(int seq, uin_t uin, int status);
		
	public:
		static void initModule();
		GaduProtocol(QObject *parent = NULL, const char *name = NULL);
		virtual ~GaduProtocol();

		/**
			Status użytkownika. Za pomocą tej metody możemy go zmienić, pobrać ikonę statusu i wykonać
			kilka innych ciekawych rzeczy.

			1. Zmiena statusu:
			<code>
				GaduProtocol gadu;

				...

				gadu.status().setOnline("Jestem zalogowany"); // zalogowanie i ustawienie opisu
				gadu.status().setFriendsOnly(true);           // tryb tylko dla przyjaciół
				...
				gadu.status().setOffline();                   // wylogowanie, usunięcie opisu
			</code>

			2. Sprawdzenie statusu:
			<code>
				GaduProtocol gadu;

				if (gadu.status().isOnline())                 // jesteśmy online
					...
				else if (gadu.status().isInvisible())         // jesteśmy niewidzialni
					...

				// można też:
				switch (gadu.status().status())
				{
					case Online:
						break;
					case Busy:
						break;
					case Invisible:
						break;
					case Offline:
						break;
				}
			</code>

			3. Pobranie ikony i nazwy statusu
			<code>
				QPixmap pix;
				QString name;
				GaduProtocol gadu;

				...

				pix = gadu.status().pixmap();
				name = gadu.status().name();
			</code>

			@see currentStatus
		**/
		UserStatus & status();

		/**
			Rzeczywisty aktualny status. Można go wykorzystać tylko w trybie do odczytu (pobranie
			ikony, nazwy, sprawdzenie rzeczywistego stanu połączenia).

			@see status
		**/
		const UserStatus & currentStatus();

		/**
			Zwraca serwer z którym jesteśmy połączeni lub do którego się właśnie łączymy.
			NULL = hub.
		**/
		QHostAddress* activeServer();

		/**
			Konwertuje listę użytkowników do postaci łańcucha.

			Łańcuch wynikowy ma postać:
			<code>
				opis_użytkownika<CR><LF>
				opis_użytkownika<CR><LF>
				...
				opis_użytkownika<CR><LF>
			</code>

			opis_użytkownika ma postać:
			<code>
				firstName;lastName;nickName;altNick;mobile;grupy;uin;email;0;;0;
			</code>

			grupy mają postać:
			<code>
				grupa_1,grupa_2,grupa_3
			</code>

			@param userList lista użytkowników, która zostanie skonwertowana
			@return łańcuch reprezentujący listę użytkowników
			@see stringToUserList
			@see streamToUserList
		**/
		QString userListToString(const UserList &userList) const;

		/**
			Konwertuję łańcuch do listy użytkowników.

			Format łańcucha jest anologiczny do tego z funkcji userListToString. Jedynym wyjątkiem
			jest to, że grupy oddzielone są średnikami, nie przecinkami.

			@param source łańuch, będący reprezentacją listy użytkowników
			@param userList lista użytkowników, do której zapisany zostanie wynik konwersji
			@see userListToString
			@see streamToUserList
		**/
		void stringToUserList(QString &source, UserList &userList) const;

		/**
			Odczytuje ze strumienia łańcuch reprezentujący listę użytkowników i konwertuje
			go go postaci obiektu UserList.

			Format łańcucha jest anologiczny do tego z funkcji userListToString. Jedynym wyjątkiem
			jest to, że grupy oddzielone są średnikami, nie przecinkami.

			@param source strumień, z którego odczytane zostaną dane
			@param userList lista użytkowników, do której zapisany zostanie wynik konwersji
			@see userListToString
			@see stringToUserList
		**/
		void streamToUserList(QTextStream &source, UserList &userList) const;

		/**
			Po jedno sekundowym opóźnieniu wykonuje próbę połączenia.
		**/
		void connectAfterOneSecond();

		/**
			Zwraca true, jeżeli jesteśmy połączenie z serwerem.

			@todo zmienić nazwę na connected
		**/
		bool userListSent();

		/**
			Ustawia adres IP który ma być przekazany do serwera GG
			jako adres komputera, który przekierowuje do nas port DCC.
			Zmiana jest uwzględniana dopiero przy ponownym połączeniu
			z serwerem.
		**/
		void setDccExternalIP(const QHostAddress& ip);

	public slots:
		/**
			Wysyła wiadomość bez formatowania tekstu. Jeśli adresatów jest więcej niż jeden, to wysyłana
			jest wiadomość konferencyjna. Zwracany jest numer sekwencyjny wiadomości, jeśli
			przypadkiem byśmy chcieli śledzić jej potwierdzenie.

			@param uins lista użytkowników, do których wysyłamy wiadomość
			@param msg wiadomość, którą wysyłamy - musi być podana w postaci cp1250
			@todo zmienić na sendMessage(const UinsList &, QString &) z wewnętrzną konwersją na cp1250
		**/
		int sendMessage(const UinsList &uins, const char *msg);

		/**
			Wysyła wiadomość z formatowaniem tekstu. Jeśli adresatów jest więcej niż jeden, to wysyłana
			jest wiadomość konferencyjna. Zwracany jest numer sekwencyjny wiadomości, jeśli
			przypadkiem byśmy chcieli śledzić jej potwierdzenie.

			@param uins lista użytkowników, do których wysyłamy wiadomość
			@param msg wiadomość, którą wysyłamy - musi być podana w postaci cp1250
			@param myLastFormats formatowanie tekstu
			@param myLastFormatsLength ilośc znaczników formatujących

			@todo zmienić na sendMessageRichText(const UinsList &, QString &, ...)
				z wewnętrzną konwersją na cp1250 oraz z jakimś lepszym sposobem formatowania tekstu
		**/
		int sendMessageRichText(const UinsList &uins, const char *msg, unsigned char *myLastFormats,
		 	unsigned int myLastFormatsLength);

		/**
			Wysyła prośbę o przysłanie obrazka z danymi parametrami.

			@param uin użytkownik, od którego chcemy obrazek
			@param size rozmiar obrazka w bajtach
			@param crc32 crc32 pliku
			@todo powinno być sendImageRequest(uniqId uint32_t) - info o obrazku zapisywać gdzieś w środku
		**/
		bool sendImageRequest(UinType uin, int size, uint32_t crc32);
		/**
			Wywyła obrazek o podanych parametrach.

			@param uin użytkownik, któremu wysyłamy obrazek
			@param file_name nazwa pliku obrazka
			@param size rozmiar obrazka w bajtach
			@param data zawartość pliku
			@todo usunąć parametry size i data - możemy to chyba sami wyznaczyć
		**/
		bool sendImage(UinType uin, const QString &file_name, uint32_t size, const char *data);

		/**
			Rejetrujemy nowe konto. Odpowiedź przychodzi poprzez sygnał registered. Może
			zostać także wywołany sygnał needTokenValue.

			@param mail nasz email, pole nieobowiązkowe
			@param password nasze hasło, pole obowiązkowe
			@see registered
			@see needTokenValue
			@see unregisterAccount
		**/
		void registerAccount(const QString &mail, const QString &password);
		/**
			Wyrejestrowujemy stare konto. Odpowiedź przychodzi poprzez sygnał unregistered. Może
			zostać także wywołany sygnał needTokenValue.

			@param uin nasz uin
			@param password nasze hasło
			@todo parametr uin naprawdę potrzebny?
		**/
		void unregisterAccount(UinType uin, const QString &password);
		/**
			Wysyła hasło na email. Odpowiedź przychodzi poprzez sygnał reminded. Może
			zostać także wywołany sygnał needTokenValue.

			@param uin nasz uin
			@param email nasz email (musi być taki sam jak podczas rejestracji)
			@todo parametr uin naprawdę potrzebny?
		**/
		void remindPassword(UinType uin, const QString& mail);
		/**
			Zmienia nasze hasło. Odpowiedź przychodzi poprzez sygnał passwordChanged. Może
			zostać także wywołany sygnał needTokenValue.

			@param uin nasz uin
			@param mail nasz email, jaki podaliśmy przy rejestracji
			@param password stare hasło
			@param newPassword nowe hasło
			@todo parametr uin naprawdę potrzebny?
		**/
		void changePassword(UinType uin, const QString &mail, const QString &password,
			const QString &newPassword);

		/**
			Wysyła listę użytkowników na serwer. Odpowiedź przychodzi przez sygnał userListExported.

			@return false, jeżli operacja się nie powiodła
			@param userList lista do wysłania
			@see userListExported
			@todo usunąć wartość zwracaną
		**/
		bool doExportUserList(const UserList &userList);

		/**
			Usuwa listę użytkowników z serwera. Odpowiedź przychodzi przez sygnał userListCleared.

			@return false, jeżli operacja się nie powiodła
			@see userListCleared
			@todo usunąć wartość zwracaną
		**/
		bool doClearUserList();

		/**
			Importuje listę użytkowników z serwera. Odpowiedź przychodzi przez sygnał userListImported.

			@return false, jeżli operacja się nie powiodła
			@see userListImported
			@todo usunąć wartość zwracaną
		**/
		bool doImportUserList();

		/**
			Wysyła naszą listę użytkowników na serwer. Uwaga: nie ma to nic wspólnego z importem/eksportem.
		**/
		void sendUserList();

		/**
			wysyła wskazaną listę użytkowników na serwer
			@see sendUserList
		**/
		void sendUserList(const UserList &ulist);

		/**
			Szuka ludzi w katalogu publicznym. Wyniki przychodzą za pomoca sygnału newSearchResults.

			@param searchRecord dane do szukania
			@see newSearchResults
			@see searchNextInPubdir
		**/
		void searchInPubdir(SearchRecord& searchRecord);
		/**
			Szuka ludzi w katalogu publicznym. Wyniki przychodzą za pomoca sygnału newSearchResults.

			@param searchRecord dane do szukania
			@see newSearchResults
			@see searchInPubdir
		**/
		void searchNextInPubdir(SearchRecord& searchRecord);

		/**
			Pobiera informacje o danych osobowych z katalogu publicznego.

			@todo jak to w ogóle działa, bo zapomniałem??
		**/
		void getPersonalInfo(SearchRecord& searchRecord);

		/**
			Ustawia informacje o danych osobowych w katalogu publicznym.

			@todo jak to w ogóle działa, bo zapomniałem??
		**/
		void setPersonalInfo(SearchRecord& searchRecord, SearchResult& newData);

		/**
			 To raczej chwilowo.

			 @todo usunąć
			 @todo niech Adrian zrobi porzadek z tym dcc...
		**/
		void freeEvent(struct gg_event* e);

		// --------------------
		//  DCC
		// --------------------

		void dccRequest(UinType);
		void dccFree(struct gg_dcc* d);
		struct gg_event* dccWatchFd(struct gg_dcc* d);
		void dccSetType(struct gg_dcc* d, int type);
		int dccFillFileInfo(struct gg_dcc* d, const QString& filename);
		void dccSocketCreate(UinType uin, uint16_t port, struct gg_dcc **out);
		void dccSendFile(uint32_t ip, uint16_t port, UinType my_uin, UinType peer_uin, struct gg_dcc **out);
		void dccGetFile(uint32_t ip, uint16_t port, UinType my_uin, UinType peer_uin, struct gg_dcc **out);
		void dccVoiceChat(uint32_t ip, uint16_t port, UinType my_uin, UinType peer_uin, struct gg_dcc **out);
		int dccVoiceSend(struct gg_dcc* d, char* buf, int length);
		void setDccIpAndPort(unsigned long dcc_ip, int dcc_port);

		// przeniesione z events.h
		/**
			@todo uprywatnić
		**/
		void onCreateConfigDialog();

		/**
			@todo uprywatnić
		**/
		void onDestroyConfigDialog();

		/**
			@todo uprywatnić
		**/
		void ifDefServerEnabled(bool value);

		/**
			@todo uprywatnić
		**/
		void useTlsEnabled(bool value);

		/**
			Slot wywoływany, gdy zmieniły się dane kontaktu. Umożliwia poinformowanie serwera
			o zmianie naszych zaleceń co do obsługi wiadomości od kontaktu.

			Przykłady:
			<code>
				GaduProtocol gadu;
				UserListElement old, new;

				...
				gadu.userDataChanged(NULL, &new);              // dodaliśmy nowy kontakt

				old = new;
				new.setBlockng(true);                          // blokujemy
				gadu.userDataChanged(&old, &new);              // informujemy o tym serwer

				gadu.userDataChanged(new, NULL);               // usunęliśmy kontakt
				...
			</code>

			Powyższe kontrukcje nie są jednak na ogół potrzebne, gdyż obiekty UserListElement
			wywołują odpowieni sygnał swoich UserList (konkretnie userDataChanged), który można
			podpiąć bezpośrednio pod taki sam slot klasy GaduProtocol, dzięki czemu każda
			zmiana zostanie automatycznie zauważona przez serwer.

			<code>
				GaduProtocol gadu;
				UserListElement *ule;
				UserList ul;

				...                                            // połaczenie odpowiednich slotów
				ule = &ul.byAltNick("AltNick");
				ule->setBlocking(true);                        // i wszystko robi się samo
			</code>

			@param oldData wskaźnik do starych danych kontatku
			@param newData wskaźnik do nowych danych konaktu
			@param massively jeżeli == true, to ta zmiana danych jest częścią większych zmian,
			       więc nie należy nic z nimi robić, wkrótce (albo już to zostało zrobione)
				   cała lista zostanie wysłana w jednej paczce
		**/
		void userDataChanged(const UserListElement* const oldData, const UserListElement* const newData, bool massively = false);

	signals:
		/**
			wiadomość została zablokowana przez serwer
		**/
		void messageBlocked(int seq, UinType uin);
		
		/**
			wiadomość dostarczono
		**/
		void messageDelivered(int seq, UinType uin);
		
		/**
			wiadomość zakolejkowano
		**/
		void messageQueued(int seq, UinType uin);
		
		/**
			skrzynka odbiorcza na serwerze jest pełna
		**/
		void messageBoxFull(int seq, UinType uin);
		
		/**
			wiadomość nie dostaczona (występuje tylko przy CTCP)
		**/
		void messageNotDelivered(int seq, UinType uin);
		
		/**
			wiadomość została przyjęta przez serwer 
		**/
		void messageAccepted(int seq, UinType uin);
		/**
			wiadomość została odrzucona przez serwer 
		**/
		void messageRejected(int seq, UinType uin);
		
		/**
			otrzymano informację o potwierdzeniu wiadomości
			
			@todo usunąć
			@see messageAccepted
			@see messageRejected

			@see messageBlocked
			@see messageDelivered
			@see messageQueued
			@see messageBoxFull
			@see messageNotDelivered
		**/
		void ackReceived(int seq);

		/**
			udało się zalogować
		**/
		void connected();

		/**
			rozpoczynamy procedurę logowania się
		**/
		void connecting();

		/**
			rozłączyliśmy się z serwerem
		**/
		void disconnected();

		/**
			wystąpił błąd połączenia
			@param err przyczyna
			@see connectionError
		**/
		void error(GaduError err);

		/**
			wystąpił błąd połączenia
			@param reason napis do wyświetlenia dla użytkownika
		**/
		void connectionError(const QString &reason);

		/**
			dostaliśmy prośbę o przysłanie obrazka
			@param sender od kogo
			@param size rozmiar pliku
			@param crc32 jego suma kontrolna obliczana przy pomocy crc32
		**/
		void imageRequestReceived(UinType sender, uint32_t size, uint32_t crc32);

		/**
			Otrzymano dane obrazka i zapisano go do pliku.
			@param sender od kogo
			@param size rozmiar pliku
			@param crc32 jego suma kontrolna obliczana przy pomocy crc32
			@param path ścieżka do zapisanego pliku
		**/
		void imageReceivedAndSaved(UinType sender, uint32_t size, uint32_t crc32, const QString &path);

		/**
			lista kontaktów została zmodyfikowana
		**/
		void userListChanged();

		/**
			dostaliśmy od serwera informację o zmianie statusu
			@param user kontakt, który właśnie zmienił status
			@param oldStatus stary startus
			@param onConnection czy zmiana związana jest z podłączeniem się do serwera
		**/
		void userStatusChanged(const UserListElement &user, const UserStatus &oldStatus, bool onConnection = false);

		/**
			dostaliśmy od serwera informację o zmianie statusu dla kontaktu,
			którego nie mamy na liście
		**/
		void userStatusChangeIgnored(UinType uin);

		/**
			otrzymana wiadomość systemową
			@param message treść wiadomości wraz z datą
		**/
		void systemMessageReceived(const QString &message);

		/**
			Otrzymano wiadomość CTCP.
			Ktoś nas prosi o połączenie dcc, ponieważ
			jesteśmy za NAT-em
			@param user wywołujący
			@todo zmienić nazwę
		**/
		void dccConnectionReceived(const UserListElement &user);

		/**
			martwy sygnał
			@todo usunąć
		**/
		void disconnectNetwork();

		/**
			otrzymano nowe wyniki wyszukiwania w katalogu publicznym
			@param searchResults wyniki
			@param seq numer sekwencyjny
			@param lastUin ?
		**/
		void newSearchResults(SearchResults& searchResults, int seq, int lastUin);

		/**
			operacja rejestracji nowego konta została zakończona
			@param ok powodzenie operacji
			@param uin nowy numer
			@see doRegisterAccount
		**/
		void registered(bool ok, UinType uin);

		/**
			operacja wyrejestrowania konta została zakończona
			@param ok powodzenie operacji
			@see doUnregisterAccount
		**/
		void unregistered(bool ok);

		/**
			operacja przypomnienia hasła została zakończona
			@param ok powodzenie operacji
			@see doRemindPassword
		**/
		void reminded(bool ok);

		/**
			operacja zmiany hasła została zakończona
			@param ok powodzenie operacji
			@see doChangePassword
		**/
		void passwordChanged(bool ok);

		/**
			operacja eksportu listy kontaktów na serwer została zakończona
			@param ok powodzenie operacji
			@see doExportUserList
		**/
		void userListExported(bool ok);

		/**
			operacja usunięcia listy kontaktów z serwera została zakończona
			@param ok powodzenie operacji
			@see doClearUserList
		**/
		void userListCleared(bool ok);

		/**
			operacja importu listy kontaktów z serwera została zakończona
			@param ok powodzenie operacji
			@param list jeżeli operacja się powiodła, to zaimportowana lista
			@see doImportUserList
		**/
		void userListImported(bool ok, UserList &list);

		/**
			Sygnał daje mozliwość operowania na wiadomości
			która przyszła z serwera jeszcze w jej oryginalnej
			formie przed konwersją na unicode i innymi zabiegami.
			Treść wiadomości można zmienić grzebiąc w buforze msg,
			ale uwaga: można zepsuć formatowanie tekstu zapisane
			w formats. Oczywiście je również można zmieniać według
			opisu protokołu GG ;)
			Można też przerwać dalszą obróbkę wiadomości ustawiając
			stop na true.
		**/
		void messageFiltering(const UinsList& senders,QCString& msg,
			QByteArray& formats,bool& stop);
		/**
			Otrzymano wiadomość którą trzeba pokazać (klasa chat lub msg,
			nadawca nie jest ignorowany, itp)
			Treść zdeszyfrowana i zdekodowana do unicode.
			Jeśli któryś ze slotów sygnału chatMsgReceived1 ustawi zmienną
			grab na true to sygnał chatReceived2 nie zostanie wygenerowany.
			Jeśli natomiast zmienna grab zostanie ustawiona przez slot
			chatMsgReceived0, to żadna czynność związana z obsługą tego
			zdarzenia nie zostanie podjęta (tj. wyświetlanie wiadomości
			w oknie, dodanie jej do historii, etc.), poza przekonwertowaniem
			kodowania wiadomości z CP1250 na Unicode.
		**/
		void chatMsgReceived0(UinsList senders, const QString& msg, time_t time, bool& grab);
		void chatMsgReceived1(UinsList senders, const QString& msg, time_t time, bool& grab);
		void chatMsgReceived2(UinsList senders, const QString& msg, time_t time);

		/**
			Wywoływane, gdy chcemy odczytać token z obrazka
		**/
		void needTokenValue(QPixmap in, QString &out);

};

extern GaduProtocol* gadu;

#endif
