#ifdef WIN32
#include "..\SpielServer.h"
#include "..\..\Datenbank\Datenbank.h"
#else
#include "SpielServer.h"
#include "Datenbank.h"
#include <unistd.h>
#define Sleep( x )   usleep( (x) * 1000 )
#endif
#include <Zeit.h>

// Inhalt der SpielErstellt Kasse aus SpielErstellt.h
// Konstruktor
SpielErstellt::SpielErstellt( int id, SpielServer *ss )
: Thread()
{
	this->ss = ss;
	this->id = id;
	karte = ss->zDB()->getKarteVonErstelltemSpiel( id );
	spielArt = ss->zDB()->getSpielArtId( karte );
	accounts = new Array< int >();
	spielerAnzahl = ss->zDB()->getSpielErstelltSpieler( id, accounts );
	klients = new RCArray< SSKlient >();
	status = new Array< int >();
	verbunden = 0;
	state = 0;
	abbr = 0;
	spielId = 0;
	ref = 1;
}

// Destruktor
SpielErstellt::~SpielErstellt()
{
	ss->release();
	accounts->release();
	klients->release();
	status->release();
}

// nicht constant
void SpielErstellt::abbruch()
{
	abbr = 1;
}

void SpielErstellt::sendeVerbindungsBefehl()
{
	unsigned short sPort = (unsigned short)ss->getPort();
	char *sIp = ss->getIp();
	unsigned char *sIp4 = new unsigned char[ 4 ];
	Text *tmpT = new Text( sIp );
	Text *ip1 = tmpT->getTeilText( 0, tmpT->positionVon( '.' ) );
	Text *ip2 = tmpT->getTeilText( tmpT->positionVon( '.' ) + 1, tmpT->positionVon( '.', 1 ) );
	Text *ip3 = tmpT->getTeilText( tmpT->positionVon( '.', 1 ) + 1, tmpT->positionVon( '.', 2 ) );
	Text *ip4 = tmpT->getTeilText( tmpT->positionVon( '.', 2 ) + 1 );
	sIp4[ 0 ] = (unsigned char)TextZuInt( ip1->getText(), 10 );
	sIp4[ 1 ] = (unsigned char)TextZuInt( ip2->getText(), 10 );
	sIp4[ 2 ] = (unsigned char)TextZuInt( ip3->getText(), 10 );
	sIp4[ 3 ] = (unsigned char)TextZuInt( ip4->getText(), 10 );
	ip1->release();
	ip2->release();
	ip3->release();
	ip4->release();
	tmpT->release();
	for( int i = 0; i < spielerAnzahl; i++ )
	{
		int accId = accounts->hat( i ) ? accounts->get( i ) : 0;
		int port = 0;
		Text *ip = new Text( "" );
		if( ss->zDB()->getChatServerPortIp( accId, &port, ip ) )
		{
			char ret = 0;
			Klient *klient = new Klient();
			unsigned char key[ 20 ] = { 78, 20, 190, 133, 10, 175, 51, 96, 62, 1, 180, 194, 126, 50, 211, 154, 105, 227, 22, 101 };
			klient->setSendeKey( (char*)key, 20 );
			klient->setEmpfangKey( (char*)key, 20 );
			klient->verbinde( (unsigned short)port, ip->getText() );
			klient->sendeEncrypted( "\5\x1D", 2 );
			klient->getNachrichtEncrypted( &ret, 1 );
			if( ret != 3 )
			{
				klient->sendeEncrypted( (char*)&accId, 4 );
				klient->sendeEncrypted( (char*)&sPort, 2 );
				klient->sendeEncrypted( (char*)sIp4, 4 );
				klient->getNachrichtEncrypted( &ret, 1 );
			}
			if( ret == 3 )
			{
				unsigned char len = 0;
				klient->getNachrichtEncrypted( (char*)&len, 1 );
				char *err = new char[ len ];
				klient->getNachrichtEncrypted( err, len );
				delete[] err;
			}
			klient->sendeEncrypted( "\3", 1 );
			klient->getNachrichtEncrypted( &ret, 1 );
			ss->addEmpfangen( klient->getDownloadBytes( 1 ) );
			ss->addGesendet( klient->getUploadBytes( 1 ) );
			klient->trenne();
			klient->release();
		}
		ip->release();
	}
	delete[] sIp4;
	start();
}

bool SpielErstellt::klientVerbunden( SSKlient *klient )
{
	if( verbunden == spielerAnzahl )
	{
		klient->release();
		return 0;
	}
	int accId = klient->getAccountId();
	bool ret = 0;
	for( int i = 0; i < spielerAnzahl; i++ )
	{
		if( accounts->hat( i ) && accounts->get( i ) == accId )
		{
			if( state == 1 )
				klient->spielGefunden( karte );
			klients->set( klient, i );
			status->set( 1, i );
			verbunden++;
			klient->setSpielErstellt( this );
			ret = 1;
			break;
		}
	}
	if( !ret )
		klient->release();
	return ret;
}

bool SpielErstellt::klientNimmtAn( int accountId )
{
	if( !verbunden || state != 1 )
		return 0;
	bool ret = 0;
	for( int i = 0; i < spielerAnzahl; i++ )
	{
		if( accounts->hat( i ) && accounts->get( i ) == accountId )
		{
			status->set( 2, i );
			ret = 1;
			break;
		}
	}
	return ret;
}

bool SpielErstellt::klientLehntAb( int accountId )
{
	if( !verbunden || state != 1 )
		return 0;
	bool ret = 0;
	for( int i = 0; i < spielerAnzahl; i++ )
	{
		if( accounts->hat( i ) && accounts->get( i ) == accountId )
		{
			status->set( 3, i );
			ret = 1;
			break;
		}
	}
	return ret;
}

bool SpielErstellt::klientGetrennt( SSKlient *zKlient )
{
	if( !verbunden )
		return 0;
	int accId = zKlient->getAccountId();
	bool ret = 0;
	for( int i = 0; i < spielerAnzahl; i++ )
	{
		if( accounts->hat( i ) && accounts->get( i ) == accId )
		{
			if( status->hat( i ) && status->get( i ) != 0 )
			{
				zKlient->setSpielErstellt( 0 );
				status->set( 0, i );
				verbunden--;
				klients->set( 0, i );
				ret = 1;
			}
			break;
		}
	}
	return ret;
}

void SpielErstellt::thread()
{
	state = 1;
	double time = 0;
	ZeitMesser *zeit = new ZeitMesser();
	int nachrichtSenden = 1;
	while( time < 10 )
	{ // warten auf annahme von allen Spielern
		if( abbr )
			break;
		zeit->messungStart();
		Sleep( 100 );
		bool br = 0;
		bool st2 = 1;
		for( int i = 0; i < spielerAnzahl; i++ )
		{
			if( status->hat( i ) && status->get( i ) == 3 )
			{
				br = 1;
				for( int j = 0; j < spielerAnzahl; j++ )
				{
					if( j != i )
						status->set( 2, j );
				}
				break;
			}
			if( st2 )
				st2 = status->hat( i ) && status->get( i ) == 2;
		}
		if( st2 )
			br = 1;
		if( br )
		{
			zeit->messungEnde();
			break;
		}
		char verbleibend = (char)( 10 - (char)time );
		if( verbleibend < 0 )
			verbleibend = 0;
		if( time > nachrichtSenden )
		{
			for( int i = 0; i < spielerAnzahl; i++ )
			{
				if( status->hat( i ) && status->get( i ) != 0 )
				{
					SSKlient *tmp = klients->z( i );
					if( tmp )
						tmp->zeitVerbleibend( verbleibend );
				}
			}
			nachrichtSenden++;
		}
		zeit->messungEnde();
		time += zeit->getSekunden();
	} // prüfen ob alle Spieler bereit sind
	state = 2;
	bool abbruch = verbunden != spielerAnzahl;
	const char *err = "Ein Spieler konnte nicht erreicht werden.";
	if( !abbruch )
	{
		for( int i = 0; i < spielerAnzahl; i++ )
		{
			if( !status->hat( i ) || status->get( i ) != 2 )
			{
				abbruch = 1;
				err = "Ein Spieler ist noch nicht bereit.";
				break;
			}
		}
	}
	if( abbr )
	{
		err = "Das Spiel wurde Wegen abwesenheit eines Spielers Abgebrochen.";
		abbruch = 1;
		for( int j = 0; j < spielerAnzahl; j++ )
			status->set( 2, j );
	}
	if( abbruch )
	{ // Es sind nicht alle bereit Vorgang abbrechen
		for( int i = 0; i < spielerAnzahl; i++ )
		{
			if( status->hat( i ) && status->get( i ) != 0 )
			{
				SSKlient *tmp = klients->z( i );
				if( tmp )
					tmp->erstellungAbgebrochen( (char*)err );
			}
		}
		Array< bool > *inWarteschlange = new Array< bool >();
		RCArray< Zeit > *wZeit = new RCArray< Zeit >();
		if( ss->zDB()->spielErstelltAbbruch( id, spielerAnzahl, accounts, status, inWarteschlange, wZeit ) ) // zurück in Warteschlange
		{
			for( int i = 0; i < spielerAnzahl; i++ )
			{
				if( inWarteschlange->hat( i ) && inWarteschlange->get( i ) )
				{
					SSKlient *tmp = klients->z( i );
					if( tmp )
					{
						Zeit *zTmp = wZeit->z( i );
						if( zTmp )
							tmp->backInWarteschlange( (char)zTmp->zUhrzeit()->getStunde(), (char)zTmp->zUhrzeit()->getMinute(), (char)zTmp->zUhrzeit()->getSekunde() );
					}
				}
			}
		}
		inWarteschlange->release();
		wZeit->release();
		for( int i = 0; i < spielerAnzahl; i++ )
		{
			SSKlient *tmp = klients->z( i );
			if( tmp )
				tmp->trenne();
		}
		zeit->release();
		ss->removeSpielErstellt( id ); // delete this
		return;
	}
	// alle Spieler sind bereit Vorgang fortsetzen
	spielId = ss->zDB()->spielErstelltFortsetzen( id );
	Spiel *weiter = new Spiel( spielId, ss->getThis() );
	ss->addSpiel( weiter );
	weiter->setAccounts( spielerAnzahl, accounts );
	weiter->setKlients( spielerAnzahl, klients );
	weiter->setKarteId( karte );
	ss->removeSpielErstellt( id ); // delete this
	zeit->release();
}

// constant
int SpielErstellt::getId() const
{
	return id;
}

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

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

// Inhalt der SpielFinden Klasse aus SpielErstellt.h
// Konstruktor
SpielFinden::SpielFinden( SpielServer *ss )
: Thread()
{
	this->ss = ss;
	end = 0;
	spielServerId = 0;
	ref = 1;
}

// Destruktor
SpielFinden::~SpielFinden()
{
	end = 1;
	warteAufThread( 2000 );
}

// nicht constant
void SpielFinden::setEnde()
{
	end = 1;
}

void SpielFinden::setSpielServerId( int id )
{
	spielServerId = id;
}

void SpielFinden::thread()
{
	end = 0;
	while( !end )
	{
		for( int i = 0; i < 10 && !end; i++ )
			Sleep( 1000 );
		if( end )
			break;
		int ret = ss->zDB()->erstelleSpiel( spielServerId );
		if( !ret )
		{ // erfolg
			int id = ss->zDB()->getSpielErstelltId( spielServerId );
			SpielErstellt *erstellt = new SpielErstellt( id, ss->getThis() );
			ss->addSpielErstellt( erstellt );
			erstellt->sendeVerbindungsBefehl();
			ss->zDB()->deleteSpielErstelltNext( spielServerId );
		}
	}
	run = 0;
	end = 0;
}

// constant

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

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