#include <Core/Core.h>
#include <Web/SSL/WebSSL.h>

using namespace Upp;

struct client_data: Moveable<client_data> {
	Socket sock;
	dword ip;
	int par1;
	int par2;
};


class TestServer{

	Vector<client_data> clients;
	
	int WaitForInput(int _timeout_ms);
	void Worker();
public:
	typedef TestServer CLASSNAME;
	void Start();
};


int TestServer::WaitForInput(int _timeout_ms)
{
	fd_set set;
	struct timeval tval;
	int max = 0;
	
	if (clients.GetCount() <= 0) return -1;
	
	tval.tv_sec = _timeout_ms / 1000;
	tval.tv_usec = 1000 * (_timeout_ms % 1000);
	
	FD_ZERO(&set);
	for (int i = 0; i < clients.GetCount(); i++){
		int tmp = clients[i].sock.GetSocket();
		if (tmp > max) max = tmp;
		FD_SET(tmp, &set);
	}
	
	if (select(max+1, &set, NULL, NULL, &tval) > 0){
		for (int i = 0; i < clients.GetCount(); i++){		
			if (FD_ISSET(clients[i].sock.GetSocket(), &set)) return i;
		}
	}
	
	return -1;
}


void TestServer::Worker()
{
	while(!Thread::IsShutdownThreads())
	{
		int ci = WaitForInput(1000);
		if (ci >= 0){
			if (!clients[ci].sock.IsOpen() || clients[ci].sock.IsError() || clients[ci].sock.IsEof()){
				Cout() << "Client no " << ci << " (" << FormatIP(clients[ci].ip) << ") closed connection\n";
				clients[ci].sock.Close();
				clients.Remove(ci);
			}else{
				Cout() << "[" << FormatIP(clients[ci].ip) << "] " << clients[ci].sock.Read() << "\n" ;
			}
		}else{
			for (int i = 0; i < clients.GetCount(); i++){
				clients[i].sock.Write(Format("%`: message to client no %d", GetSysTime(), i));
			}
		}
	}
}


void TestServer::Start()
{
	Socket server;
#if 1
	SSLContext context;

	if (!context.Create(SSLv3_server_method())){
		Cout() << "Can not create context\n";
		return;
	}

	if (!context.UseCertificate(LoadFile(ConfigFile("servercert.pem")), LoadFile(ConfigFile("serverkey.pem")), false)){
		Cout() << "Certificate and key are diffrent!\n";
		return;
	}

	if(!SSLServerSocket(server, context, 11111, true, 5, true)){
#else
	if(!ServerSocket(server, 11111)){
#endif
		Cout() << "Can not start server\n";
		return;
	}
	
	Thread().Run(THISBACK(Worker));

	Cout() << "Waiting for connections\n";

	while(!Thread::IsShutdownThreads()){
		client_data new_client;
		if (server.Accept(new_client.sock, &new_client.ip)){
			Cout() << "Client no " << clients.GetCount() << " from " << FormatIP(new_client.ip) << "\n";		
			clients.Add(new_client);
		}else{
			Cout() << "+\n";
		}
	}
}

CONSOLE_APP_MAIN
{
	TestServer().Start();
}

