#include "../KSGKlient.h"
#include "../KSGServer.h"
#include "../../Global/Variablen.h"
#include "../Keys.h"

// Inhalt der SpielKlient Klasse aus KSGKlient.h
// Konstruktor
SpielKlient::SpielKlient()
    : Thread()
{
    verbunden = 0;
    senden = 0;
    empfangen = 0;
    fehler = new Text( "" );
    klientId = getKlientId();
    ref = 1;
}

// Destruktor
SpielKlient::~SpielKlient()
{
    cs.lock();
    if( senden )
    {
        char serverReturn = 0;
        if( verbunden )
        {
            senden->sendeEncrypted( "\3", 1 );
            senden->getNachrichtEncrypted( &serverReturn, 1 );
            senden->trenne();
        }
        senden = senden->release();
        verbunden = 0;
        if( empfangen )
        {
            warteAufThread( 5000 );
            ende();
            empfangen->trenne();
            empfangen = empfangen->release();
        }
    }
    fehler = fehler->release();
    cs.unlock();
}

// nicht constant
bool SpielKlient::verbinde( unsigned short port, char *ip ) // verbindet ich mit dem Spiel Server
{
    cs.lock();
    if( verbunden )
    {
        cs.unlock();
        return 1;
    }
    if( !senden )
        senden = new Klient();
    int keyLen = 0;
    char *key = 0;
    Keys::getServerKey( &key, keyLen, Keys::SPIEL, Keys::SENDEN );
    senden->setSendeKey( key, keyLen );
    delete[] key;
    Keys::getServerKey( &key, keyLen, Keys::SPIEL, Keys::EMPFANGEN );
    senden->setEmpfangKey( key, keyLen );
    delete[] key;
    if( senden->verbinde( port, ip ) )
    {
        if( senden->sendeEncrypted( "\1", 1 ) )
        {
            char serverReturn = 0;
            senden->sendeEncrypted( (char*)&klientId, 4 );
            char ret = 0;
            senden->getNachrichtEncrypted( &ret, 1 );
            if( ret != 1 )
            {
                if( ret == 3 )
                {
                    char byte = 0;
                    senden->getNachrichtEncrypted( &byte, 1 );
                    char *f = new char[ byte + 1 ];
                    f[ byte ] = 0;
                    senden->getNachrichtEncrypted( f, byte );
                    fehler->setText( f );
                    delete[]f;
                }
                senden->sendeEncrypted( "\3", 1 );
                senden->getNachrichtEncrypted( &serverReturn, 1 );
                senden->trenne();
                WMessageBox( 0, new Text( "Fehler" ), new Text( "Server akzeptiert den Klient nicht" ), MB_ICONERROR );
                cs.unlock();
                return 0;
            }
            char *sl = 0;
            char slL�n = getSchl�ssel( &sl );
            senden->setSendeKey( sl, slL�n );
            senden->setEmpfangKey( sl, slL�n );
            delete[] sl;
            if( !empfangen )
                empfangen = new Klient();
            int keyLen = 0;
            char *key = 0;
            Keys::getServerKey( &key, keyLen, Keys::SPIEL, Keys::SENDEN );
            empfangen->setSendeKey( key, keyLen );
            delete[] key;
            Keys::getServerKey( &key, keyLen, Keys::SPIEL, Keys::EMPFANGEN );
            empfangen->setEmpfangKey( key, keyLen );
            delete[] key;
            if( empfangen->verbinde( senden->getServerPort(), senden->getServerIp() ) )
            {
                start();
                verbunden = 1;
                cs.unlock();
                return 1;
            }
            else
            {
                fehler->setText( "Der dir zugewiesene Spiel Server kann dir keine Nachrichten senden." );
                cs.unlock();
                return 1;
            }
        }
        else
        {
            fehler->setText( "Der dir zugewiesene Spiel Server hat die Verbindung abgebrochen. Bitte versuche es Sp�ter erneut." );
            senden = senden->release();
        }
    }
    else
    {
        fehler->setText( "Der dir zugewiesene Spiel Server antwortet nicht. Bitte versuche es Sp�ter erneut." );
        senden = senden->release();
    }
    cs.unlock();
    return 0;
}

bool SpielKlient::spielErstelltAnnehmen() // klient ist bereit dem erstellten Spiel beizutreten
{
    if( !senden )
        return 0;
    char ret = 0;
    cs.lock();
    senden->sendeEncrypted( "\4", 1 );
    senden->getNachrichtEncrypted( &ret, 1 );
    cs.unlock();
    return ret == 1;
}

bool SpielKlient::spielErstelltAblehnen() // klient ist nicht bereit dem erstellten Spiel beizutreten
{
    if( !senden )
        return 0;
    char ret = 0;
    cs.lock();
    senden->sendeEncrypted( "\5", 1 );
    senden->getNachrichtEncrypted( &ret, 1 );
    cs.unlock();
    return ret == 1;
}

bool SpielKlient::spielErstelltTeamWechseln( int team ) // wechselt das Team
{
    if( !senden )
        return 0;
    char ret = 0;
    cs.lock();
    senden->sendeEncrypted( "\6", 1 );
    senden->getNachrichtEncrypted( &ret, 1 );
    if( !ret )
    {
        cs.unlock();
        return 0;
    }
    senden->sendeEncrypted( (char*)&team, 4 );
    senden->getNachrichtEncrypted( &ret, 1 );
    cs.unlock();
    return ret == 1;
}

bool SpielKlient::spielErstelltTeamFertig() // bestetigt die Team Auswahl
{
    if( !senden )
        return 0;
    char ret = 0;
    cs.lock();
    senden->sendeEncrypted( "\xE", 1 );
    senden->getNachrichtEncrypted( &ret, 1 );
    cs.unlock();
    return ret == 1;
}

bool SpielKlient::spielErstelltChatNachricht( char *nachricht ) // sendet eine Chat Nachricht an die mitglieder des erstellten Spiels
{
    char l�n = textLength( nachricht );
    if( !l�n )
        return 1;
    if( !senden )
        return 0;
    char ret = 0;
    cs.lock();
    senden->sendeEncrypted( "\7", 1 );
    senden->getNachrichtEncrypted( &ret, 1 );
    if( !ret )
    {
        cs.unlock();
        return 0;
    }
    senden->sendeEncrypted( &l�n, 1 );
    senden->sendeEncrypted( nachricht, l�n );
    senden->getNachrichtEncrypted( &ret, 1 );
    cs.unlock();
    return ret == 1;
}

bool SpielKlient::bereitZumLaden() // Klient ist bereit zum Laden
{
    if( !senden )
        return 0;
    char ret = 0;
    cs.lock();
    senden->sendeEncrypted( "\x9", 1 );
    senden->getNachrichtEncrypted( &ret, 1 );
    cs.unlock();
    return ret == 1;
}

bool SpielKlient::setLadenProzent( int prozent ) // Setzt den Fortschritt des Klients
{
    if( !senden )
        return 0;
    char ret = 0;
    cs.lock();
    senden->sendeEncrypted( "\xC", 1 );
    senden->getNachrichtEncrypted( &ret, 1 );
    if( ret )
    {
        senden->sendeEncrypted( (char*)&prozent, 4 );
        senden->getNachrichtEncrypted( &ret, 1 );
    }
    cs.unlock();
    return ret == 1;
}

bool SpielKlient::bereitZumSpiel() // Klient ist bereit zum Spiel
{
    if( !senden )
        return 0;
    char ret = 0;
    cs.lock();
    senden->sendeEncrypted( "\xA", 1 );
    senden->getNachrichtEncrypted( &ret, 1 );
    cs.unlock();
    return ret == 1;
}

bool SpielKlient::spielNachricht( short l�n, char *bytes ) // Nachricht w�hrend des Spiels
{
    if( !senden )
        return 0;
    cs.lock();
    senden->sendeEncrypted( "\xB", 1 );
    senden->sende( (char*)&l�n, 2 );
    senden->sende( bytes, l�n );
    cs.unlock();
    return 1;
}

bool SpielKlient::statistikNachricht( short l�n, char *bytes ) // Nachricht w�hrend der Statistik
{
    if( !senden )
        return 0;
    char ret = 0;
    cs.lock();
    senden->sendeEncrypted( "\xD", 1 );
    senden->getNachrichtEncrypted( &ret, 1 );
    if( ret )
    {
        senden->sendeEncrypted( (char*)&l�n, 2 );
        senden->sendeEncrypted( bytes, l�n );
        senden->getNachrichtEncrypted( &ret, 1 );
    }
    cs.unlock();
    return ret == 1;
}

bool SpielKlient::keepAlive() // Erh�lt die Verbindung aufrecht
{
    if( !verbunden )
        return 0;
    char res = 0;
    if( !cs.tryLock() )
        return 1;
    senden->sendeEncrypted( "\xF", 1 );
    senden->getNachrichtEncrypted( &res, 1 );
    cs.unlock();
    if( res != 1 )
        trenne();
    return res == 1;
}

bool SpielKlient::trenne() // trennt sich von dem Chat Server
{
    if( !verbunden )
        return 1;
    verbunden = 0;
    cs.lock();
    senden->sendeEncrypted( "\3", 1 );
    char serverReturn = 0;
    senden->getNachrichtEncrypted( &serverReturn, 1 );
    senden->trenne();
    warteAufThread( 2000 );
    empfangen->trenne();
    cs.unlock();
    run = 0;
    ende();
    return 1;
}

void SpielKlient::thread() // empfangen von Nachrichten
{
    if( !verbunden || !empfangen || !senden )
        return;
    empfangen->sendeEncrypted( "\1", 1 );
    empfangen->sendeEncrypted( (char*)&klientId, 4 );
    char res = 0;
    empfangen->getNachrichtEncrypted( &res, 1 );
    if( res == 3 )
    {
        char l�n = 0;
        empfangen->getNachrichtEncrypted( &l�n, 1 );
        char *nachricht = new char[ l�n + 1 ];
        nachricht[ l�n ] = 0;
        empfangen->getNachrichtEncrypted( nachricht, l�n );
        fehler->setText( nachricht );
        delete[]nachricht;
    }
    if( res == 1 )
    {
        char *sl = 0;
        char slL�n = getSchl�ssel( &sl );
        empfangen->setSendeKey( sl, slL�n );
        empfangen->setEmpfangKey( sl, slL�n );
        delete[] sl;
    }
    else if( res != 0 )
    {
        WMessageBox( 0, new Text( "Fehler" ), new Text( "Server hat keine Verwendung f�r den Client." ), MB_ICONERROR );
        return;
    }
    char befehl = 0;
    while( verbunden )
    {
        if( !empfangen->getNachrichtEncrypted( &befehl, 1 ) )
        {
            run = 0;
            return;
        }
        switch( befehl )
        {
        case 0: // verbindung getrennt
            trenne();
            run = 0;
            return;
        case 1: // verbleibende Zeit
            if( 1 )
            {
                char sekunden = 0;
                empfangen->getNachrichtEncrypted( &sekunden, 1 );
                if( nachLogin && nachLogin->zSpielenFenster() )
                    nachLogin->zSpielenFenster()->spielGefundenZeitVerbleibend( sekunden );
            }
            break;
        case 2: // SpielErstellt abbruch
            if( 1 )
            {
                char l�n = 0;
                empfangen->getNachrichtEncrypted( &l�n, 1 );
                char *grund = new char[ l�n + 1 ];
                grund[ l�n ] = 0;
                empfangen->getNachrichtEncrypted( grund, l�n );
                if( nachLogin && nachLogin->zNachrichtenListe() )
                    nachLogin->zNachrichtenListe()->addNachricht( new Text( "Spiel Abgebrochen" ), new Text( grund ), new Text( "Ok" ), 0, NachrichtType::nachricht, 0 );
                if( nachLogin && nachLogin->zSpielenFenster() )
                    nachLogin->zSpielenFenster()->spielGefundenAbbruch();
                delete[] grund;
            }
            break;
        case 3: // Fehler
            if( 1 )
            {
                char l�nge = 0;
                empfangen->getNachrichtEncrypted( &l�nge, 1 );
                char *txt = new char[ l�nge + 1 ];
                txt[ l�nge ] = 0;
                empfangen->getNachrichtEncrypted( txt, l�nge );
                hauptScreen->lock();
                if( nachLogin && nachLogin->zNachrichtenListe() )
                    nachLogin->zNachrichtenListe()->addNachricht( new Text( "Fehler" ), new Text( txt ), new Text( "Ok" ), 0, NachrichtType::nachricht, 0 );
                hauptScreen->unlock();
                delete[]txt;
            }
            break;
        case 4: // zur�ck in Warteschlange
            if( 1 )
            {
                char stunden = 0;
                char minuten = 0;
                char sekunden = 0;
                empfangen->getNachrichtEncrypted( &stunden, 1 );
                empfangen->getNachrichtEncrypted( &minuten, 1 );
                empfangen->getNachrichtEncrypted( &sekunden, 1 );
                if( nachLogin && nachLogin->zSpielenFenster() )
                    nachLogin->zSpielenFenster()->zur�ckInWarteschlange( stunden, minuten, sekunden );
            }
            break;
        case 5: // Erstellung fortsetzen
            if( nachLogin && nachLogin->zSpielenFenster() )
                nachLogin->zSpielenFenster()->teamAuswahlBetreten();
            break;
        case 6: // SpielErstellt Initialisierung
            if( 1 )
            {
                SpielerTeamStruktur *sts = new SpielerTeamStruktur();
                empfangen->getNachrichtEncrypted( (char*)&sts->spielerAnzahl, 4 );
                empfangen->getNachrichtEncrypted( (char*)&sts->teamAnzahl, 4 );
                for( int i = 0; i < sts->spielerAnzahl; i++ )
                {
                    int farbe = 0;
                    empfangen->getNachrichtEncrypted( (char*)&farbe, 4 );
                    sts->spielerFarbe->set( farbe, i );
                }
                for( int i = 0; i < sts->teamAnzahl; i++ )
                {
                    int farbe = 0;
                    empfangen->getNachrichtEncrypted( (char*)&farbe, 4 );
                    sts->teamFarbe->set( farbe, i );
                }
                for( int i = 0; i < sts->teamAnzahl; i++ )
                {
                    char l�n = 0;
                    empfangen->getNachrichtEncrypted( &l�n, 1 );
                    char *name = new char[ l�n + 1 ];
                    name[ l�n ] = 0;
                    if( l�n )
                        empfangen->getNachrichtEncrypted( name, l�n );
                    Text *tmp = new Text( name );
                    delete[] name;
                    sts->teamName->set( tmp, i );
                }
                for( int i = 0; i < sts->teamAnzahl; i++ )
                {
                    int gr��e = 0;
                    empfangen->getNachrichtEncrypted( (char*)&gr��e, 4 );
                    sts->teamGr��e->set( gr��e, i );
                }
                if( nachLogin && nachLogin->zSpielenFenster() )
                    nachLogin->zSpielenFenster()->teamAuswahlInit( sts );
                else
                    sts->release();
            }
            break;
        case 7: // SpielErstellt Spieler hinzugef�gt
            if( 1 )
            {
                int accountId = 0;
                empfangen->getNachrichtEncrypted( (char*)&accountId, 4 );
                if( nachLogin && nachLogin->zSpielenFenster() )
                    nachLogin->zSpielenFenster()->teamAuswahlAddSpieler( accountId );
            }
            break;
        case 8: // SpielErstellt Spieler entfernt
            if( 1 )
            {
                int accountId = 0;
                empfangen->getNachrichtEncrypted( (char*)&accountId, 4 );
                if( nachLogin && nachLogin->zSpielenFenster() )
                    nachLogin->zSpielenFenster()->teamAuswahlRemoveSpieler( accountId );
            }
            break;
        case 9: // SpielErstellt Spieler wechselt Team
            if( 1 )
            {
                int accountId = 0;
                int spielerNummer = 0;
                empfangen->getNachrichtEncrypted( (char*)&accountId, 4 );
                empfangen->getNachrichtEncrypted( (char*)&spielerNummer, 4 );
                if( nachLogin && nachLogin->zSpielenFenster() )
                    nachLogin->zSpielenFenster()->teamAuswahlSpielerWehseltTeam( accountId, spielerNummer );
            }
            break;
        case 0xA: // SpielErstellt Chat Nachricht
            if( 1 )
            {
                char l�n = 0;
                empfangen->getNachrichtEncrypted( &l�n, 1 );
                char *nachricht = new char[ l�n + 1 ];
                nachricht[ l�n ] = 0;
                if( l�n )
                    empfangen->getNachrichtEncrypted( nachricht, l�n );
                if( nachLogin && nachLogin->zSpielenFenster() )
                    nachLogin->zSpielenFenster()->teamAuswahlChatNachricht( nachricht );
                delete[] nachricht;
            }
            break;
        case 0xB: // Spiel gefunden
            if( 1 )
            {
                int karteId = 0;
                empfangen->getNachrichtEncrypted( (char*)&karteId, 4 );
                if( nachLogin && nachLogin->zSpielenFenster() )
                    nachLogin->zSpielenFenster()->spielGefunden( karteId );
            }
            break;
        case 0xC: // Spiel Laden beginnen
            aktion = 5;
            break;
        case 0xD: // Spiel Laden Spieler hinzuf�gen
            if( 1 )
            {
                int accId = 0;
                int spielerNummer = 0;
                empfangen->getNachrichtEncrypted( (char*)&accId, 4 );
                empfangen->getNachrichtEncrypted( (char*)&spielerNummer, 4 );
                if( nachLogin->zImSpiel() && nachLogin->zImSpiel()->istSichtbar() )
                    nachLogin->zImSpiel()->lBAddSpieler( accId, spielerNummer );
            }
            break;
        case 0xE: // Spiel Laden Spieler Prozent
            if( 1 )
            {
                int accId = 0;
                int prozent = 0;
                empfangen->getNachrichtEncrypted( (char*)&accId, 4 );
                empfangen->getNachrichtEncrypted( (char*)&prozent, 4 );
                if( nachLogin->zImSpiel() && nachLogin->zImSpiel()->istSichtbar() )
                    nachLogin->zImSpiel()->lBSetSpielerProzent( accId, prozent );
            }
            break;
        case 0xF: // Spiel Laden Spieler Ping
            if( 1 )
            {
                int accId = 0;
                int ping = 0;
                empfangen->getNachrichtEncrypted( (char*)&accId, 4 );
                empfangen->getNachrichtEncrypted( (char*)&ping, 4 );
                if( nachLogin->zImSpiel() && nachLogin->zImSpiel()->istSichtbar() )
                    nachLogin->zImSpiel()->lBSetSpielerPing( accId, ping );
            }
            break;
        case 0x10: // ping
            empfangen->sendeEncrypted( "\1", 1 );
            break;
        case 0x11: // Spiel Laden Fertig
            if( nachLogin->zImSpiel() && nachLogin->zImSpiel()->istSichtbar() )
                nachLogin->zImSpiel()->endLaden();
            break;
        case 0x12: // Spiel Nachricht
            if( 1 )
            {
                short l�n = 0;
                empfangen->getNachricht( (char*)&l�n, 2 );
                char *n = new char[ l�n ];
                empfangen->getNachricht( n, l�n );
                if( nachLogin->zImSpiel() && nachLogin->zImSpiel()->istSichtbar() )
                    nachLogin->zImSpiel()->spielNachricht( l�n, n );
                delete[] n;
            }
            break;
        case 0x13: // Statistik Nachricht
            if( 1 )
            {
                short l�n = 0;
                empfangen->getNachrichtEncrypted( (char*)&l�n, 2 );
                char *n = new char[ l�n ];
                empfangen->getNachrichtEncrypted( n, l�n );
                if( nachLogin && nachLogin->zSpielenFenster() )
                    nachLogin->zSpielenFenster()->statistikNachricht( l�n, n );
                delete[] n;
            }
            break;
        case 0x14: // ping
            empfangen->sendeEncrypted( "\1", 1 );
            break;
        default: // Unbekannte Servernachricht
            if( nachLogin && nachLogin->zNachrichtenListe() )
            {
                hauptScreen->lock();
                nachLogin->zNachrichtenListe()->addNachricht( new Text( "Fehler" ),
                                                              new Text( "Unbekannte Nachricht vom Server. Eventuel ist der Client nicht mehr Aktuell." ),
                                                              new Text( "Ok" ), 0, NachrichtType::nachricht, 0 );
                hauptScreen->unlock();
            }
            break;
        }
    }
    run = 0;
}

// constant
bool SpielKlient::istVerbunden() const // pr�ft, ob mit Chat Server verbunden
{
    return verbunden;
}

char *SpielKlient::getLetzterFehler() const // gibt den Letzten Fehlertext zu�ck
{
    return fehler->getText();
}

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

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