#include "../KSGKlient.h"
#include "../KSGServer.h"
#include "../../Global/Variablen.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;
    InitializeCriticalSection( &cs );
}

// Destruktor
SpielKlient::~SpielKlient()
{
    LOCK( &cs );
    if( senden )
    {
        char serverReturn = 0;
        if( verbunden )
        {
            senden->sendeVerschlüsselt( "\3", 1 );
            senden->getNachrichtVerschlüsselt( &serverReturn, 1 );
            senden->trenne();
        }
        senden = senden->release();
        verbunden = 0;
        if( empfangen )
        {
            warteAufThread( 5000 );
            ende();
            empfangen->trenne();
            empfangen = empfangen->release();
        }
    }
    fehler = fehler->release();
    UNLOCK( &cs );
    DeleteCriticalSection( &cs );
}

// nicht constant
bool SpielKlient::verbinde( unsigned short port, char *ip ) // verbindet ich mit dem Spiel Server
{
    LOCK( &cs );
    if( verbunden )
    {
        UNLOCK( &cs );
        return 1;
    }
    if( !senden )
        senden = new Klient();
    unsigned char schlüssel[ 20 ] = { 253, 234, 211, 132, 121, 230, 95, 145, 201, 13, 43, 77, 153, 223, 253, 69, 234, 43, 52, 99 };
    senden->setSendeSchlüssel( (char*)schlüssel, 20 );
    senden->setEmpfangSchlüssel( (char*)schlüssel, 20 );
    if( senden->verbinde( port, ip ) )
    {
        if( senden->sendeVerschlüsselt( "\1", 1 ) )
        {
            char serverReturn = 0;
            senden->sendeVerschlüsselt( (char*)&klientId, 4 );
            char ret = 0;
            senden->getNachrichtVerschlüsselt( &ret, 1 );
            if( ret != 1 )
            {
                if( ret == 3 )
                {
                    char byte = 0;
                    senden->getNachrichtVerschlüsselt( &byte, 1 );
                    char *f = new char[ byte + 1 ];
                    f[ byte ] = 0;
                    senden->getNachrichtVerschlüsselt( f, byte );
                    fehler->setText( f );
                    delete[]f;
                }
                senden->sendeVerschlüsselt( "\3", 1 );
                senden->getNachrichtVerschlüsselt( &serverReturn, 1 );
                senden->trenne();
                WMessageBox( 0, new Text( "Fehler" ), new Text( "Server akzeptiert den Klient nicht" ), MB_ICONERROR );
                UNLOCK( &cs );
                return 0;
            }
            char *sl = 0;
            char slLän = getSchlüssel( &sl );
            senden->setSendeSchlüssel( sl, slLän );
            senden->setEmpfangSchlüssel( sl, slLän );
            delete[] sl;
            if( !empfangen )
                empfangen = new Klient();
            unsigned char schlüssel[ 20 ] = { 253, 234, 211, 132, 121, 230, 95, 145, 201, 13, 43, 77, 153, 223, 253, 69, 234, 43, 52, 99 };
            empfangen->setSendeSchlüssel( (char*)schlüssel, 20 );
            empfangen->setEmpfangSchlüssel( (char*)schlüssel, 20 );
            if( empfangen->verbinde( senden->getServerPort(), senden->getServerIp() ) )
            {
                start();
                verbunden = 1;
                UNLOCK( &cs );
                return 1;
            }
            else
            {
                fehler->setText( "Der dir zugewiesene Spiel Server kann dir keine Nachrichten senden." );
                UNLOCK( &cs );
                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();
    }
    UNLOCK( &cs );
    return 0;
}

bool SpielKlient::spielErstelltAnnehmen() // klient ist bereit dem erstellten Spiel beizutreten
{
    if( !senden )
        return 0;
    char ret = 0;
    LOCK( &cs );
    senden->sendeVerschlüsselt( "\4", 1 );
    senden->getNachrichtVerschlüsselt( &ret, 1 );
    UNLOCK( &cs );
    return ret == 1;
}

bool SpielKlient::spielErstelltAblehnen() // klient ist nicht bereit dem erstellten Spiel beizutreten
{
    if( !senden )
        return 0;
    char ret = 0;
    LOCK( &cs );
    senden->sendeVerschlüsselt( "\5", 1 );
    senden->getNachrichtVerschlüsselt( &ret, 1 );
    UNLOCK( &cs );
    return ret == 1;
}

bool SpielKlient::spielErstelltTeamWechseln( int team ) // wechselt das Team
{
    if( !senden )
        return 0;
    char ret = 0;
    LOCK( &cs );
    senden->sendeVerschlüsselt( "\6", 1 );
    senden->getNachrichtVerschlüsselt( &ret, 1 );
    if( !ret )
    {
        UNLOCK( &cs );
        return 0;
    }
    senden->sendeVerschlüsselt( (char*)&team, 4 );
    senden->getNachrichtVerschlüsselt( &ret, 1 );
    UNLOCK( &cs );
    return ret == 1;
}

bool SpielKlient::spielErstelltTeamFertig() // bestetigt die Team Auswahl
{
    if( !senden )
        return 0;
    char ret = 0;
    LOCK( &cs );
    senden->sendeVerschlüsselt( "\xE", 1 );
    senden->getNachrichtVerschlüsselt( &ret, 1 );
    UNLOCK( &cs );
    return ret == 1;
}

bool SpielKlient::spielErstelltChatNachricht( char *nachricht ) // sendet eine Chat Nachricht an die mitglieder des erstellten Spiels
{
    char län = textLänge( nachricht );
    if( !län )
        return 1;
    if( !senden )
        return 0;
    char ret = 0;
    LOCK( &cs );
    senden->sendeVerschlüsselt( "\7", 1 );
    senden->getNachrichtVerschlüsselt( &ret, 1 );
    if( !ret )
    {
        UNLOCK( &cs );
        return 0;
    }
    senden->sendeVerschlüsselt( &län, 1 );
    senden->sendeVerschlüsselt( nachricht, län );
    senden->getNachrichtVerschlüsselt( &ret, 1 );
    UNLOCK( &cs );
    return ret == 1;
}

bool SpielKlient::bereitZumLaden() // Klient ist bereit zum Laden
{
    if( !senden )
        return 0;
    char ret = 0;
    LOCK( &cs );
    senden->sendeVerschlüsselt( "\x9", 1 );
    senden->getNachrichtVerschlüsselt( &ret, 1 );
    UNLOCK( &cs );
    return ret == 1;
}

bool SpielKlient::setLadenProzent( int prozent ) // Setzt den Fortschritt des Klients
{
    if( !senden )
        return 0;
    char ret = 0;
    LOCK( &cs );
    senden->sendeVerschlüsselt( "\xC", 1 );
    senden->getNachrichtVerschlüsselt( &ret, 1 );
    if( ret )
    {
        senden->sendeVerschlüsselt( (char*)&prozent, 4 );
        senden->getNachrichtVerschlüsselt( &ret, 1 );
    }
    UNLOCK( &cs );
    return ret == 1;
}

bool SpielKlient::bereitZumSpiel() // Klient ist bereit zum Spiel
{
    if( !senden )
        return 0;
    char ret = 0;
    LOCK( &cs );
    senden->sendeVerschlüsselt( "\xA", 1 );
    senden->getNachrichtVerschlüsselt( &ret, 1 );
    UNLOCK( &cs );
    return ret == 1;
}

bool SpielKlient::spielNachricht( short län, char *bytes ) // Nachricht während des Spiels
{
    if( !senden )
        return 0;
    LOCK( &cs );
    senden->sendeVerschlüsselt( "\xB", 1 );
    senden->sende( (char*)&län, 2 );
    senden->sende( bytes, län );
    UNLOCK( &cs );
    return 1;
}

bool SpielKlient::statistikNachricht( short län, char *bytes ) // Nachricht während der Statistik
{
    if( !senden )
        return 0;
    char ret = 0;
    LOCK( &cs );
    senden->sendeVerschlüsselt( "\xD", 1 );
    senden->getNachrichtVerschlüsselt( &ret, 1 );
    if( ret )
    {
        senden->sendeVerschlüsselt( (char*)&län, 2 );
        senden->sendeVerschlüsselt( bytes, län );
        senden->getNachrichtVerschlüsselt( &ret, 1 );
    }
    UNLOCK( &cs );
    return ret == 1;
}

bool SpielKlient::keepAlive() // Erhält die Verbindung aufrecht
{
    if( !verbunden )
        return 0;
    char res = 0;
    if( !TryEnterCriticalSection( &cs ) )
        return 1;
    senden->sendeVerschlüsselt( "\xF", 1 );
    senden->getNachrichtVerschlüsselt( &res, 1 );
    UNLOCK( &cs );
    if( res != 1 )
        trenne();
    return res == 1;
}

bool SpielKlient::trenne() // trennt sich von dem Chat Server
{
    if( !verbunden )
        return 1;
    verbunden = 0;
    LOCK( &cs );
    senden->sendeVerschlüsselt( "\3", 1 );
    char serverReturn = 0;
    senden->getNachrichtVerschlüsselt( &serverReturn, 1 );
    senden->trenne();
    warteAufThread( 2000 );
    empfangen->trenne();
    UNLOCK( &cs );
    run = 0;
    ende();
    return 1;
}

void SpielKlient::thread() // empfangen von Nachrichten
{
    if( !verbunden || !empfangen || !senden )
        return;
    empfangen->sendeVerschlüsselt( "\1", 1 );
    empfangen->sendeVerschlüsselt( (char*)&klientId, 4 );
    char res = 0;
    empfangen->getNachrichtVerschlüsselt( &res, 1 );
    if( res == 3 )
    {
        char län = 0;
        empfangen->getNachrichtVerschlüsselt( &län, 1 );
        char *nachricht = new char[ län + 1 ];
        nachricht[ län ] = 0;
        empfangen->getNachrichtVerschlüsselt( nachricht, län );
        fehler->setText( nachricht );
        delete[]nachricht;
    }
    if( res == 1 )
    {
        char *sl = 0;
        char slLän = getSchlüssel( &sl );
        empfangen->setSendeSchlüssel( sl, slLän );
        empfangen->setEmpfangSchlüssel( 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->getNachrichtVerschlüsselt( &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->getNachrichtVerschlüsselt( &sekunden, 1 );
                if( nachLogin && nachLogin->zSpielenFenster() )
                    nachLogin->zSpielenFenster()->spielGefundenZeitVerbleibend( sekunden );
            }
            break;
        case 2: // SpielErstellt abbruch
            if( 1 )
            {
                char län = 0;
                empfangen->getNachrichtVerschlüsselt( &län, 1 );
                char *grund = new char[ län + 1 ];
                grund[ län ] = 0;
                empfangen->getNachrichtVerschlüsselt( 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->getNachrichtVerschlüsselt( &länge, 1 );
                char *txt = new char[ länge + 1 ];
                txt[ länge ] = 0;
                empfangen->getNachrichtVerschlüsselt( 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->getNachrichtVerschlüsselt( &stunden, 1 );
                empfangen->getNachrichtVerschlüsselt( &minuten, 1 );
                empfangen->getNachrichtVerschlüsselt( &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->getNachrichtVerschlüsselt( (char*)&sts->spielerAnzahl, 4 );
                empfangen->getNachrichtVerschlüsselt( (char*)&sts->teamAnzahl, 4 );
                for( int i = 0; i < sts->spielerAnzahl; i++ )
                {
                    int farbe = 0;
                    empfangen->getNachrichtVerschlüsselt( (char*)&farbe, 4 );
                    sts->spielerFarbe->set( farbe, i );
                }
                for( int i = 0; i < sts->teamAnzahl; i++ )
                {
                    int farbe = 0;
                    empfangen->getNachrichtVerschlüsselt( (char*)&farbe, 4 );
                    sts->teamFarbe->set( farbe, i );
                }
                for( int i = 0; i < sts->teamAnzahl; i++ )
                {
                    char län = 0;
                    empfangen->getNachrichtVerschlüsselt( &län, 1 );
                    char *name = new char[ län + 1 ];
                    name[ län ] = 0;
                    if( län )
                        empfangen->getNachrichtVerschlüsselt( 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->getNachrichtVerschlüsselt( (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->getNachrichtVerschlüsselt( (char*)&accountId, 4 );
                if( nachLogin && nachLogin->zSpielenFenster() )
                    nachLogin->zSpielenFenster()->teamAuswahlAddSpieler( accountId );
            }
            break;
        case 8: // SpielErstellt Spieler entfernt
            if( 1 )
            {
                int accountId = 0;
                empfangen->getNachrichtVerschlüsselt( (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->getNachrichtVerschlüsselt( (char*)&accountId, 4 );
                empfangen->getNachrichtVerschlüsselt( (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->getNachrichtVerschlüsselt( &län, 1 );
                char *nachricht = new char[ län + 1 ];
                nachricht[ län ] = 0;
                if( län )
                    empfangen->getNachrichtVerschlüsselt( nachricht, län );
                if( nachLogin && nachLogin->zSpielenFenster() )
                    nachLogin->zSpielenFenster()->teamAuswahlChatNachricht( nachricht );
                delete[] nachricht;
            }
            break;
        case 0xB: // Spiel gefunden
            if( 1 )
            {
                int karteId = 0;
                empfangen->getNachrichtVerschlüsselt( (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->getNachrichtVerschlüsselt( (char*)&accId, 4 );
                empfangen->getNachrichtVerschlüsselt( (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->getNachrichtVerschlüsselt( (char*)&accId, 4 );
                empfangen->getNachrichtVerschlüsselt( (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->getNachrichtVerschlüsselt( (char*)&accId, 4 );
                empfangen->getNachrichtVerschlüsselt( (char*)&ping, 4 );
                if( nachLogin->zImSpiel() && nachLogin->zImSpiel()->istSichtbar() )
                    nachLogin->zImSpiel()->lBSetSpielerPing( accId, ping );
            }
            break;
        case 0x10: // ping
            empfangen->sendeVerschlüsselt( "\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->getNachrichtVerschlüsselt( (char*)&län, 2 );
                char *n = new char[ län ];
                empfangen->getNachrichtVerschlüsselt( n, län );
                if( nachLogin && nachLogin->zSpielenFenster() )
                    nachLogin->zSpielenFenster()->statistikNachricht( län, n );
                delete[] n;
            }
            break;
        case 0x14: // ping
            empfangen->sendeVerschlüsselt( "\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;
}