
#ifndef SERVER_H
#define SERVER_H

#include "common.h"

#include "sockets.h"
#include "threads.h"
#include "datetime.h"
#include "protocol.h"

#include <hash_map>
#include <hash_set>
#include <list>
#include <utility>

// ################################################################################################

class acceptingThread;
class cleaningThread;
class mainThread;
class client;

// ################################################################################################

typedef std :: pair<unsigned long, unsigned long> id;

class server
{
private:
	friend class cleaningThread;
	friend class client;
	friend class acceptingThread;

	sockets :: serverSocket* socket_;

	acceptingThread* acceptor_;
	threads :: thread* acceptingThread_;

	cleaningThread* cleaner_;
	threads :: thread* cleaningThread_;

	mainThread* main_;
	threads :: thread* mainThread_;

	threads :: semaphore clientsLock;
	stdext :: hash_set<client*> unauthorizedClients;
	std :: list<client*> removingClients;
	std :: map<id, client*> clients;
	void moveFromUCToRemoving(client* c);
	void moveFromMapToRemoving(id userId);
	bool moveFromUCToMap(client* c, id userId);
	void addUnauthorizedClient(client* c);
	void dispatchMessage(id idUser, message* msg);
public:
	server(void);
	bool start(int port, int maxConnections);
	void stop(void);
	bool sendToClient(id idUser, char* msg);
    void closeClient(id idUser);
};

// ################################################################################################

class sender : public threads :: runnable
{
private:
	client* client_;
	threads :: semaphore workerSemaphore_;
	threads :: semaphore listLock_;
	std :: list<char*> messages_;
public:
	sender(client* c);
	void send(char* msg);
	void operator()(threads :: thread* thd);
};

// ################################################################################################

#define MAX_BAD_REQUEST_BEFORE_CLOSE      4

class client : public threads :: runnable
{
private:
	id userId_;
	sockets :: clientSocket* socket_;
	dv_time:: datetime lastActivity_;
	server* srv_;
	threads :: thread* ownThread_;
	sender* sender_;
	threads :: thread* senderThread_;
	int errCounter_;

	void dispatchMessage(message* msg);
	bool getMessage(char** result);
public:
	              client(sockets :: clientSocket* socket, server* srv);
	              ~client(void);
             void send(char* msg);
	         void operator()(threads :: thread* thd);
	         void startClient(void);
	         void close(void);
	         void waitForThreadExit(void);
    unsigned long getUserId  (void) { return userId_.first;        };
    unsigned long getServerId(void) { return userId_.second;       };

	friend class cleaningThread;
	friend class sender;
};

// ################################################################################################

class acceptingThread : public threads :: runnable
{
private:
	server* srv_;
	sockets :: serverSocket* socket_;
public:
	acceptingThread(server* srv, sockets :: serverSocket* socket);
	void operator()(threads :: thread* thd);
};

// ################################################################################################

#define CLEANING_THREAD_SEM_MAX_VALUE 16777216
class cleaningThread : public threads :: runnable
{
private:
	server* srv_;
	bool finishing_;
	dv_time:: datetime delta_;
    threads :: semaphore sem_;
public:
	cleaningThread(server* srv, dv_time:: datetime delta);
	void prepareFinish(void);
	void operator()(threads :: thread* thd);
    void notify();
};

// ################################################################################################

struct msgItem
{
	message* msg;
	id idUser;
};

// ################################################################################################

class mainThread : public threads :: runnable
{
private:
	server* srv_;

	threads :: semaphore workerSemaphore_;
	threads :: semaphore listLock_;
	std :: list<msgItem> messages_;
	bool finishing_;
public:
	mainThread(server* srv);
	void dispatchMessage(id idUser, message* msg);
	void prepareFinish(void);
	void operator()(threads :: thread* thd);
};

// ################################################################################################

id authorizeUser(sockets :: clientSocket* socket);

#define MAX_MESSAGE_LENGTH 65536
#define MAX_WORKER_QUEUE_ITEMS 16777216
#define CLEANING_THREAD_INTERVAL_MSEC 1000

// ################################################################################################

#endif