#include "../KSGKlient.h"
#include "../KSGServer.h"
#include <Datei.h>

// Inhalt der NewsKlients Klasse aus KSGKlient.h
// Konstruktor
NewsKlient::NewsKlient()
{
    verbunden = 0;
    klient = 0;
    fehler = new Text( "" );
    klientId = getKlientId();
    ref = 1;
    InitializeCriticalSection( &cs );
}

// Destruktor
NewsKlient::~NewsKlient()
{
    LOCK( &cs );
    if( klient )
    {
        char serverReturn = 0;
        if( verbunden )
        {
            klient->sendeVerschlüsselt( "\4", 1 );
            klient->getNachrichtVerschlüsselt( &serverReturn, 1 );
            if( serverReturn == 3 )
            {
                char län = 0;
                klient->getNachrichtVerschlüsselt( &län, 1 );
                char *nachricht = new char[ län + 1 ];
                nachricht[ län ] = 0;
                klient->getNachrichtVerschlüsselt( nachricht, län );
                delete[]nachricht;
            }
            klient->sendeVerschlüsselt( "\3", 1 );
            klient->getNachrichtVerschlüsselt( &serverReturn, 1 );
            klient->trenne();
        }
        else
        {
            unsigned char schlüssel[ 20 ] = { 4, 150, 243, 145, 204, 71, 253, 88, 4, 51, 189, 207, 189, 248, 231, 40, 106, 7, 182, 5 };
            klient->setSendeSchlüssel( (char*)schlüssel, 20 );
            klient->setEmpfangSchlüssel( (char*)schlüssel, 20 );
            klient->verbinde( klient->getServerPort(), klient->getServerIp() );
            klient->sendeVerschlüsselt( "\1", 1 );
            klient->sendeVerschlüsselt( (char*)&klientId, 4 );
            klient->getNachrichtVerschlüsselt( &serverReturn, 1 );
            if( serverReturn == 3 )
            {
                char län = 0;
                klient->getNachrichtVerschlüsselt( &län, 1 );
                char *nachricht = new char[ län + 1 ];
                nachricht[ län ] = 0;
                klient->getNachrichtVerschlüsselt( nachricht, län );
                delete[]nachricht;
            }
            else
            {
                char *sl = 0;
                char slLän = getSchlüssel( &sl );
                klient->setSendeSchlüssel( sl, slLän );
                klient->setEmpfangSchlüssel( sl, slLän );
                delete[] sl;
                klient->sendeVerschlüsselt( "\4", 1 );
                klient->getNachrichtVerschlüsselt( &serverReturn, 1 );
                if( serverReturn == 3 )
                {
                    char län = 0;
                    klient->getNachrichtVerschlüsselt( &län, 1 );
                    char *nachricht = new char[ län + 1 ];
                    nachricht[ län ] = 0;
                    klient->getNachrichtVerschlüsselt( nachricht, län );
                    delete[]nachricht;
                }
            }
            klient->sendeVerschlüsselt( "\3", 1 );
            klient->getNachrichtVerschlüsselt( &serverReturn, 1 );
            klient->trenne();
        }
        klient = klient->release();
    }
    fehler = fehler->release();
    UNLOCK( &cs );
    DeleteCriticalSection( &cs );
}

// nicht constant
bool NewsKlient::verbinde() // verbindet ich mit dem News Server
{
    LOCK( &cs );
    if( verbunden )
    {
        UNLOCK( &cs );
        return 1;
    }
    if( !klient )
    {
        char *msIp = getMainServerIp();
        unsigned short msPort = getMainServerPort();
        klient = new Klient();
        unsigned char schlüssel[ 20 ] = { 199, 164, 172, 55, 246, 16, 155, 8, 151, 176, 250, 181, 227, 133, 30, 41, 200, 26, 7, 151 };
        klient->setSendeSchlüssel( (char*)schlüssel, 20 );
        klient->setEmpfangSchlüssel( (char*)schlüssel, 20 );
        if( !klient->verbinde( msPort, msIp ) )
        {
            fehler->setText( "Fehler beim verbinden mit dem Main Server. Bitte versuche es Später erneut." );
            klient = klient->release();
            delete[]msIp;
            UNLOCK( &cs );
            return 0;
        }
        delete[]msIp;
        klient->sende( "\0", 1 ); // Verschlüsselung aktivieren
        klient->sendeVerschlüsselt( "\1", 1 );
        klient->sendeVerschlüsselt( (char*)&klientId, 4 );
        char serverReturn = 0;
        klient->getNachrichtVerschlüsselt( &serverReturn, 1 );
        if( serverReturn == 3 )
        {
            char län = 0;
            klient->getNachrichtVerschlüsselt( &län, 1 );
            char *nachricht = new char[ län + 1 ];
            nachricht[ län ] = 0;
            klient->getNachrichtVerschlüsselt( nachricht, län );
            fehler->setText( nachricht );
            delete[]nachricht;
            klient->sendeVerschlüsselt( "\3", 1 );
            klient->getNachrichtVerschlüsselt( &serverReturn, 1 );
            klient->trenne();
            klient = klient->release();
            UNLOCK( &cs );
            return 0;
        }
        char *sl = 0;
        char slLän = getSchlüssel( &sl );
        klient->setSendeSchlüssel( sl, slLän );
        klient->setEmpfangSchlüssel( sl, slLän );
        delete[] sl;
        klient->sendeVerschlüsselt( "\6\x9", 2 );
        char byte = 0;
        klient->getNachrichtVerschlüsselt( &byte, 1 );
        if( byte == 2 )
        {
            unsigned char lsIp[ 4 ];
            klient->getNachrichtVerschlüsselt( (char *)lsIp, 4 );
            unsigned short lsPort = 0;
            klient->getNachrichtVerschlüsselt( (char*)&lsPort, 2 );
            klient->sendeVerschlüsselt( "\3", 1 );
            klient->getNachrichtVerschlüsselt( &serverReturn, 1 );
            klient->trenne();
            Text *lsIpT = new Text( "" );
            lsIpT->anhängen( (int)lsIp[ 0 ] );
            lsIpT->anhängen( "." );
            lsIpT->anhängen( (int)lsIp[ 1 ] );
            lsIpT->anhängen( "." );
            lsIpT->anhängen( (int)lsIp[ 2 ] );
            lsIpT->anhängen( "." );
            lsIpT->anhängen( (int)lsIp[ 3 ] );
            unsigned char schlüssel[ 20 ] = { 4, 150, 243, 145, 204, 71, 253, 88, 4, 51, 189, 207, 189, 248, 231, 40, 106, 7, 182, 5 };
            klient->setSendeSchlüssel( (char*)schlüssel, 20 );
            klient->setEmpfangSchlüssel( (char*)schlüssel, 20 );
            klient->verbinde( lsPort, lsIpT->getText() );
            lsIpT = lsIpT->release();
            klient->sendeVerschlüsselt( "\3", 1 );
            klient->getNachrichtVerschlüsselt( &serverReturn, 1 );
            klient->trenne();
        }
        if( byte == 3 )
        {
            klient->getNachrichtVerschlüsselt( &byte, 1 );
            char *f = new char[ byte + 1 ];
            f[ byte ] = 0;
            klient->getNachrichtVerschlüsselt( f, byte );
            fehler->setText( f );
            delete[]f;
            klient->sendeVerschlüsselt( "\3", 1 );
            klient->getNachrichtVerschlüsselt( &serverReturn, 1 );
            klient->trenne();
            klient = klient->release();
            UNLOCK( &cs );
            return 0;
        }
    }
    unsigned char schlüssel[ 20 ] = { 4, 150, 243, 145, 204, 71, 253, 88, 4, 51, 189, 207, 189, 248, 231, 40, 106, 7, 182, 5 };
    klient->setSendeSchlüssel( (char*)schlüssel, 20 );
    klient->setEmpfangSchlüssel( (char*)schlüssel, 20 );
    if( klient->verbinde( klient->getServerPort(), klient->getServerIp() ) )
    {
        if( klient->sendeVerschlüsselt( "\1", 1 ) )
        {
            klient->sendeVerschlüsselt( (char*)&klientId, 4 );
            char serverReturn = 0;
            klient->getNachrichtVerschlüsselt( &serverReturn, 1 );
            if( serverReturn == 3 )
            {
                char byte = 0;
                klient->getNachrichtVerschlüsselt( &byte, 1 );
                char *f = new char[ byte + 1 ];
                f[ byte ] = 0;
                klient->getNachrichtVerschlüsselt( f, byte );
                fehler->setText( f );
                delete[]f;
                klient->sendeVerschlüsselt( "\3", 1 );
                klient->getNachrichtVerschlüsselt( &serverReturn, 1 );
                klient->trenne();
                UNLOCK( &cs );
                return 0;
            }
            char *sl = 0;
            char slLän = getSchlüssel( &sl );
            klient->setSendeSchlüssel( sl, slLän );
            klient->setEmpfangSchlüssel( sl, slLän );
            delete[] sl;
            verbunden = 1;
            UNLOCK( &cs );
            return 1;
        }
        else
        {
            fehler->setText( "Der dir zugewiesene News Server hat die Verbindung abgebrochen. Bitte versuche es Später erneut." );
            klient = klient->release();
        }
    }
    else
    {
        fehler->setText( "Der dir zugewiesene News Server antwortet nicht. Bitte versuche es Später erneut." );
        klient = klient->release();
    }
    UNLOCK( &cs );
    return 0;
}

bool NewsKlient::ladeSeite( char *name )
{
    LOCK( &cs );
    if( !verbunden )
        verbinde();
    if( !verbunden )
    {
        UNLOCK( &cs );
        return 0;
    }
    klient->sendeVerschlüsselt( "\5", 1 );
    char ret = 0;
    klient->getNachrichtVerschlüsselt( &ret, 1 );
    if( ret == 1 )
    {
        char län = (char)textLänge( name );
        klient->sendeVerschlüsselt( &län, 1 );
        klient->sendeVerschlüsselt( name, län );
        klient->getNachrichtVerschlüsselt( &ret, 1 );
        if( ret == 1 )
        {
            Text *pfad = new Text( "data/tmp/news/" );
            pfad->anhängen( name );
            if( DateiExistiert( pfad->getThis() ) )
                DateiLöschen( pfad->getThis() );
            pfad->anhängen( "/" );
            int dAnz = 0;
            klient->getNachrichtVerschlüsselt( (char*)&dAnz, 4 );
            for( int i = 0; i < dAnz; i++ )
            {
                char nLän = 0;
                klient->getNachrichtVerschlüsselt( &nLän, 1 );
                char *dName = new char[ nLän + 1 ];
                dName[ nLän ] = 0;
                klient->getNachrichtVerschlüsselt( dName, nLän );
                Text *pf = new Text( pfad->getText() );
                pf->anhängen( dName );
                delete[] dName;
                Datei *d = new Datei();
                d->setDatei( pf );
                d->erstellen();
                d->öffnen( Datei::Style::schreiben );
                __int64 dGr = 0;
                klient->getNachrichtVerschlüsselt( (char*)&dGr, 8 );
                char buffer[ 2048 ];
                while( dGr > 0 )
                {
                    int län = dGr > 2048 ? 2048 : (int)dGr;
                    klient->getNachricht( buffer, län );
                    d->schreibe( buffer, län );
                    dGr -= län;
                }
                d->schließen();
                d->release();
            }
            pfad->release();
        }
    }
    if( ret == 3 )
    {
        klient->getNachrichtVerschlüsselt( &ret, 1 );
        if( ret )
        {
            char *tmp = new char[ ret ];
            klient->getNachrichtVerschlüsselt( tmp, ret );
            delete[] tmp;
        }
        UNLOCK( &cs );
        return 0;
    }
    UNLOCK( &cs );
    return 1;
}

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

bool NewsKlient::trenne() // trennt sich von dem News Server
{
    if( !verbunden )
        return 1;
    LOCK( &cs );
    klient->sendeVerschlüsselt( "\3", 1 );
    char serverReturn = 0;
    klient->getNachrichtVerschlüsselt( &serverReturn, 1 );
    klient->trenne();
    verbunden = 0;
    UNLOCK( &cs );
    return 1;
}

// constant
bool NewsKlient::istVerbunden() const // prüft, ob mit News Server verbunden
{
    return verbunden;
}

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

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

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