#include "ShopServer.h"
#include <iostream>
#include <Klient.h>
#include <Globals.h>

// Inhalt der ShopServer Klasse aus ShopServer.h
// Konstruktor 
ShopServer::ShopServer( InitDatei* zIni )
    : Thread()
{
    Network::Start( 100 );
    std::cout << "ShS: Verbindung mit Datenbank wird hergestellt...\n";
    db = new ShSDatenbank( zIni );
    klients = new RCArray< ShSKlient >();
    empfangen = 0;
    gesendet = 0;
    fehler = new Text();
    ini = dynamic_cast<InitDatei*>(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 << "ShS: Starten des Admin Servers...\n";
    if( !aServer->verbinde( (unsigned short)TextZuInt( ini->zWert( "AdminServerPort" )->getText(), 10 ), 10 ) )
    {
        std::cout << "ShS: Der Admin Server konnte nicht gestartet werden. Das Programm wird beendet.\n";
        exit( 1 );
    }
    db->setServerStatus( id, 2 );
    end = 0;
    nichtPausiert = 0;
    InitializeCriticalSection( &cs );
    if( zIni->zWert( "Aktiv" )->istGleich( "TRUE" ) )
    {
        serverStarten();
        serverFortsetzen();
    }
    updater = new Updater( dynamic_cast<ShopServer*>(getThis()) );
}

// Destruktor 
ShopServer::~ShopServer()
{
    updater->release();
    fehler->release();
    server->trenne();
    server->release();
    aServer->trenne();
    aServer->release();
    if( klients )
        klients->release();
    ini->release();
    db->release();
    DeleteCriticalSection( &cs );
}

// nicht constant 
void ShopServer::runn()
{
    while( !end && aServer->isConnected() )
    {
        SSLSKlient* klient;
        klient = aServer->getKlient();
        if( end && klient )
        {
            klient->trenne();
            klient = (SSLSKlient*)klient->release();
            Sleep( 1000 );
            return;
        }
        if( !klient )
            continue;
        ShSAKlient* clHandle = new ShSAKlient( klient, dynamic_cast<ShopServer*>(getThis()) );
        clHandle->start();
    }
}

void ShopServer::thread()
{
    while( server->isConnected() )
    {
        SKlient* klient;
        klient = server->getKlient();
        if( !klient )
            continue;
        Framework::getThreadRegister()->cleanUpClosedThreads();
        ShSKlient* clHandle = new ShSKlient( klient, dynamic_cast<ShopServer*>(getThis()) );
        EnterCriticalSection( &cs );
        klients->add( clHandle );
        LeaveCriticalSection( &cs );
        clHandle->start();
    }
}

void ShopServer::close()
{
    db->setServerStatus( id, 1 );
    server->trenne();
#ifdef WIN32
    warteAufThread( 1000 );
#endif
    EnterCriticalSection( &cs );
    for( int i = 0; i < klients->getEintragAnzahl(); i++ )
        klients->z( i )->absturz();
    klients = (RCArray<ShSKlient>*) klients->release();
    LeaveCriticalSection( &cs );
    ende();
    run = 0;
    end = 1;
    Klient* klient = new Klient();
    klient->verbinde( aServer->getPort(), "127.0.0.1" );
    Sleep( 500 );
    aServer->trenne();
    klient->release();
}

bool ShopServer::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();
        return 1;
    }
    else
    {
        serverBeenden();
        fehler->setText( "Der Server konnte nicht gestartet werden: Eventuell ist der Port in benutzung." );
        return 0;
    }
}

bool ShopServer::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 ShopServer::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 ShopServer::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;
    }
    nichtPausiert = 0;
    ende();
    if( server )
        server->trenne();
    return 1;
}

bool ShopServer::setMaxKarten( int mk )
{
    if( !db->setMaxKarten( id, mk ) )
    {
        fehler->setText( "Die maximale Anzahl der Karten konnte nicht gesetzt werden:\n" );
        fehler->append( db->getLetzterFehler() );
        return 0;
    }
    ini->setWert( "MaxKarten", Text() += mk );
    return 1;
}

bool ShopServer::absturzKlient( int klientId )
{
    bool gefunden = 0;
    EnterCriticalSection( &cs );
    for( int i = 0; i < klients->getEintragAnzahl(); i++ )
    {
        if( klients->z( i )->getKlientNummer() == klientId )
        {
            klients->z( i )->absturz();
            klients->remove( i );
            gefunden = 1;
            break;
        }
    }
    LeaveCriticalSection( &cs );
    return gefunden;
}

bool ShopServer::removeKlient( ShSKlient* zKlient )
{
    bool gefunden = 0;
    EnterCriticalSection( &cs );
    for( int i = 0; i < klients->getEintragAnzahl(); i++ )
    {
        if( klients->z( i ) == zKlient )
        {
            klients->remove( i );
            gefunden = 1;
            break;
        }
    }
    LeaveCriticalSection( &cs );
    return gefunden;
}

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

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

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

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

ShSDatenbank* ShopServer::zDB() const
{
    return db;
}

bool ShopServer::hatClients() const
{
    return klients->hat( 0 );
}

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

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

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


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

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

// nicht constant 
void ShSAKlient::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 = shs->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( shs->zDB()->adminHatRecht( adminId, Admin_Recht::ShSStarten ) )
                    {
                        if( !shs->serverStarten() )
                        {
                            Text* err = new Text();
                            err->append( shs->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( shs->zDB()->adminHatRecht( adminId, Admin_Recht::ShSBeenden ) )
                    {
                        if( shs->serverBeenden() )
                            klient->sende( "\1", 1 );
                        else
                        {
                            Text* err = new Text();
                            err->append( shs->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( shs->isRunning() )
                    {
                        if( shs->zDB()->adminHatRecht( adminId, Admin_Recht::ShSBeenden ) )
                        {
                            if( shs->serverBeenden() )
                                ok = 1;
                            else
                            {
                                Text* err = new Text();
                                err->append( shs->getLetzterFehler() );
                                errorZuKlient( err->getText() );
                                err->release();
                            }
                        }
                        else
                            errorZuKlient( "Du bist nicht berechtigt den Server zu beenden." );
                    }
                    else
                        ok = 1;
                    if( ok && shs->hatClients() )
                    {
                        errorZuKlient( "Es sind noch Klients Online. Bitte versuche es später erneut." );
                        break;
                    }
                    if( ok )
                    {
                        klient->sende( "\1", 1 );
                        std::cout << "ShS: Der Server wird von Benutzer " << adminId << " heruntergefahren.\n";
                        shs->close();
                        br = 1;
                    }
                }
                break;
            case 7: // Programm abstürzen
                if( !adminId )
                    errorZuKlient( "Du musst dich einloggen." );
                else
                {
                    bool ok = 0;
                    if( shs->isRunning() )
                    {
                        if( shs->zDB()->adminHatRecht( adminId, Admin_Recht::ShSBeenden ) )
                        {
                            shs->serverBeenden();
                            ok = 1;
                        }
                        else
                            errorZuKlient( "Du bist nicht berechtigt den Server zu beenden." );
                    }
                    else
                        ok = 1;
                    if( ok )
                    {
                        klient->sende( "\1", 1 );
                        std::cout << "ShS: Der Server wurde von Benutzer " << adminId << " terminiert.\n";
                        shs->close();
                        br = 1;
                    }
                }
                break;
            case 8: // Status Frage
                if( 1 )
                {
                    char status = 0;
                    if( shs->isRunning() )
                    {
                        status = 1;
                        if( shs->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( shs->zDB()->adminHatRecht( adminId, Admin_Recht::ShSPausieren ) )
                    {
                        bool ok = 0;
                        if( pause )
                            ok = shs->serverPause();
                        else
                            ok = shs->serverFortsetzen();
                        if( ok )
                            klient->sende( "\1", 1 );
                        else
                        {
                            Text* err = new Text();
                            err->append( shs->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 Karten setzen
                if( !adminId )
                    errorZuKlient( "Du musst dich einloggen." );
                else
                {
                    klient->sende( "\1", 1 );
                    int maxK = 0;
                    klient->getNachricht( (char*)&maxK, 4 );
                    if( shs->zDB()->adminHatRecht( adminId, Admin_Recht::ShSMKChange ) )
                    {
                        if( shs->setMaxKarten( maxK ) )
                            klient->sende( "\1", 1 );
                        else
                        {
                            Text* err = new Text();
                            err->append( shs->getLetzterFehler() );
                            errorZuKlient( err->getText() );
                            err->release();
                        }
                    }
                    else
                        errorZuKlient( "Du bist nicht berechtigt die maximale Anzahl der Karten zu verändern." );
                }
                break;
            case 0xC: // klient absturtz
                if( 1 )
                {
                    klient->sende( "\1", 1 );
                    int klientId = 0;
                    klient->getNachricht( (char*)&klientId, 4 );
                    if( klientId && shs->absturzKlient( klientId ) )
                        klient->sende( "\1", 1 );
                    else
                        klient->sende( "\0", 1 );
                }
                break;
            default:
                errorZuKlient( "Unbekannte Nachricht!" );
                break;
            }
            if( br )
                break;
            shs->addEmpfangen( klient->getDownloadBytes( 1 ) );
            shs->addGesendet( klient->getUploadBytes( 1 ) );
        }
    }
    shs->addEmpfangen( klient->getDownloadBytes( 1 ) );
    shs->addGesendet( klient->getUploadBytes( 1 ) );
    delete this;
}

void ShSAKlient::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 ShSKlient aus ShopServer.h
// Konstruktor 
ShSKlient::ShSKlient( SKlient* klient, ShopServer* shs )
    : Thread()
{
    this->klient = klient;
    unsigned char key[ 20 ] = { 218, 226, 231, 216, 68, 63, 132, 155, 49, 235, 239, 0, 19, 23, 116, 244, 235, 21, 189, 53 };
    klient->setSendeKey( (char*)key, 20 );
    klient->setEmpfangKey( (char*)key, 20 );
    klientNummer = 0;
    this->shs = shs;
}

// Destruktor 
ShSKlient::~ShSKlient()
{
    klient->release();
    shs->release();
}

// nicht constant
void ShSKlient::absturz()
{
    ende();
    klient->trenne();
    shs->zDB()->unregisterKlient( klientNummer, shs->getId() );
}

void ShSKlient::thread()
{
    while( 1 )
    {
        char c = 0;
        if( !klient->getNachrichtEncrypted( &c, 1 ) )
            break;
        else
        {
            bool br = 0;
            switch( c )
            {
            case 1: // Klient identifikation
                klient->getNachrichtEncrypted( (char*)&klientNummer, 4 );
                if( !shs->zDB()->proveKlient( klientNummer, shs->getId() ) )
                {
                    klientNummer = 0;
                    errorZuKlient( "Du bist nicht für diesen Server eingetragen" );
                }
                else
                {
                    Text* key = shs->zDB()->getKlientKey( klientNummer );
                    if( !key )
                        errorZuKlient( "Es konnte kein Key ermittelt werden." );
                    else
                    {
                        klient->sendeEncrypted( "\1", 1 );
                        klient->setEmpfangKey( *key, key->getLength() );
                        klient->setSendeKey( *key, key->getLength() );
                        key->release();
                    }
                }
                break;
            case 2: // Main / Erhaltung Server message
                if( 1 )
                {
                    char befehl = 0;
                    klient->getNachrichtEncrypted( &befehl, 1 );
                    switch( befehl )
                    {
                    case 2: // klient absturtz
                        if( 1 )
                        {
                            int klientId = 0;
                            klient->getNachrichtEncrypted( (char*)&klientId, 4 );
                            if( klientId && shs->absturzKlient( klientId ) )
                                klient->sendeEncrypted( "\1", 1 );
                            else
                                klient->sendeEncrypted( "\0", 1 );
                        }
                        break;
                    default:
                        errorZuKlient( "Befehl nicht bekannt!" );
                        break;
                    }
                }
                break;
            case 3: // Verbindungsende
                br = 1;
                klient->sendeEncrypted( "\1", 1 );
                break;
            case 4: // unregister Klient
                if( !klientNummer )
                {
                    errorZuKlient( "Du bist nicht Identifiziert." );
                    break;
                }
                shs->zDB()->unregisterKlient( klientNummer, shs->getId() );
                klient->sendeEncrypted( "\1", 1 );
                break;
            case 5: // nach Spielen suchen
                if( klientNummer )
                {
                    klient->sendeEncrypted( "\1", 1 );
                    unsigned char len = 0;
                    klient->getNachrichtEncrypted( (char*)&len, 1 );
                    char* suche = new char[ len + 1 ];
                    suche[ (int)len ] = 0;
                    if( len )
                        klient->getNachrichtEncrypted( suche, len );
                    Array< int >* list = new Array< int >();
                    if( shs->zDB()->getSpielArtListe( klientNummer, suche, list ) )
                    {
                        klient->sendeEncrypted( "\1", 1 );
                        int anz = list->getEintragAnzahl();
                        klient->sendeEncrypted( (char*)&anz, 4 );
                        if( anz )
                        {
                            for( int i = 0; i < anz; i++ )
                            {
                                int w = list->get( i );
                                klient->sendeEncrypted( (char*)&w, 4 );
                            }
                        }
                    }
                    else
                        errorZuKlient( "Bei der Suche ist ein Fehler aufgetreten." );
                    list->release();
                    delete[] suche;
                }
                else
                    errorZuKlient( "Du bist nicht Identifiziert." );
                break;
            case 6: // frage nach Spiel Titelbild
                if( klientNummer )
                {
                    klient->sendeEncrypted( "\1", 1 );
                    int spielId = 0;
                    klient->getNachrichtEncrypted( (char*)&spielId, 4 );
                    if( !shs->zDB()->proveSpiel( shs->getId(), spielId ) )
                    {
                        Text ip = "";
                        int port = 0;
                        if( !shs->zDB()->getSpielShopServerPortIp( spielId, klientNummer, &port, &ip ) )
                            errorZuKlient( "Dieses Spiel ist momentan nicht erreichbar. Bitte versuche es später erneut." );
                        else
                        {
                            klient->sendeEncrypted( "\2", 1 );
                            klient->sendeEncrypted( (char*)&port, 4 );
                            char len = (char)ip.getLength();
                            klient->sendeEncrypted( &len, 1 );
                            klient->sendeEncrypted( ip.getText(), len );
                            br = 1;
                            break;
                        }
                    }
                    Text* pfad = new Text( shs->zInit()->zWert( "ShopSeitenPfad" )->getText() );
                    pfad->append( "/spiele/" );
                    pfad->append( spielId );
                    pfad->append( "/titelbg.ltdb" );
                    if( !DateiExistiert( pfad->getText() ) )
                    {
                        errorZuKlient( "Das Spiel ist nicht vorhanden." );
                        pfad->release();
                        break;
                    }
                    klient->sendeEncrypted( "\1", 1 );
                    Datei* dat = new Datei();
                    dat->setDatei( pfad );
                    dat->open( Datei::Style::lesen );
                    __int64 gr = dat->getSize();
                    klient->sendeEncrypted( (char*)&gr, 8 );
                    char* bytes = new char[ 2048 ];
                    while( gr > 0 )
                    {
                        int len = gr > 2048 ? 2048 : (int)gr;
                        dat->lese( bytes, len );
                        klient->sende( bytes, len );
                        gr -= len;
                    }
                    dat->close();
                    dat->release();
                    delete[] bytes;
                }
                else
                    errorZuKlient( "Du bist nicht Identifiziert." );
                break;
            case 7: // frage nach Spiel Seite
                if( klientNummer )
                {
                    klient->sendeEncrypted( "\1", 1 );
                    int spielId = 0;
                    klient->getNachrichtEncrypted( (char*)&spielId, 4 );
                    if( !shs->zDB()->proveSpiel( shs->getId(), spielId ) )
                    {
                        Text ip = "";
                        int port = 0;
                        if( !shs->zDB()->getSpielShopServerPortIp( spielId, klientNummer, &port, &ip ) )
                            errorZuKlient( "Dieses Spiel ist momentan nicht erreichbar. Bitte versuche es später erneut." );
                        else
                        {
                            klient->sendeEncrypted( "\2", 1 );
                            klient->sendeEncrypted( (char*)&port, 4 );
                            char len = (char)ip.getLength();
                            klient->sendeEncrypted( &len, 1 );
                            klient->sendeEncrypted( ip.getText(), len );
                            br = 1;
                            break;
                        }
                    }
                    Text* pfad = new Text( shs->zInit()->zWert( "ShopSeitenPfad" )->getText() );
                    pfad->append( "/spiele/" );
                    pfad->append( spielId );
                    pfad->append( "/seite" );
                    if( !DateiExistiert( pfad->getText() ) )
                    {
                        errorZuKlient( "Das Spiel ist nicht vorhanden." );
                        pfad->release();
                        break;
                    }
                    klient->sendeEncrypted( "\1", 1 );
                    Datei* dat = new Datei();
                    dat->setDatei( pfad );
                    RCArray< Text >* list = dat->getDateiListe();
                    int dAnz = list->getEintragAnzahl();
                    for( int i = 0; i < dAnz; i++ )
                    {
                        if( DateiIstVerzeichnis( list->get( i ) ) || list->z( i )->istGleich( "." ) || list->z( i )->istGleich( ".." ) )
                        {
                            list->remove( i );
                            dAnz--;
                            i--;
                        }
                    }
                    klient->sendeEncrypted( (char*)&dAnz, 4 );
                    for( int i = 0; i < dAnz; i++ )
                    {
                        Text* pf = new Text( dat->zPfad()->getText() );
                        pf->append( "/" );
                        pf->append( list->z( i )->getText() );
                        Datei* d = new Datei();
                        d->setDatei( pf );
                        d->open( Datei::Style::lesen );
                        char nl = (char)list->z( i )->getLength();
                        klient->sendeEncrypted( &nl, 1 );
                        klient->sendeEncrypted( list->z( i )->getText(), nl );
                        __int64 gr = d->getSize();
                        klient->sendeEncrypted( (char*)&gr, 8 );
                        char* bytes = new char[ 2048 ];
                        while( gr > 0 )
                        {
                            int len = gr > 2048 ? 2048 : (int)gr;
                            d->lese( bytes, len );
                            klient->sende( bytes, len );
                            gr -= len;
                        }
                        delete[] bytes;
                        d->close();
                        d->release();
                    }
                    list->release();
                    dat->release();
                }
                else
                    errorZuKlient( "Du bist nicht Identifiziert." );
                break;
            case 8: // frage nach Spiel Besitz Status
                if( klientNummer )
                {
                    klient->sendeEncrypted( "\1", 1 );
                    int spielId = 0;
                    klient->getNachrichtEncrypted( (char*)&spielId, 4 );
                    int status = shs->zDB()->getSpielBesitzStatus( klientNummer, spielId );
                    klient->sendeEncrypted( (char*)&status, 4 );
                }
                else
                    errorZuKlient( "Du bist nicht Identifiziert." );
                break;
            case 9: // frage nach Spiel Testversion Status
                if( klientNummer )
                {
                    klient->sendeEncrypted( "\1", 1 );
                    int spielId = 0;
                    klient->getNachrichtEncrypted( (char*)&spielId, 4 );
                    int status = shs->zDB()->getSpielTestversionStatus( klientNummer, spielId );
                    klient->sendeEncrypted( (char*)&status, 4 );
                }
                else
                    errorZuKlient( "Du bist nicht Identifiziert." );
                break;
            case 0xA: // frage, ob Spiel erwerbbar ist
                if( klientNummer )
                {
                    klient->sendeEncrypted( "\1", 1 );
                    int spielId = 0;
                    klient->getNachrichtEncrypted( (char*)&spielId, 4 );
                    int status = shs->zDB()->getSpielErwerbbarStatus( klientNummer, spielId );
                    klient->sendeEncrypted( (char*)&status, 4 );
                }
                else
                    errorZuKlient( "Du bist nicht Identifiziert." );
                break;
            case 0xB: // frage nach Spiel Preis
                if( klientNummer )
                {
                    klient->sendeEncrypted( "\1", 1 );
                    int spielId = 0;
                    klient->getNachrichtEncrypted( (char*)&spielId, 4 );
                    char testVersion = 0;
                    klient->getNachrichtEncrypted( &testVersion, 1 );
                    int status = shs->zDB()->getSpielPreis( spielId, testVersion == 1 );
                    klient->sendeEncrypted( (char*)&status, 4 );
                }
                else
                    errorZuKlient( "Du bist nicht Identifiziert." );
                break;
            case 0xC: // Spiel kaufen
                if( klientNummer )
                {
                    klient->sendeEncrypted( "\1", 1 );
                    int spielId = 0;
                    klient->getNachrichtEncrypted( (char*)&spielId, 4 );
                    char testVersion = 0;
                    klient->getNachrichtEncrypted( &testVersion, 1 );
                    bool ok = shs->zDB()->spielKaufen( klientNummer, spielId, testVersion == 1 );
                    if( ok )
                        klient->sendeEncrypted( "\1", 1 );
                    else
                        errorZuKlient( "Das Spiel konnte nicht erworben werden." );
                }
                else
                    errorZuKlient( "Du bist nicht Identifiziert." );
                break;
            case 0xD: // nach Karten suchen
                if( klientNummer )
                {
                    klient->sendeEncrypted( "\1", 1 );
                    int spielId = 0;
                    klient->getNachrichtEncrypted( (char*)&spielId, 4 );
                    unsigned char len = 0;
                    klient->getNachrichtEncrypted( (char*)&len, 1 );
                    char* suche = new char[ len + 1 ];
                    suche[ (int)len ] = 0;
                    if( len )
                        klient->getNachrichtEncrypted( suche, len );
                    Array< int >* list = new Array< int >();
                    if( shs->zDB()->getKarteListe( klientNummer, spielId, suche, list ) )
                    {
                        klient->sendeEncrypted( "\1", 1 );
                        int anz = list->getEintragAnzahl();
                        klient->sendeEncrypted( (char*)&anz, 4 );
                        if( anz )
                        {
                            for( int i = 0; i < anz; i++ )
                            {
                                int w = list->get( i );
                                klient->sendeEncrypted( (char*)&w, 4 );
                            }
                        }
                    }
                    else
                        errorZuKlient( "Bei der Suche ist ein Fehler aufgetreten." );
                    list->release();
                    delete[] suche;
                }
                else
                    errorZuKlient( "Du bist nicht Identifiziert." );
                break;
            case 0xE: // frage nach Karte Titelbild
                if( klientNummer )
                {
                    klient->sendeEncrypted( "\1", 1 );
                    int karteId = 0;
                    klient->getNachrichtEncrypted( (char*)&karteId, 4 );
                    if( !shs->zDB()->proveKarte( shs->getId(), karteId ) )
                    {
                        Text ip = "";
                        int port = 0;
                        if( !shs->zDB()->getKarteShopServerPortIp( karteId, klientNummer, &port, &ip ) )
                            errorZuKlient( "Diese Karte ist momentan nicht erreichbar. Bitte versuche es später erneut." );
                        else
                        {
                            klient->sendeEncrypted( "\2", 1 );
                            klient->sendeEncrypted( (char*)&port, 4 );
                            char len = (char)ip.getLength();
                            klient->sendeEncrypted( &len, 1 );
                            klient->sendeEncrypted( ip.getText(), len );
                            br = 1;
                            break;
                        }
                    }
                    Text* pfad = new Text( shs->zInit()->zWert( "ShopSeitenPfad" )->getText() );
                    pfad->append( "/karten/" );
                    pfad->append( karteId );
                    pfad->append( "/titelbg.ltdb" );
                    if( !DateiExistiert( pfad->getText() ) )
                    {
                        errorZuKlient( "Das Spiel ist nicht vorhanden." );
                        pfad->release();
                        break;
                    }
                    klient->sendeEncrypted( "\1", 1 );
                    Datei* dat = new Datei();
                    dat->setDatei( pfad );
                    dat->open( Datei::Style::lesen );
                    __int64 gr = dat->getSize();
                    klient->sendeEncrypted( (char*)&gr, 8 );
                    char* bytes = new char[ 2048 ];
                    while( gr > 0 )
                    {
                        int len = gr > 2048 ? 2048 : (int)gr;
                        dat->lese( bytes, len );
                        klient->sende( bytes, len );
                        gr -= len;
                    }
                    dat->close();
                    dat->release();
                    delete[] bytes;
                }
                else
                    errorZuKlient( "Du bist nicht Identifiziert." );
                break;
            case 0xF: // frage nach Karte Seite
                if( klientNummer )
                {
                    klient->sendeEncrypted( "\1", 1 );
                    int karteId = 0;
                    klient->getNachrichtEncrypted( (char*)&karteId, 4 );
                    if( !shs->zDB()->proveKarte( shs->getId(), karteId ) )
                    {
                        Text ip = "";
                        int port = 0;
                        if( !shs->zDB()->getKarteShopServerPortIp( karteId, klientNummer, &port, &ip ) )
                            errorZuKlient( "Diese Karte ist momentan nicht erreichbar. Bitte versuche es später erneut." );
                        else
                        {
                            klient->sendeEncrypted( "\2", 1 );
                            klient->sendeEncrypted( (char*)&port, 4 );
                            char len = (char)ip.getLength();
                            klient->sendeEncrypted( &len, 1 );
                            klient->sendeEncrypted( ip.getText(), len );
                            br = 1;
                            break;
                        }
                    }
                    Text* pfad = new Text( shs->zInit()->zWert( "ShopSeitenPfad" )->getText() );
                    pfad->append( "/karten/" );
                    pfad->append( karteId );
                    pfad->append( "/seite" );
                    if( !DateiExistiert( pfad->getText() ) )
                    {
                        errorZuKlient( "Das Spiel ist nicht vorhanden." );
                        pfad->release();
                        break;
                    }
                    klient->sendeEncrypted( "\1", 1 );
                    Datei* dat = new Datei();
                    dat->setDatei( pfad );
                    RCArray< Text >* list = dat->getDateiListe();
                    int dAnz = list->getEintragAnzahl();
                    for( int i = 0; i < dAnz; i++ )
                    {
                        if( DateiIstVerzeichnis( list->get( i ) ) || list->z( i )->istGleich( "." ) || list->z( i )->istGleich( ".." ) )
                        {
                            list->remove( i );
                            dAnz--;
                            i--;
                        }
                    }
                    klient->sendeEncrypted( (char*)&dAnz, 4 );
                    for( int i = 0; i < dAnz; i++ )
                    {
                        Text* pf = new Text( dat->zPfad()->getText() );
                        pf->append( "/" );
                        pf->append( list->z( i )->getText() );
                        Datei* d = new Datei();
                        d->setDatei( pf );
                        d->open( Datei::Style::lesen );
                        char nl = (char)list->z( i )->getLength();
                        klient->sendeEncrypted( &nl, 1 );
                        klient->sendeEncrypted( list->z( i )->getText(), nl );
                        __int64 gr = d->getSize();
                        klient->sendeEncrypted( (char*)&gr, 8 );
                        char* bytes = new char[ 2048 ];
                        while( gr > 0 )
                        {
                            int len = gr > 2048 ? 2048 : (int)gr;
                            d->lese( bytes, len );
                            klient->sende( bytes, len );
                            gr -= len;
                        }
                        delete[] bytes;
                        d->close();
                        d->release();
                    }
                    list->release();
                    dat->release();
                }
                else
                    errorZuKlient( "Du bist nicht Identifiziert." );
                break;
            case 0x10: // frage nach Karte Besitz Status
                if( klientNummer )
                {
                    klient->sendeEncrypted( "\1", 1 );
                    int karteId = 0;
                    klient->getNachrichtEncrypted( (char*)&karteId, 4 );
                    int status = shs->zDB()->getKarteBesitzStatus( klientNummer, karteId );
                    klient->sendeEncrypted( (char*)&status, 4 );
                }
                else
                    errorZuKlient( "Du bist nicht Identifiziert." );
                break;
            case 0x11: // frage nach Karte Testversion Status
                if( klientNummer )
                {
                    klient->sendeEncrypted( "\1", 1 );
                    int karteId = 0;
                    klient->getNachrichtEncrypted( (char*)&karteId, 4 );
                    int status = shs->zDB()->getKarteTestversionStatus( klientNummer, karteId );
                    klient->sendeEncrypted( (char*)&status, 4 );
                }
                else
                    errorZuKlient( "Du bist nicht Identifiziert." );
                break;
            case 0x12: // frage, ob Karte erwerbbar ist
                if( klientNummer )
                {
                    klient->sendeEncrypted( "\1", 1 );
                    int karteId = 0;
                    klient->getNachrichtEncrypted( (char*)&karteId, 4 );
                    int status = shs->zDB()->getKarteErwerbbarStatus( klientNummer, karteId );
                    klient->sendeEncrypted( (char*)&status, 4 );
                }
                else
                    errorZuKlient( "Du bist nicht Identifiziert." );
                break;
            case 0x13: // frage nach Karte Preis
                if( klientNummer )
                {
                    klient->sendeEncrypted( "\1", 1 );
                    int karteId = 0;
                    klient->getNachrichtEncrypted( (char*)&karteId, 4 );
                    char testVersion = 0;
                    klient->getNachrichtEncrypted( &testVersion, 1 );
                    int status = shs->zDB()->getKartePreis( karteId, testVersion == 1 );
                    klient->sendeEncrypted( (char*)&status, 4 );
                }
                else
                    errorZuKlient( "Du bist nicht Identifiziert." );
                break;
            case 0x14: // Karte kaufen
                if( klientNummer )
                {
                    klient->sendeEncrypted( "\1", 1 );
                    int karteId = 0;
                    klient->getNachrichtEncrypted( (char*)&karteId, 4 );
                    char testVersion = 0;
                    klient->getNachrichtEncrypted( &testVersion, 1 );
                    bool ok = shs->zDB()->karteKaufen( klientNummer, karteId, testVersion == 1 );
                    if( ok )
                        klient->sendeEncrypted( "\1", 1 );
                    else
                        errorZuKlient( "Die Karte konnte nicht erworben werden." );
                }
                else
                    errorZuKlient( "Du bist nicht Identifiziert." );
                break;
            case 0x15: // ping
                if( 1 )
                {
                    if( !klientNummer )
                    {
                        errorZuKlient( "Du bist nicht Identifiziert." );
                        break;
                    }
                    klient->sendeEncrypted( "\1", 1 );
                }
                break;
            default:
                errorZuKlient( "Unbekannte Nachricht!" );
                break;
            }
            if( br )
                break;
            shs->addEmpfangen( klient->getDownloadBytes( 1 ) );
            shs->addGesendet( klient->getUploadBytes( 1 ) );
        }
    }
    shs->addEmpfangen( klient->getDownloadBytes( 1 ) );
    shs->addGesendet( klient->getUploadBytes( 1 ) );
    shs->removeKlient( this ); // delete this
}

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

int ShSKlient::getKlientNummer() const // gibt die KlientId zurück
{
    return klientNummer;
}