//! \~\file picloudserver.h
//! \~\ingroup Cloud
//! \~\brief
//! \~english Server-side PICloud device for one named endpoint
//! \~russian Серверное устройство PICloud для одной именованной конечной точки
/*
    PIP - Platform Independent Primitives
    PICloud Server
    Ivan Pelipenko peri4ko@yandex.ru, Andrey Bychkov work.a.b@yandex.ru

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef PICLOUDSERVER_H
#define PICLOUDSERVER_H

#include "picloudbase.h"
#include "piconditionvar.h"

//! \~\ingroup Cloud
//! \~\brief
//! \~english %PIIODevice implementation for a logical PICloud server.
//! \~russian Реализация %PIIODevice для логического сервера PICloud.
class PIP_CLOUD_EXPORT PICloudServer
	: public PIIODevice
	, public PICloudBase {
	PIIODEVICE(PICloudServer, "");

public:
	//! \~english Constructs a logical server for transport endpoint "path" and mode "mode".
	//! \~russian Создает логический сервер для транспортной точки "path" и режима "mode".
	explicit PICloudServer(const PIString & path = PIString(), PIIODevice::DeviceMode mode = PIIODevice::ReadWrite);

	//! \~english Destroys the server and closes all logical clients.
	//! \~russian Уничтожает сервер и закрывает всех логических клиентов.
	virtual ~PICloudServer();


	//! \~\ingroup Cloud
	//! \~\brief
	//! \~english Per-client %PIIODevice exposed by %PICloudServer.
	//! \~russian Клиентское %PIIODevice, предоставляемое %PICloudServer.
	//! \~\details
	//! \~english Instances are created by \a newConnection() and represent one logical cloud client.
	//! \~russian Экземпляры создаются через \a newConnection() и представляют одного логического облачного клиента.
	class Client: public PIIODevice {
		PIIODEVICE(PICloudServer::Client, "");
		friend class PICloudServer;

	public:
		//! \~english Constructs a wrapper for logical client "id" owned by server "srv".
		//! \~russian Создает обертку для логического клиента "id", принадлежащего серверу "srv".
		Client(PICloudServer * srv = nullptr, uint id = 0);

		//! \~english Destroys the client wrapper.
		//! \~russian Уничтожает клиентскую обертку.
		virtual ~Client();

	protected:
		bool openDevice() override;
		bool closeDevice() override;
		ssize_t readDevice(void * read_to, ssize_t max_size) override;
		ssize_t writeDevice(const void * data, ssize_t size) override;
		DeviceInfoFlags deviceInfoFlags() const override { return PIIODevice::Reliable; }
		ssize_t bytesAvailable() const override { return buff.size(); }
		void interrupt() override;

	private:
		void pushBuffer(const PIByteArray & ba);
		PICloudServer * server;
		uint client_id;
		PIByteArray buff;
		PIMutex mutex_buff;
		PIConditionVariable cond_buff;
		std::atomic_bool is_connected;
	};

	//! \~english Sets the logical server name announced by this server.
	//! \~russian Устанавливает логическое имя сервера, объявляемое этим сервером.
	void setServerName(const PIString & server_name);

	//! \~english Returns a snapshot of the currently connected logical clients.
	//! \~russian Возвращает снимок текущих подключенных логических клиентов.
	PIVector<PICloudServer::Client *> clients() const;


	//! \events
	//! \{

	//! \fn void newConnection(PICloudServer::Client * client)
	//! \~english Raised when a new logical client appears for this server name.
	//! \~russian Вызывается, когда для этого имени сервера появляется новый логический клиент.
	EVENT1(newConnection, PICloudServer::Client *, client);

	//! \}

protected:
	bool openDevice() override;
	bool closeDevice() override;
	ssize_t readDevice(void * read_to, ssize_t max_size) override;
	ssize_t writeDevice(const void * data, ssize_t max_size) override;
	void interrupt() override;

private:
	EVENT_HANDLER1(void, _readed, PIByteArray &, ba);
	EVENT_HANDLER1(void, clientDeleted, PIObject *, o);
	void clientDisconnect(uint client_id);
	int sendData(const PIByteArray & data, uint client_id);

	PIVector<Client *> clients_;
	PIVector<Client *> removed_clients_;
	PIMap<uint, Client *> index_clients;
	PIThread ping_timer;
	PIConditionVariable cvar;
	PIMutex open_mutex;
	mutable PIMutex clients_mutex;
	std::atomic_bool is_deleted;
};

#endif // PICLOUDSERVER_H
