#include "SpielServer.h"
#include <iostream>
#include <Zeit.h>
#include <Klient.h>
#include <Globals.h>
#ifdef WIN32
#include "..\Datenbank\Datenbank.h"
#else
#include "Datenbank.h"
#endif

// Inhalt der SpielServer Klasse aus SpielServer.h
// Konstruktor 
SpielServer::SpielServer( InitDatei *zIni )
: Thread()
{
	Network::Start( 100 );
	std::cout << "SS: Verbindung mit Datenbank wird hergestellt...\n";
	db = new SSDatenbank( zIni );
	empfangen = 0;
	gesendet = 0;
	fehler = new Text();
	ini = zIni->getThis();
	id = *zIni->zWert( "ServerId" );
	server = new Server();
	aServer = new SSLServer();
    aServer->setPrivateKeyPassword( zIni->zWert( "SSLPasswort" )->getText() );
    aServer->setCertificateFile( zIni->zWert( "SSLCert" )->getText() );
    aServer->setPrivateKeyFile( zIni->zWert( "SSLKey" )->getText() );
	std::cout << "SS: Starten des Admin Servers...\n";
	if( !aServer->verbinde( (unsigned short)TextZuInt( ini->zWert( "AdminServerPort" )->getText(), 10 ), 10 ) )
	{
		std::cout << "SS: Der Admin Server konnte nicht gestartet werden. Das Programm wird beendet.\n";
		exit( 1 );
	}
	db->setServerStatus( id, 2 );
	end = 0;
	nichtPausiert = 0;
	InitializeCriticalSection( &cs );
	ref = 1;
    startSpielThread = new SpielFinden( this );
	if( zIni->zWert( "Aktiv" )->istGleich( "TRUE" ) )
	{
		serverStarten();
		serverFortsetzen();
	}
	erstellt = new RCArray< SpielErstellt >();
	spiele = new RCArray< Spiel >();
	klients = new RCArray< SSKlient >();
	spielHistorieId = new Array< int >();
	spielErstelltAnzahl = 0;
	spielAnzahl = 0;
	klientAnzahl = 0;
	klientAnzahl2 = 0;
	historieS = new HistorieSender( this );
	historieS->start();
}

// Destruktor 
SpielServer::~SpielServer()
{
	historieS->setEnde();
	historieS->warteAufThread( 20000 );
	historieS->ende();
	historieS->release();
	fehler->release();
	server->trenne();
	server->release();
	aServer->trenne();
	aServer->release();
	ini->release();
	db->release();
	DeleteCriticalSection( &cs );
	startSpielThread->release();
	erstellt->release();
	spiele->release();
	if( klients )
		klients->release();
	spielHistorieId->release();
}

// nicht constant
void SpielServer::lock()
{
	EnterCriticalSection( &cs );
}

void SpielServer::unlock()
{
	LeaveCriticalSection( &cs );
}

void SpielServer::runn()
{
	while( !end && aServer->isConnected() )
	{
		SSLSKlient *klient;
		klient = aServer->getKlient();
		if( end && klient )
		{
			klient->trenne();
			klient = klient->release();
			Sleep( 1000 );
			return;
		}
		if( !klient )
			continue;
		SSAKlient *slHandle = new SSAKlient( klient, getThis() );
		slHandle->start();
	}
}

void SpielServer::thread()
{
	while( server->isConnected() )
	{
		SKlient *klient;
		klient = server->getKlient();
		if( !klient )
			continue;
		Framework::getThreadRegister()->cleanUpClosedThreads();
		SSKlient *slHandle = new SSKlient( klient, getThis() );
		klientAnzahl2++;
		slHandle->start();
	}
}

void SpielServer::close()
{
	db->setServerStatus( id, 1 );
	server->trenne();
#ifdef WIN32
	warteAufThread( 1000 );
#endif
	lock();
	for( int i = 0; i < klientAnzahl; i++ )
		klients->z( i )->absturz();
	if( klients )
		 klients = klients->release();
	klientAnzahl = 0;
	unlock();
	ende();
	run = 0;
	end = 1;
	Klient *klient = new Klient();
	klient->verbinde( aServer->getPort(), "127.0.0.1" );
	Sleep( 500 );
	aServer->trenne();
	klient->release();
}

bool SpielServer::serverStarten()
{
	if( nichtPausiert )
	{
		fehler->setText( "Der Server konnte nicht gestartet werden: Der Server läuft bereits." );
		return 0;
	}
	if( server )
		server->release();
	server = new Server();
	if( server->verbinde( (unsigned short)TextZuInt( ini->zWert( "ServerPort" )->getText(), 10 ), 10 ) )
	{
		nichtPausiert = 1;
		start();
		startSpielThread->setSpielServerId( id );
		startSpielThread->start();
		return 1;
	}
	else
	{
		serverBeenden();
		fehler->setText( "Der Server konnte nicht gestartet werden: Eventuell ist der Port in benutzung." );
		return 0;
	}
}

bool SpielServer::serverPause()
{
	if( !nichtPausiert )
	{
		fehler->setText( "Der Server konnte nicht pausiert werden: Der Server läuft nicht." );
		return 0;
	}
	if( !db->setServerStatus( id, 2 ) )
	{
		fehler->setText( "Der Server konnte nicht pausiert werden: " );
		fehler->append( db->getLetzterFehler() );
		return 0;
	}
	return 1;
}

bool SpielServer::serverFortsetzen()
{
	if( !nichtPausiert )
	{
		fehler->setText( "Der Server konnte nicht fortgesetzt werden: Der Server läuft nicht." );
		return 0;
	}
	if( !db->setServerStatus( id, 3 ) )
	{
		fehler->setText( "Der Server konnte nicht fortgesetzt werden: " );
		fehler->append( db->getLetzterFehler() );
		return 0;
	}
	return 1;
}

bool SpielServer::serverBeenden()
{
	if( !nichtPausiert )
	{
		fehler->setText( "Der Server konnte nicht beendet werden: Der Server läuft nicht." );
		return 0;
	}
	if( db->serverIstNichtPausiert( id ) )
	{
		fehler->setText( "Der Server konnte nicht beendet werden: Der Server muss erst pausiert werden." );
		return 0;
	}
	startSpielThread->setEnde();
	startSpielThread->warteAufThread( 2000 );
	startSpielThread->ende();
	nichtPausiert = 0;
	ende();
	if( server )
		server->trenne();
	return 1;
}

bool SpielServer::setMaxSpiele( int ms )
{
	if( !db->setMaxSpiele( id, ms ) )
	{
		fehler->setText( "Die maximale Anzahl der Spiele konnte nicht gesetzt werden:\n" );
		fehler->append( db->getLetzterFehler() );
		return 0;
	}
	ini->setWert( "MaxSpiele", Text() += ms );
	return 1;
}

void SpielServer::addSpielErstellt( SpielErstellt *erstellt )
{
	lock();
	this->erstellt->set( erstellt, spielErstelltAnzahl );
	spielErstelltAnzahl++;
	unlock();
}

void SpielServer::SpielErstelltAbbruch( int spielErstelltId )
{
	lock();
	for( int i = 0; i < spielErstelltAnzahl; i++ )
	{
		if( erstellt->z( i )->getId() == spielErstelltId )
		{
			erstellt->z( i )->abbruch();
			break;
		}
	}
	unlock();
}

void SpielServer::removeSpielErstellt( int id )
{
	lock();
	bool gefunden = 0;
	for( int i = 0; i < spielErstelltAnzahl; i++ )
	{
		if( erstellt->z( i )->getId() == id )
		{
			spielErstelltAnzahl--;
			SpielErstellt *zse = erstellt->get( i );
			erstellt->remove( i );
			unlock();
			zse->release();
			gefunden = 1;
			break;
		}
	}
	if( !gefunden )
		unlock();
}

bool SpielServer::registerKlient( SSKlient *klient )
{
	lock();
	bool gefunden = 0;
	for( int i = 0; i < klientAnzahl; i++ )
	{
		SSKlient *tmp = klients->z( i );
		if( tmp )
		{
			if( tmp->getAccountId() == klient->getAccountId() )
			{
				klient->getThis();
				tmp->setSenden( klient );
				klient->release();
				gefunden = 1;
				unlock();
				return 1;
			}
		}
	}
	if( !gefunden )
	{
		klients->set( klient, klientAnzahl );
		klientAnzahl++;
	}
	unlock();
	return 0;
}

bool SpielServer::absturzKlient( int klientId )
{
	lock();
	SSKlient *zKlient = 0;
	for( int i = 0; i < klientAnzahl; i++ )
	{
		if( klients->z( i )->getKlientNummer() == klientId )
		{
			zKlient = klients->z( i );
			break;
		}
	}
	if( !zKlient )
	{
		unlock();
		return 0;
	}
	int id = 0;
	int ak = db->getAktionForKlient( this->id, zKlient->getAccountId(), &id );
	switch( ak )
	{
	case 0: // Information Über Verwendung des Klient ist verlorengegangen (überall abmelden)
		for( int i = 0; i < spielErstelltAnzahl; i++ )
		{
			SpielErstellt *tmp = erstellt->z( i );
			if( tmp )
				tmp->klientGetrennt( zKlient );
		}
		for( int i = 0; i < spielAnzahl; i++ )
		{
			Spiel *tmp = spiele->z( i );
			if( tmp )
				tmp->klientGetrennt( zKlient );
		}
		break;
	case 1: // Spiel Erstellt
		for( int i = 0; i < spielErstelltAnzahl; i++ )
		{
			SpielErstellt *tmp = erstellt->z( i );
			if( tmp )
			{
				if( tmp->getId() == id )
				{
					tmp->klientGetrennt( zKlient );
					break;
				}
			}
		}
		break;
	case 2: // Spielt
		for( int i = 0; i < spielAnzahl; i++ )
		{
			Spiel *tmp = spiele->z( i );
			if( tmp && tmp->getId() == id )
			{
				tmp->klientGetrennt( zKlient );
				break;
			}
		}
		break;
	}
	bool gefunden = 0;
	for( int i = 0; i < klientAnzahl; i++ )
	{
		if( klients->z( i ) == zKlient )
		{
			zKlient->absturz();
			klientAnzahl--;
			zKlient->getThis();
			klients->remove( i );
			unlock();
			zKlient->release();
			gefunden = 1;
			break;
		}
	}
	if( !gefunden )
		unlock();
	return 1;
}

bool SpielServer::verteileKlient( SSKlient *klient )
{
	lock();
	int id = 0;
	int ak = db->getAktionForKlient( this->id, klient->getAccountId(), &id );
	switch( ak )
	{
	case 0: // Keine Verwendung für den Klient
		unlock();
		return 0;
		break;
	case 1: // Spiel Erstellt
		for( int i = 0; i < spielErstelltAnzahl; i++ )
		{
			SpielErstellt *tmp = erstellt->z( i );
			if( tmp )
			{
				if( tmp->getId() == id )
				{
					tmp->klientVerbunden( (SSKlient*)klient->getThis() );
					unlock();
					return 1;
				}
			}
		}
		break;
	case 2: // Spielt
		for( int i = 0; i < spielAnzahl; i++ )
		{
			Spiel *tmp = spiele->z( i );
			if( tmp )
			{
				if( tmp->getId() == id )
				{
					tmp->klientVerbunden( (SSKlient*)klient->getThis() );
					unlock();
					return 1;
				}
			}
		}
		break;
	}
	unlock();
	return 0;
}

void SpielServer::unregisterKlient( SSKlient *zKlient )
{
	lock();
	int id = 0;
	int ak = db->getAktionForKlient( this->id, zKlient->getAccountId(), &id );
	switch( ak )
	{
	case 0: // Information Über Verwendung des Klient ist verlorengegangen (überall abmelden)
		for( int i = 0; i < spielErstelltAnzahl; i++ )
		{
			SpielErstellt *tmp = erstellt->z( i );
			if( tmp )
				tmp->klientGetrennt( zKlient );
		}
		for( int i = 0; i < spielAnzahl; i++ )
		{
			Spiel *tmp = spiele->z( i );
			if( tmp )
				tmp->klientGetrennt( zKlient );
		}
		break;
	case 1: // Spiel Erstellt
		for( int i = 0; i < spielErstelltAnzahl; i++ )
		{
			SpielErstellt *tmp = erstellt->z( i );
			if( tmp )
			{
				if( tmp->getId() == id )
				{
					tmp->klientGetrennt( zKlient );
					break;
				}
			}
		}
		break;
	case 2: // Spielt
		for( int i = 0; i < spielAnzahl; i++ )
		{
			Spiel *tmp = spiele->z( i );
			if( tmp && tmp->getId() == id )
			{
				tmp->klientGetrennt( zKlient );
				break;
			}
		}
		break;
	}
	bool gefunden = 0;
	for( int i = 0; i < klientAnzahl; i++ )
	{
		if( klients->z( i ) == zKlient )
		{
			klientAnzahl--;
			zKlient->getThis();
			klients->remove( i );
			unlock();
			zKlient->release();
			gefunden = 1;
			break;
		}
	}
	if( !gefunden )
	{
		unlock();
		zKlient->release();
	}
}

void SpielServer::klientsMM( SSKlient *k )
{
	lock();
	for( int i = 0; i < klientAnzahl; i++ )
	{
		if( klients->z( i ) == k )
		{
			klients->z( i )->getThis();
			klients->z( i )->getThis();
			klients->remove( i );
		}
	}
	unlock();
	klientAnzahl2--;
}

void SpielServer::addSpiel( Spiel *spiel )
{
	lock();
	spiele->add( spiel, spielAnzahl );
	spielAnzahl++;
	unlock();
}

void SpielServer::removeSpiel( int spielId )
{
	lock();
	bool gefunden = 0;
	for( int i = 0; i < spielAnzahl; i++ )
	{
		if( spiele->z( i ) && spiele->z( i )->getId() == spielId )
		{
			spielAnzahl--;
			Spiel *zs = spiele->get( i );
			spiele->remove( i );
			unlock();
			zs->release();
			gefunden = 1;
			break;
		}
	}
	if( !gefunden )
		unlock();
}

void SpielServer::addGesendet( int bytes )
{
	gesendet += bytes;
}

void SpielServer::addEmpfangen( int bytes )
{
	empfangen += bytes;
}

void SpielServer::addSpielHistorieId( int id )
{
	lock();
	spielHistorieId->add( id );
	unlock();
}

bool SpielServer::hatSpielHistorieId( int id )
{
	bool ret = 0;
	lock();
	int anz = spielHistorieId->getEintragAnzahl();
	for( int i = 0; i < anz; i++ )
	{
		if( spielHistorieId->hat( i ) && spielHistorieId->get( i ) == id )
		{
			ret = 1;
			break;
		}
	}
	unlock();
	return ret;
}

void SpielServer::removeSpielHistorieId( int id )
{
	lock();
	int anz = spielHistorieId->getEintragAnzahl();
	for( int i = 0; i < anz; i++ )
	{
		if( spielHistorieId->hat( i ) && spielHistorieId->get( i ) == id )
		{
			spielHistorieId->remove( i );
			break;
		}
	}
	unlock();
}

// constant
bool SpielServer::istAn() const
{
	return db->serverIstNichtPausiert( id );
}

Server *SpielServer::zServer() const
{
	return server;
}

SSDatenbank *SpielServer::zDB() const
{
	return db;
}

InitDatei *SpielServer::zInit() const
{
    return ini;
}

bool SpielServer::hatClients() const
{
	return klientAnzahl > 0;
}

int SpielServer::getId() const
{
	return id;
}

char *SpielServer::getLetzterFehler() const
{
	return fehler->getText();
}

char *SpielServer::getIp() const
{
	return ini->zWert( "ServerIp" )->getText();
}

int SpielServer::getPort() const
{
	if( !ini->wertExistiert( "ServerPort" ) )
		return 0;
	return TextZuInt( ini->zWert( "ServerPort" )->getText(), 10 );
}

// Reference Counting
SpielServer *SpielServer::getThis()
{
	ref++;
	return this;
}

SpielServer *SpielServer::release()
{
	ref--;
	if( !ref )
		delete this;
	return 0;
}


// Inhalt der SSAKlient Klasse aus SpielServer.h
// Konstruktor 
SSAKlient::SSAKlient( SSLSKlient *klient, SpielServer *ss )
: Thread()
{
	this->klient = klient;
	name = new Text( "" );
	passwort = new Text( "" );
	adminId = 0;
	version = 0;
	this->ss = ss;
}

// Destruktor 
SSAKlient::~SSAKlient()
{
	klient->trenne();
	klient->release();
	ss->release();
	name->release();
	passwort->release();
}

// nicht constant 
void SSAKlient::thread()
{
	while( 1 )
	{
		char c = 0;
		if( !klient->getNachricht( &c, 1 ) )
			break;
		else
		{
			bool br = 0;
			switch( c )
			{
			case 1: // Login
				if( 1 )
				{
					klient->sende( "\1", 1 );
					unsigned char nLen = 0;
					klient->getNachricht( (char*)&nLen, 1 );
					char *n = new char[ nLen + 1 ];
					n[ (int)nLen ] = 0;
					if( nLen )
						klient->getNachricht( n, nLen );
					unsigned char pLen = 0;
					klient->getNachricht( (char*)&pLen, 1 );
					char *p = new char[ pLen + 1 ];
					p[ (int)pLen ] = 0;
					if( pLen )
						klient->getNachricht( p, pLen );
					int adminId = ss->zDB()->istAdministrator( n, p );
					if( adminId )
					{
						klient->sende( "\1", 1 );
						name->setText( n );
						passwort->setText( p );
						this->adminId = adminId;
					}
					else
						errorZuKlient( "Falsche Kombination aus Name und Passwort." );
					delete[] n;
					delete[] p;
				}
				break;
			case 2: // Logout
				adminId = 0;
				name->setText( "" );
				passwort->setText( "" );
				klient->sende( "\1", 1 );
				break;
			case 3: // Trennen
				br = 1;
				klient->sende( "\1", 1 );
				break;
			case 4: // Server starten
				if( !adminId )
					errorZuKlient( "Du musst dich einloggen." );
				else
				{
					if( ss->zDB()->adminHatRecht( adminId, Admin_Recht::SSStarten ) )
					{
						if( !ss->serverStarten() )
						{
							Text *err = new Text();
							err->append( ss->getLetzterFehler() );
							errorZuKlient( err->getText() );
							err->release();
						}
						else
							klient->sende( "\1", 1 );
					}
					else
						errorZuKlient( "Du bist nicht berechtigt den Server zu starten." );
				}
				break;
			case 5: // Server beenden
				if( !adminId )
					errorZuKlient( "Du musst dich einloggen." );
				else
				{
					if( ss->zDB()->adminHatRecht( adminId, Admin_Recht::SSBeenden ) )
					{
						if( ss->serverBeenden() )
							klient->sende( "\1", 1 );
						else
						{
							Text *err = new Text();
							err->append( ss->getLetzterFehler() );
							errorZuKlient( err->getText() );
							err->release();
						}
					}
					else
						errorZuKlient( "Du bist nicht berechtigt den Server zu beenden." );
				}
				break;
			case 6: // Programm Schließen
				if( !adminId )
					errorZuKlient( "Du musst dich einloggen." );
				else
				{
					bool ok = 0;
					if( ss->isRunning() )
					{
						if( ss->zDB()->adminHatRecht( adminId, Admin_Recht::SSBeenden ) )
						{
							if( ss->serverBeenden() )
								ok = 1;
							else
							{
								Text *err = new Text();
								err->append( ss->getLetzterFehler() );
								errorZuKlient( err->getText() );
								err->release();
							}
						}
						else
							errorZuKlient( "Du bist nicht berechtigt den Server zu beenden." );
					}
					else
						ok = 1;
					if( ok && ss->hatClients() )
					{
						errorZuKlient( "Es sind noch Klients Online. Bitte versuche es später erneut." );
						break;
					}
					if( ok )
					{
						klient->sende( "\1", 1 );
						std::cout << "SS: Der Server wird von Benutzer " << adminId << " heruntergefahren.\n";
						ss->close();
						br = 1;
					}
				}
				break;
			case 7: // Progtamm abstürzen
				if( !adminId )
					errorZuKlient( "Du musst dich einloggen." );
				else
				{
					bool ok = 0;
					if( ss->isRunning() )
					{
						if( ss->zDB()->adminHatRecht( adminId, Admin_Recht::SSBeenden ) )
						{
							ss->serverBeenden();
							ok = 1;
						}
						else
							errorZuKlient( "Du bist nicht berechtigt den Server zu beenden." );
					}
					else
						ok = 1;
					if( ok )
					{
						klient->sende( "\1", 1 );
						std::cout << "sS: Der Server wurde von Benutzer " << adminId << " terminiert.\n";
						ss->close();
						br = 1;
					}
				}
				break;
			case 8: // Status Frage
				if( 1 )
				{
					char status = 0;
					if( ss->isRunning() )
					{
						status = 1;
						if( ss->istAn() )
							status = 2;
					}
					klient->sende( "\1", 1 );
					klient->sende( &status, 1 );
				}
				break;
			case 9: // Server pausieren
				if( !adminId )
					errorZuKlient( "Du musst dich einloggen." );
				else
				{
					klient->sende( "\1", 1 );
					char pause = 0;
					klient->getNachricht( &pause, 1 );
					if( ss->zDB()->adminHatRecht( adminId, Admin_Recht::SSPausieren ) )
					{
						bool ok = 0;
						if( pause )
							ok = ss->serverPause();
						else
							ok = ss->serverFortsetzen();
						if( ok )
							klient->sende( "\1", 1 );
						else
						{
							Text *err = new Text();
							err->append( ss->getLetzterFehler() );
							errorZuKlient( err->getText() );
							err->release();
						}
					}
					else
					{
						if( pause )
							errorZuKlient( "Du bist nicht berechtigt den Server zu pausieren." );
						else
							errorZuKlient( "Du bist nicht berechtigt den Server fortzusetzen." );
					}
				}
				break;
			case 0xA: // maximale Anzahl der Spiele setzen
				if( !adminId )
					errorZuKlient( "Du musst dich einloggen." );
				else
				{
					klient->sende( "\1", 1 );
					int maxS = 0;
					klient->getNachricht( (char*)&maxS, 4 );
					if( ss->zDB()->adminHatRecht( adminId, Admin_Recht::SSMSChange ) )
					{
						if( ss->setMaxSpiele( maxS ) )
							klient->sende( "\1", 1 );
						else
						{
							Text *err = new Text();
							err->append( ss->getLetzterFehler() );
							errorZuKlient( err->getText() );
							err->release();
						}
					}
					else
						errorZuKlient( "Du bist nicht berechtigt die maximale Anzahl der Clients zu verändern." );
				}
				break;
			case 0xB: // Spiel Historie downloaden
				if( adminId )
					errorZuKlient( "Zugriff verweigert." );
				else
				{
					klient->sende( "\1", 1 );
					int spielId = 0;
					klient->getNachricht( (char*)&spielId, 4 );
					if( !ss->hatSpielHistorieId( spielId ) )
					{
						errorZuKlient( "Zugriff auf Spiel-Historie verweigert." );
						break;
					}
					Text *pfad = new Text( "../spiel log/" );
					*pfad += spielId;
					*pfad += "/fertig";
					if( !DateiExistiert( *pfad ) )
					{
						pfad->release();
						errorZuKlient( "Die Spiel-Historie wurde nicht gefunden." );
						break;
					}
					klient->sende( "\1", 1 );
					pfad->remove( "/fertig" );
					sendeVerzeichnisAnKlient( *pfad, pfad->getLength() + 1 );
					pfad->release();
					klient->sende( "\0", 1 );
				}
				break;
            case 0xC: // klient absturtz
                if( 1 )
                {
                    klient->sende( "\1", 1 );
                    int klientId = 0;
                    klient->getNachricht( (char*)&klientId, 4 );
                    if( klientId && ss->absturzKlient( klientId ) )
                        klient->sende( "\1", 1 );
                    else
                        klient->sende( "\0", 1 );
                }
                break;
			default:
				errorZuKlient( "Unbekannte Nachricht!" );
				break;
			}
			if( br )
				break;
			ss->addEmpfangen( klient->getDownloadBytes( 1 ) );
			ss->addGesendet( klient->getUploadBytes( 1 ) );
		}
	}
	ss->addEmpfangen( klient->getDownloadBytes( 1 ) );
	ss->addGesendet( klient->getUploadBytes( 1 ) );
	delete this;
}

void SSAKlient::sendeVerzeichnisAnKlient( char *pfad, int gpl )
{
	Datei *d = new Datei();
	d->setDatei( pfad );
	if( d->istOrdner() )
	{
		int dAnz = d->getUnterdateiAnzahl();
		RCArray< Text > *list = d->getDateiListe();
		for( int i = 0; i < dAnz; i++ )
		{
			if( list->z( i ) )
			{
				Text *pf = new Text( pfad );
				pf->append( "/" );
				pf->append( list->z( i )->getText() );
				sendeVerzeichnisAnKlient( *pf, gpl );
				pf->release();
			}
		}
		list->release();
	}
	else if( d->existiert() )
	{
		__int64 gr = d->getSize();
		if( gr && d->zPfad()->getLength() - gpl > 0 && d->open( Datei::Style::lesen ) )
		{
			char pfLen = (char)( d->zPfad()->getLength() - gpl );
			klient->sende( &pfLen, 1 );
			klient->sende( d->zPfad()->getText() + gpl, pfLen );
			klient->sende( (char*)&gr, 8 );
			char *bytes = new char[ 2048 ];
			while( gr )
			{
				short len = gr > 2048 ? 2048 : (short)gr;
				d->lese( bytes, len );
				klient->sende( bytes, len );
				gr -= len;
			}
			delete[] bytes;
			d->close();
		}
	}
	d->release();
}

void SSAKlient::errorZuKlient( const char *nachricht ) const // sendet eine Fehlernachricht zum Klient
{
	klient->sende( "\3", 1 );
	char len = (char)textLength( nachricht );
	klient->sende( &len, 1 );
	klient->sende( nachricht, len );
}


// Inhalt der SSKlient Klasse aus SpielServer.h
// Konstruktor 
SSKlient::SSKlient( SKlient *klient, SpielServer *ss )
: SSKlientV()
{
	empfangen = klient;
	unsigned char key[ 20 ] = { 253, 234, 211, 132, 121, 230, 95, 145, 201, 13, 43, 77, 153, 223, 253, 69, 234, 43, 52, 99 };
	klient->setSendeKey( (char*)key, 20 );
	klient->setEmpfangKey( (char*)key, 20 );
	senden = 0;
	this->ss = ss;
	accountId = 0;
	klientNummer = 0;
	termThread = 1;
	verbunden = 0;
	zErstellt = 0;
	zSpiel = 0;
	InitializeCriticalSection( &cs );
	ref = 1;
}

// Destruktor 
SSKlient::~SSKlient()
{
	lock();
	unlock();
	if( verbunden )
		trenne();
	if( termThread )
	{
		if( verbunden )
			warteAufThread( 1000 );
		if( verbunden )
		{
			if( empfangen )
				empfangen->trenne();
			if( senden )
				senden->trenne();
		}
		if( empfangen )
			empfangen->release();
		if( senden )
			senden->release();
		DeleteCriticalSection( &cs );
		ss->klientsMM( this );
		ss->release();
		ende(); // Eventuelles Ende des Aktuellen Threades
	}
	else
	{
		if( verbunden )
		{
			if( empfangen )
				empfangen->trenne();
			if( senden )
				senden->trenne();
		}
		if( empfangen )
			empfangen->release();
		if( senden )
			senden->release();
		DeleteCriticalSection( &cs );
		ss->klientsMM( this );
		ss->release();
	}
}

// nicht constant
void SSKlient::lock()
{
	EnterCriticalSection( &cs );
}

void SSKlient::unlock()
{
	LeaveCriticalSection( &cs );
}

void SSKlient::absturz()
{
	ende();
	if( senden )
		senden->trenne();
	empfangen->trenne();
}

void SSKlient::setSpielErstellt( SpielErstellt *zErstellt )
{
	this->zErstellt = zErstellt;
}

void SSKlient::setSpiel( Spiel *zSpiel )
{
	this->zSpiel = zSpiel;
}

void SSKlient::nichtTrennen()
{
	verbunden = 0;
}

void SSKlient::spielGefunden( int karteId )
{
	if( !senden )
		return;
	lock();
	senden->sendeEncrypted( "\xB", 1 );
	senden->sendeEncrypted( (char*)&karteId, 4 );
	unlock();
	ss->addEmpfangen( senden->getDownloadBytes( 1 ) );
	ss->addGesendet( senden->getUploadBytes( 1 ) );
}

void SSKlient::zeitVerbleibend( char sekunden )
{
	if( !senden )
		return;
	lock();
	senden->sendeEncrypted( "\1", 1 );
	senden->sendeEncrypted( &sekunden, 1 );
	unlock();
	ss->addEmpfangen( senden->getDownloadBytes( 1 ) );
	ss->addGesendet( senden->getUploadBytes( 1 ) );
}

void SSKlient::erstellungAbgebrochen( char *grund )
{
	if( !senden )
		return;
	char len = (char)textLength( grund );
	lock();
	senden->sendeEncrypted( "\2", 1 );
	senden->sendeEncrypted( &len, 1 );
	if( len )
		senden->sendeEncrypted( grund, len );
	unlock();
	ss->addEmpfangen( senden->getDownloadBytes( 1 ) );
	ss->addGesendet( senden->getUploadBytes( 1 ) );
}

void SSKlient::backInWarteschlange( char st, char min, char sec )
{
	if( !senden )
		return;
	lock();
	senden->sendeEncrypted( "\4", 1 );
	senden->sendeEncrypted( &st, 1 );
	senden->sendeEncrypted( &min, 1 );
	senden->sendeEncrypted( &sec, 1 );
	unlock();
	ss->addEmpfangen( senden->getDownloadBytes( 1 ) );
	ss->addGesendet( senden->getUploadBytes( 1 ) );
}

void SSKlient::erstellungFortsetzen()
{
	if( !senden )
		return;
	lock();
	senden->sendeEncrypted( "\5", 1 );
	unlock();
	ss->addEmpfangen( senden->getDownloadBytes( 1 ) );
	ss->addGesendet( senden->getUploadBytes( 1 ) );
}

void SSKlient::erstellungInitialisierung( SpielerTeamStruktur *sts )
{
	if( !senden )
		return;
	lock();
	senden->sendeEncrypted( "\6", 1 );
	senden->sendeEncrypted( (char*)&sts->spielerAnzahl, 4 );
	senden->sendeEncrypted( (char*)&sts->teamAnzahl, 4 );
	for( int i = 0; i < sts->spielerAnzahl; i++ )
	{
		int f = sts->spielerFarbe->hat( i ) ? sts->spielerFarbe->get( i ) : 0;
		senden->sendeEncrypted( (char*)&f, 4 );
	}
	for( int i = 0; i < sts->teamAnzahl; i++ )
	{
		int f = sts->teamFarbe->hat( i ) ? sts->teamFarbe->get( i ) : 0;
		senden->sendeEncrypted( (char*)&f, 4 );
	}
	for( int i = 0; i < sts->teamAnzahl; i++ )
	{
		Text *name = sts->teamName->z( i );
		char len = (char)name->getLength();
		senden->sendeEncrypted( &len, 1 );
		if( len )
			senden->sendeEncrypted( name->getText(), len );
	}
	for( int i = 0; i < sts->teamAnzahl; i++ )
	{
		int size = sts->teamSize->hat( i ) ? sts->teamSize->get( i ) : 0;
		senden->sendeEncrypted( (char*)&size, 4 );
	}
	unlock();
	ss->addEmpfangen( senden->getDownloadBytes( 1 ) );
	ss->addGesendet( senden->getUploadBytes( 1 ) );
}

void SSKlient::erstellungAddSpieler( int accountId )
{
	if( !senden )
		return;
	lock();
	senden->sendeEncrypted( "\7", 1 );
	senden->sendeEncrypted( (char*)&accountId, 4 );
	unlock();
	ss->addEmpfangen( senden->getDownloadBytes( 1 ) );
	ss->addGesendet( senden->getUploadBytes( 1 ) );
}

void SSKlient::erstellungRemoveSpieler( int accountId )
{
	if( !senden )
		return;
	lock();
	senden->sendeEncrypted( "\x8", 1 );
	senden->sendeEncrypted( (char*)&accountId, 4 );
	unlock();
	ss->addEmpfangen( senden->getDownloadBytes( 1 ) );
	ss->addGesendet( senden->getUploadBytes( 1 ) );
}

void SSKlient::erstellungSpielerTeam( int accountId, int spielerNummer )
{
	if( !senden )
		return;
	lock();
	senden->sendeEncrypted( "\x9", 1 );
	senden->sendeEncrypted( (char*)&accountId, 4 );
	senden->sendeEncrypted( (char*)&spielerNummer, 4 );
	unlock();
	ss->addEmpfangen( senden->getDownloadBytes( 1 ) );
	ss->addGesendet( senden->getUploadBytes( 1 ) );
}

void SSKlient::erstellungChatNachricht( char *nachricht )
{
	if( !senden )
		return;
	char len = (char)textLength( nachricht );
	if( !len )
		return;
	lock();
	senden->sendeEncrypted( "\xA", 1 );
	senden->sendeEncrypted( &len, 1 );
	senden->sendeEncrypted( nachricht, len );
	unlock();
	ss->addEmpfangen( senden->getDownloadBytes( 1 ) );
	ss->addGesendet( senden->getUploadBytes( 1 ) );
}

void SSKlient::spielLadenBeginn()
{
	if( !senden )
		return;
	lock();
	senden->sendeEncrypted( "\xC", 1 );
	unlock();
	ss->addEmpfangen( senden->getDownloadBytes( 1 ) );
	ss->addGesendet( senden->getUploadBytes( 1 ) );
}

void SSKlient::spielLadenSpielerAdd( int accId, int spielerNumer )
{
	if( !senden )
		return;
	lock();
	senden->sendeEncrypted( "\xD", 1 );
	senden->sendeEncrypted( (char*)&accId, 4 );
	senden->sendeEncrypted( (char*)&spielerNumer, 4 );
	unlock();
	ss->addEmpfangen( senden->getDownloadBytes( 1 ) );
	ss->addGesendet( senden->getUploadBytes( 1 ) );
}

void SSKlient::spielLadenSetSpielerProzent( int accId, int prozent )
{
	if( !senden )
		return;
	lock();
	senden->sendeEncrypted( "\xE", 1 );
	senden->sendeEncrypted( (char*)&accId, 4 );
	senden->sendeEncrypted( (char*)&prozent, 4 );
	unlock();
	ss->addEmpfangen( senden->getDownloadBytes( 1 ) );
	ss->addGesendet( senden->getUploadBytes( 1 ) );
}

void SSKlient::spielLadenSetSpielerPing( int accId, int ping )
{
	if( !senden )
		return;
	lock();
	senden->sendeEncrypted( "\xF", 1 );
	senden->sendeEncrypted( (char*)&accId, 4 );
	senden->sendeEncrypted( (char*)&ping, 4 );
	unlock();
	ss->addEmpfangen( senden->getDownloadBytes( 1 ) );
	ss->addGesendet( senden->getUploadBytes( 1 ) );
}

double SSKlient::ping()
{
	if( !senden )
		return 0;
	ZeitMesser *zeit = new ZeitMesser();
	char ret = 0;
	lock();
	zeit->messungStart();
	senden->sendeEncrypted( "\x10", 1 );
	senden->getNachrichtEncrypted( &ret, 1 );
	zeit->messungEnde();
	unlock();
	double retVal = zeit->getSekunden();
	zeit->release();
	ss->addEmpfangen( senden->getDownloadBytes( 1 ) );
	ss->addGesendet( senden->getUploadBytes( 1 ) );
	return retVal;
}

void SSKlient::spielLadenFertig()
{
	if( !senden )
		return;
	lock();
	senden->sendeEncrypted( "\x11", 1 );
	unlock();
	ss->addEmpfangen( senden->getDownloadBytes( 1 ) );
	ss->addGesendet( senden->getUploadBytes( 1 ) );
}

void SSKlient::spielNachricht( short len, char *bytes )
{
	if( !senden )
		return;
	lock();
	senden->sendeEncrypted( "\x12", 1 );
	senden->sende( (char*)&len, 2 );
	senden->sende( bytes, len );
	unlock();
	ss->addEmpfangen( senden->getDownloadBytes( 1 ) );
	ss->addGesendet( senden->getUploadBytes( 1 ) );
}

void SSKlient::statistikNachricht( short len, char *bytes )
{
	if( !senden )
		return;
	lock();
	senden->sendeEncrypted( "\x13", 1 );
	senden->sendeEncrypted( (char*)&len, 2 );
	senden->sendeEncrypted( bytes, len );
	unlock();
	ss->addEmpfangen( senden->getDownloadBytes( 1 ) );
	ss->addGesendet( senden->getUploadBytes( 1 ) );
}

bool SSKlient::keepAlive()
{
	if( !senden || !verbunden )
		return 0;
	char res = 0;
	lock();
	senden->sendeEncrypted( "\x14", 1 );
	senden->getNachrichtEncrypted( &res, 1 );
	unlock();
	return res == 1;
}

void SSKlient::trenne()
{
	if( !verbunden )
		return;
	verbunden = 0;
	if( !senden )
		return;
	lock();
	senden->sendeEncrypted( "\0", 1 );
	unlock();
	ss->addEmpfangen( senden->getDownloadBytes( 1 ) );
	ss->addGesendet( senden->getUploadBytes( 1 ) );
}

void SSKlient::setSenden( SSKlient *klient )
{
	senden = klient->getKlient();
	Text *key = ss->zDB()->getKlientKey( klientNummer );
	if( !key )
	{
		senden->sendeEncrypted( "\0", 1 );
		errorZuKlientSende( "Es konnte kein Key ermittelt werden." );
	}
	else
	{
		senden->sendeEncrypted( "\1", 1 );
		senden->setEmpfangKey( *key, key->getLength() );
		senden->setSendeKey( *key, key->getLength() );
		key->release();
	}
	verbunden = 1;
	if( !ss->verteileKlient( this ) )
		ss->unregisterKlient( this );
	klient->nichtTrennen();
	klient->release();
	ss->addEmpfangen( senden->getDownloadBytes( 1 ) );
	ss->addGesendet( senden->getUploadBytes( 1 ) );
}

void SSKlient::thread()
{
	int unbekannt = 0;
	bool del = 0;
	while( 1 )
	{
		bool br = 0;
		char message = 0;
		if( empfangen->getNachrichtEncrypted( &message, 1 ) )
		{
			switch( message )
			{
			case 1: // Klient Identifikation
				empfangen->getNachrichtEncrypted( (char*)&klientNummer, 4 );
				if( !klientNummer )
					errorZuKlientEmpfang( "Du besitzt noch keine Klient Nummer." );
				else
				{
					accountId = ss->zDB()->getAccountId( klientNummer );
					termThread = 0;
					if( ss->registerKlient( this ) )
						return;
					termThread = 1; 
					Text *key = ss->zDB()->getKlientKey( klientNummer );
					if( !key )
						errorZuKlientEmpfang( "Es konnte kein Key ermittelt werden." );
					else
					{
						empfangen->sendeEncrypted( "\1", 1 );
						empfangen->setEmpfangKey( *key, key->getLength() );
						empfangen->setSendeKey( *key, key->getLength() );
						key->release();
					}
				}
				break;
			case 2: // Main / Erhaltung Server message
				if( 1 )
				{
					char befehl = 0;
					empfangen->getNachrichtEncrypted( &befehl, 1 );
					switch( befehl )
					{
					case 2: // klient absturtz
						if( 1 )
						{
							int klientId = 0;
							empfangen->getNachrichtEncrypted( (char*)&klientId, 4 );
							if( klientId && ss->absturzKlient( klientId ) )
								empfangen->sendeEncrypted( "\1", 1 );
							else
								empfangen->sendeEncrypted( "\0", 1 );
						}
						break;
					default:
						errorZuKlientEmpfang( "Befehl nicht bekannt!" );
						break;
					}
				}
				break;
			case 3: // Verbindungsende
				trenne();
				empfangen->sendeEncrypted( "\1", 1 );
				br = 1;
				break;
			case 4: // SpielErstellt annehmen
				if( zErstellt )
				{
					if( zErstellt->klientNimmtAn( accountId ) )
						empfangen->sendeEncrypted( "\1", 1 );
					else
					{
						empfangen->sendeEncrypted( "\0", 1 );
						errorZuKlientSende( "Es ist ein unbekannter Fehler aufgetreten." );
					}
				}
				else
				{
					empfangen->sendeEncrypted( "\0", 1 );
					errorZuKlientSende( "Dir wurde kein erstelltes Spiel zugeteilt." );
				}
				break;
			case 5: // SpielErstellt ablehnen
				if( zErstellt )
				{
					if( zErstellt->klientLehntAb( accountId ) )
						empfangen->sendeEncrypted( "\1", 1 );
					else
					{
						empfangen->sendeEncrypted( "\0", 1 );
						errorZuKlientSende( "Es ist ein unbekannter Fehler aufgetreten." );
					}
				}
				else
				{
					empfangen->sendeEncrypted( "\0", 1 );
					errorZuKlientSende( "Dir wurde kein erstelltes Spiel zugeteilt." );
				}
				break;
			case 6: // SpielErstellt team wechseln
				if( zSpiel )
				{
					empfangen->sendeEncrypted( "\1", 1 );
					int team = 0;
					empfangen->getNachrichtEncrypted( (char*)&team, 4 );
					if( zSpiel->klientWechseltTeam( accountId, team ) )
						empfangen->sendeEncrypted( "\1", 1 );
					else
					{
						empfangen->sendeEncrypted( "\0", 1 );
						errorZuKlientSende( "Das Team Ist bereits voll." );
					}
				}
				else
				{
					empfangen->sendeEncrypted( "\0", 1 );
					errorZuKlientSende( "Dir wurde kein erstelltes Spiel zugeteilt." );
				}
				break;
			case 7: // SpielErstellt chat Nachricht
				if( zSpiel )
				{
					empfangen->sendeEncrypted( "\1", 1 );
					unsigned char len = 0;
					empfangen->getNachrichtEncrypted( (char*)&len, 1 );
					char *nachricht = new char[ len + 1 ];
					nachricht[ (int)len ] = 0;
					if( len )
						empfangen->getNachrichtEncrypted( nachricht, len );
					Text *msg = ss->zDB()->getAccountRufName( accountId );
					msg->append( ": " );
					msg->append( nachricht );
					delete[] nachricht;
					if( zSpiel->chatNachricht( msg->getText() ) )
						empfangen->sendeEncrypted( "\1", 1 );
					else
					{
						empfangen->sendeEncrypted( "\0", 1 );
						errorZuKlientSende( "Es ist ein unbekannter Fehler aufgetreten." );
					}
					msg->release();
				}
				else
				{
					empfangen->sendeEncrypted( "\0", 1 );
					errorZuKlientSende( "Dir wurde kein erstelltes Spiel zugeteilt." );
				}
				break;
			case 8: // Server Message
				if( 1 )
				{
					char befehl = 0;
					empfangen->getNachrichtEncrypted( &befehl, 1 );
					switch( befehl )
					{
					case 0: // Verbindung Trennen
						empfangen->sendeEncrypted( "\1", 1 );
						br = 1;
						del = 1;
						break;
					case 1: // Spiel Erstellen Abbrechen
						if( 1 )
						{
							empfangen->sendeEncrypted( "\1", 1 );
							int spielErstelltId = 0;
							empfangen->getNachrichtEncrypted( (char*)&spielErstelltId, 4 );
							ss->SpielErstelltAbbruch( spielErstelltId );
							empfangen->sendeEncrypted( "\1", 1 );
						}
						break;
					default:
						empfangen->sendeEncrypted( "\0", 1 );
						break;
					}
				}
				break;
			case 9: // Bereit zum Laden
				if( zSpiel && zSpiel->klientBereitZumLaden( accountId ) )
					empfangen->sendeEncrypted( "\1", 1 );
				else
				{
					empfangen->sendeEncrypted( "\0", 1 );
					errorZuKlientSende( "Es ist ein unbekannter Fehler aufgetreten." );
				}
				break;
			case 0xA: // Bereit zum Spiel
				if( zSpiel && zSpiel->klientBereitZumSpiel( accountId ) )
					empfangen->sendeEncrypted( "\1", 1 );
				else
				{
					empfangen->sendeEncrypted( "\0", 1 );
					errorZuKlientSende( "Es ist ein unbekannter Fehler aufgetreten." );
				}
				break;
			case 0xB: // Spiel Nachricht
				if( 1 )
				{
					unsigned short len = 0;
					empfangen->getNachricht( (char*)&len, 2 );
					char *bytes = new char[ len ];
					empfangen->getNachricht( bytes, len );
					if( !zSpiel || !zSpiel->spielNachricht( accountId, len, bytes ) )
						errorZuKlientSende( "Es ist ein unbekannter Fehler aufgetreten." );
					delete[] bytes;
				}
				break;
			case 0xC: // Spiel Laden Prozent
				if( 1 )
				{
					empfangen->sendeEncrypted( "\1", 1 );
					int prozent = 0;
					empfangen->getNachrichtEncrypted( (char*)&prozent, 4 );
					if( zSpiel && zSpiel->klientSpielLadenProzent( accountId, prozent ) )
						empfangen->sendeEncrypted( "\1", 1 );
					else
					{
						empfangen->sendeEncrypted( "\0", 1 );
						errorZuKlientSende( "Es ist ein unbekannter Fehler aufgetreten." );
					}
				}
				break;
			case 0xD: // Statistik Nachricht
				if( 1 )
				{
					empfangen->sendeEncrypted( "\1", 1 );
					unsigned short len = 0;
					empfangen->getNachrichtEncrypted( (char*)&len, 2 );
					char *bytes = new char[ len ];
					empfangen->getNachrichtEncrypted( bytes, len );
					if( zSpiel && zSpiel->statistikNachricht( accountId, len, bytes ) )
						empfangen->sendeEncrypted( "\1", 1 );
					else
					{
						empfangen->sendeEncrypted( "\0", 1 );
						errorZuKlientSende( "Es ist ein unbekannter Fehler aufgetreten." );
					}
					delete[] bytes;
				}
				break;
			case 0xE: // Team wählen Fertig
				if( 1 )
				{
					if( zSpiel && zSpiel->klientTeamAuswahlFertig( accountId ) )
						empfangen->sendeEncrypted( "\1", 1 );
					else
					{
						empfangen->sendeEncrypted( "\0", 1 );
						errorZuKlientSende( "Es ist ein unbekannter Fehler aufgetreten." );
					}
				}
				break;
			case 0xF: // ping
				if( 1 )
				{
					empfangen->sendeEncrypted( "\1", 1 );
					keepAlive();
				}
				break;
			default:
				empfangen->sendeEncrypted( "\0", 1 );
				errorZuKlientSende( "Unbekannte Nachricht." );
				unbekannt += 2;
				if( unbekannt > 10 )
				{
					trenne();
					br = 1;
				}
				break;
			}
			unbekannt--;
			if( unbekannt < 0 )
				unbekannt = 0;
		}
		else
		{
			trenne();
			br = 1;
		}
		if( br )
			break;
		ss->addEmpfangen( empfangen->getDownloadBytes( 1 ) );
		ss->addGesendet( empfangen->getUploadBytes( 1 ) );
	}
	ss->addEmpfangen( empfangen->getDownloadBytes( 1 ) );
	ss->addGesendet( empfangen->getUploadBytes( 1 ) );
	if( del || !accountId )
	{
		termThread = 0;
		delete this;
	}
	else
	{
		termThread = 0;
		ss->unregisterKlient( this );
	}
}

void SSKlient::errorZuKlientEmpfang( const char *nachricht ) // sendet eine Fehlernachricht zum Klient
{
	char len = (char)textLength( nachricht );
	if( !len )
		return;
	if( empfangen )
	{
		empfangen->sendeEncrypted( "\3", 1 );
		empfangen->sendeEncrypted( &len, 1 );
		empfangen->sendeEncrypted( nachricht, len );
	}
	ss->addEmpfangen( empfangen->getDownloadBytes( 1 ) );
	ss->addGesendet( empfangen->getUploadBytes( 1 ) );
}

void SSKlient::errorZuKlientSende( const char *nachricht )
{
	char len = (char)textLength( nachricht );
	if( !len )
		return;
	if( senden )
	{
		lock();
		senden->sendeEncrypted( "\3", 1 );
		senden->sendeEncrypted( &len, 1 );
		senden->sendeEncrypted( nachricht, len );
		unlock();
		ss->addEmpfangen( senden->getDownloadBytes( 1 ) );
		ss->addGesendet( senden->getUploadBytes( 1 ) );
	}
}

// const
int SSKlient::getAccountId() const
{
	return accountId;
}

int SSKlient::getKlientNummer() const
{
	return klientNummer;
}

SKlient *SSKlient::getKlient() const
{
	return empfangen->getThis();
}

// Reference Counting
SSKlientV *SSKlient::getThis()
{
	ref++;
	return this;
}

SSKlientV *SSKlient::release()
{
	ref--;
	if( !ref )
		delete this;
	return 0;
}