/*
    PIP - Platform Independent Primitives
    Ivan Pelipenko peri4ko@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/>.
*/

#include "piclientserver_server.h"

#include "piclientserver_client.h"
#include "piethernet.h"
#include "pitranslator.h"


PIClientServer::Server::Server() {
	tcp_server     = new PIEthernet(PIEthernet::TCP_Server);
	clean_thread   = new PIThread();
	client_factory = [] { return new ServerClient(); };
	CONNECTL(tcp_server, newConnection, [this](PIEthernet * c) {
		PIMutexLocker guard(clients_mutex);
		if (clients.size_s() >= max_clients) {
			piCout << "Server::newConnection overflow clients count"_tr("PIClientServer");
			delete c;
			return;
		}
		auto sc = client_factory();
		if (!sc) {
			piCout << "ClientFactory returns nullptr!"_tr("PIClientServer");
			delete c;
			return;
		}
		sc->createForServer(this, c);
		newClient(sc);
	});

	clean_thread->start([this]() {
		clean_notifier.wait();
		PIVector<ServerClient *> to_delete;
		clients_mutex.lock();
		for (auto c: clients) {
			const PIEthernet * eth = c->getTCP();
			if (!eth) continue;
			if (eth->isConnected()) continue;
			c->can_write = false;
			to_delete << c;
		}
		for (auto c: to_delete)
			clients.removeOne(c);
		clients_mutex.unlock();
		for (auto c: to_delete) {
			c->aboutDelete();
			c->destroy();
			delete c;
		}
	});
}


PIClientServer::Server::~Server() {
	clean_thread->stop();
	clean_notifier.notify();
	clean_thread->waitForFinish();
	piDeleteSafety(clean_thread);
	stopServer();
	closeAll();
	piDeleteSafety(tcp_server);
}


void PIClientServer::Server::listen(PINetworkAddress addr) {
	if (!tcp_server) return;
	stopServer();
	is_closing = false;
	tcp_server->listen(addr, true);
	// piCout << "Listen on" << addr.toString();
}


void PIClientServer::Server::closeAll() {
	clients_mutex.lock();
	for (auto c: clients) {
		c->aboutDelete();
		c->destroy();
		delete c;
	}
	clients.clear();
	clients_mutex.unlock();
}


void PIClientServer::Server::setMaxClients(int new_max_clients) {
	max_clients = new_max_clients;
}


int PIClientServer::Server::clientsCount() const {
	PIMutexLocker guard(clients_mutex);
	return clients.size_s();
}


void PIClientServer::Server::forEachClient(std::function<void(ServerClient *)> func) {
	PIMutexLocker guard(clients_mutex);
	for (auto * c: clients) {
		func(c);
		if (is_closing) break;
	}
}


void PIClientServer::Server::stopServer() {
	if (!tcp_server) return;
	is_closing = true;
	tcp_server->stopThreadedListen();
	tcp_server->stopAndWait();
}


void PIClientServer::Server::newClient(ServerClient * c) {
	clients << c;
	c->setConfiguration(configuration());
	c->tcp->startThreadedRead();
	c->connected();
}


void PIClientServer::Server::clientDisconnected(ServerClient * c) {
	clean_notifier.notify();
}
