#include "KarteEditor.h"
#include <Datei.h>
#include <DateiSystem.h>
#include <Bild.h>
#include <Zeit.h>
#include "KsgsBeschreibung.h"
#include "KsgsShopSeite.h"
#include <unistd.h>
#define Sleep( x )               usleep( (x) * 1000 )
#include <dlfcn.h>
#define LoadLibrary( x )         dlopen( (x), RTLD_LAZY )
#define GetProcAddress           dlsym
#define FreeLibrary              dlclose

typedef ESEditorV*( *DllStart )( );

// Inhalt der KarteEditor Klasse aus KarteEditor.h
// Konstruktor
KarteEditor::KarteEditor( int id, EdSDatenbank *datenbank, char *pf, InitDatei *zIni )
{
    this->id = id;
    db = datenbank;
    ini = zIni->getThis();
    pfad = new Text( pf );
    pfad->append( "/" );
    pfad->append( id );
    if( !DateiExistiert( *pfad ) )
    { // Karte erstellen
        // Client Daten
        Text dn = pfad->getText();
        dn += "/live/map/client/titel.ltdb";
        LTDBDatei bilder;
        bilder.setDatei( dn.getThis() );
        bilder.erstellen();
        Bild *bild = new Bild();
        bild->neuBild( 200, 100, 0x00000000 );
        bilder.speichern( 0, bild->getThis(), new Text( "titel" ) );
        dn = pfad->getText();
        dn += "/live/map/client/minimap.ltdb";
        bilder.setDatei( dn.getThis() );
        bilder.erstellen();
        bild->neuBild( 348, 348, 0x00000000 );
        bilder.speichern( 0, bild->getThis(), new Text( "vorschau" ) );
        dn = pfad->getText();
        dn += "/live/map/client/ladebild.ltdb";
        bilder.setDatei( dn.getThis() );
        bilder.erstellen();
        bild->neuBild( 1300, 900, 0x00000000 );
        bilder.speichern( 0, bild, new Text( "spielladen" ) );
        dn = pfad->getText();
        dn += "/live/map/client/beschreibung.ksgs";
        Datei d;
        d.setDatei( dn );
        d.erstellen();
        d.open( Datei::Style::schreiben );
        d.schreibe( (char*)_neu_karte_ksgs_beschreibung, textLength( _neu_karte_ksgs_beschreibung ) );
        d.close();
        dn = pfad->getText();
        dn += "/live/map/client/map/data/data.sts";
        d.setDatei( dn );
        d.erstellen();
        d.open( Datei::Style::schreiben );
        int i = 0;
        d.schreibe( (char*)&i, 4 );
        d.schreibe( (char*)&i, 4 );
        d.close();
        // Shop Seite
        dn = pfad->getText();
        dn += "/live/shop/seite/seite.ksgs";
        d.setDatei( dn );
        d.erstellen();
        d.open( Datei::Style::schreiben );
        d.schreibe( (char*)_neu_karte_ksgs_shop_seite, textLength( _neu_karte_ksgs_shop_seite ) );
        d.close();
        dn = pfad->getText();
        dn += "/live/shop/titelbg.ltdb";
        bilder.setDatei( dn.getThis() );
        bilder.erstellen();
        bild->neuBild( 200, 100, 0x00000000 );
        bilder.speichern( 0, bild->getThis(), new Text( "auswbg.jpg" ) );
        // Server Daten
        dn = pfad->getText();
        dn += "/live/map/server/sts.ltgd";
        d.setDatei( dn );
        d.erstellen();
        d.open( Datei::Style::schreiben );
        i = 0;
        d.schreibe( (char*)&i, 4 );
        d.schreibe( (char*)&i, 4 );
        d.close();
        dn = pfad->getText();
        dn += "/live/map/server/...";

    }
    shop = new SSEditor( pfad->getText() );
    beschreibung = new BeschreibungEditor( pfad->getText() );
    teams = new TeamsEditor( id, pfad->getText(), db->getThis() );
    dateien = new DateienEditor( pfad->getText(), zIni );
    editor = 0;
    editorDll = 0;
    fehler = "";
    ref = 1;
}

// Destruktor
KarteEditor::~KarteEditor()
{
    shop->release();
    beschreibung->release();
    teams->release();
    dateien->release();
    db->release();
    pfad->release();
    if( editor )
        editor->release();
    if( editorDll )
        FreeLibrary( editorDll );
    ini->release();
}

// private
int KarteEditor::findeDateien( char *pf, RCArray< Text > *pfad )
{
    Datei d;
    d.setDatei( pf );
    if( d.existiert() && !d.istOrdner() )
    {
        pfad->add( new Text( pf ) );
        return 1;
    }
    if( d.istOrdner() )
    {
        RCArray< Text > *list = d.getDateiListe();
        int anz = list->getEintragAnzahl();
        int ret = 0;
        for( int i = 0; i < anz; i++ )
        {
            Text p = pf;
            p += "/";
            p += list->z( i )->getText();
            ret += findeDateien( p, pfad );
        }
        list->release();
        return ret;
    }
    return 0;
}

// nicht constant
bool KarteEditor::getAbbildListe( RCArray< Text > *name, RCArray< Zeit > *datum )
{
    name->leeren();
    datum->leeren();
    Text pf = pfad->getText();
    pf += "/abbilder";
    Datei d;
    d.setDatei( pf );
    if( !d.existiert() || !d.istOrdner() )
        return 1;
    RCArray< Text > *list = d.getDateiListe();
    if( !list )
        return 0;
    int anz = list->getEintragAnzahl();
    for( int i = 0; i < anz; i++ )
    {
        Text p = pf.getText();
        p += "/";
        p += list->z( i )->getText();
        d.setDatei( p );
        Zeit *z = d.getLastChange();
        if( z )
        {
            name->set( list->get( i ), i );
            datum->set( z, i );
        }
    }
    list->release();
    return 1;
}

int KarteEditor::getDateiUpdateListe( char *p, RCArray< Text > *pfad )
{
    pfad->leeren();
    Text pf = this->pfad->getText();
    pf += "/live/";
    pf += p;
    RCArray< Text > *list = new RCArray< Text >();
    int anz = findeDateien( pf, list );
    for( int i = 0; i < anz; i++ )
    {
        list->z( i )->remove( pf.getText() );
        pfad->set( list->get( i ), i );
    }
    list->release();
    return anz;
}

bool KarteEditor::abbildErstellen( char *name )
{
    Text pf = pfad->getText();
    pf += "/abbilder/";
    pf += name;
    if( DateiExistiert( pf ) )
    {
        fehler = "Es existiert bereits ein Abbild mit diesem Namen. Du musst es vorher remove, oder einen anderen Namen angeben.";
        return 0;
    }
    Text live = pfad->getText();
    live += "/live";
    RCArray< Text > *list = new RCArray< Text >();
    int anz = findeDateien( live, list );
    bool ret = 1;
    for( int i = 0; i < anz; i++ )
    {
        Datei alt;
        Datei neu;
        alt.setDatei( list->z( i )->getText() );
        Text pfNeu = pf.getText();
        pfNeu += ( list->z( i )->getText() + live.getLength() );
        neu.setDatei( pfNeu );
        neu.erstellen();
        ret &= alt.open( Datei::Style::lesen );
        ret &= neu.open( Datei::Style::schreiben );
        __int64 size = alt.getSize();
        char *buffer = new char[ 2048 ];
        while( size > 0 )
        {
            int l = size > 2048 ? 2048 : (int)size;
            alt.lese( buffer, l );
            neu.schreibe( buffer, l );
            size -= l;
        }
        delete[] buffer;
        alt.close();
        neu.close();
    }
    list->release();
    if( !ret )
    {
        DateiRemove( pf );
        fehler = "Es ist ein Fehler beim Lesen oder Schreiben einer Datei aufgetreten.";
    }
    return ret;
}

bool KarteEditor::abbildRemove( char *name )
{
    Text pf = pfad->getText();
    pf += "/abbilder/";
    pf += name;
    if( !DateiRemove( pf ) )
    {
        fehler = "Es ist ein Fehler beim Löschen des Abbildes aufgetreten.";
        return 0;
    }
    return 1;
}

bool KarteEditor::abbildHerstellen( char *name )
{
    if( inUpdate() )
    {
        fehler = "Die Karte wird momentan aktualisiert. Bitte haben sie Geduld, bis dieser Vorgang abgeschlossen ist.";
        return 0;
    }
    Text pf = pfad->getText();
    pf += "/abbilder/";
    pf += name;
    if( !DateiExistiert( pf ) )
    {
        fehler = "Es existiert kein Abbild mit dem angegebenen Namen.";
        return 0;
    }
    Text live = pfad->getText();
    live += "/live";
    if( !DateiRemove( live ) )
    {
        fehler = "Es ist ein Fehler beim Löschen der aktuellen Version aufgetreten.";
        return 0;
    }
    RCArray< Text > *list = new RCArray< Text >();
    int anz = findeDateien( pf, list );
    bool ret = 1;
    for( int i = 0; i < anz; i++ )
    {
        Datei alt;
        Datei neu;
        alt.setDatei( list->z( i )->getText() );
        Text pfNeu = live.getText();
        pfNeu += ( list->z( i )->getText() + pf.getLength() );
        neu.setDatei( pfNeu );
        neu.erstellen();
        ret |= alt.open( Datei::Style::lesen );
        ret |= neu.open( Datei::Style::schreiben );
        __int64 size = alt.getSize();
        char *buffer = new char[ 2048 ];
        while( size > 0 )
        {
            int l = size > 2048 ? 2048 : (int)size;
            alt.lese( buffer, l );
            neu.schreibe( buffer, l );
            size -= l;
        }
        delete[] buffer;
        alt.close();
        neu.close();
    }
    list->release();
    if( !ret )
    {
        DateiRemove( pf );
        fehler = "Es ist ein Fehler beim Lesen oder Schreiben einer Datei aufgetreten.";
    }
    return ret;
}

bool KarteEditor::startUpdate()
{
    if( inUpdate() )
    {
        fehler = "Die Karte wird momentan aktualisiert. Bitte haben sie Geduld, bis dieser Vorgang abgeschlossen ist.";
        return 0;
    }
    if( !db->startKarteUpdate( id ) )
    {
        Text *t = db->getLetzterFehler();
        fehler = t->getText();
        t->release();
        return 0;
    }
    return 1;
}

bool KarteEditor::startShopUpdate()
{
    if( !db->startShopUpdate( id ) )
    {
        Text *t = db->getLetzterFehler();
        fehler = t->getText();
        t->release();
        return 0;
    }
    return 1;
}

bool KarteEditor::prozessMessage( SKlient *zKlient )
{
    char message = 0;
    zKlient->getNachrichtEncrypted( &message, 1 );
    switch( message )
    {
    case 0x1: // Abbild Erstellen
    {
        zKlient->sendeEncrypted( "\1", 1 );
        unsigned char l = 0;
        zKlient->getNachrichtEncrypted( (char*)&l, 1 );
        char *name = new char[ l + 1 ];
        name[ (int)l ] = 0;
        if( l )
            zKlient->getNachrichtEncrypted( name, l );
        if( !textLength( name ) )
        {
            delete[] name;
            fehler = "Du musst einen Namen angeben.";
            return 0;
        }
        if( !abbildErstellen( name ) )
        {
            delete[] name;
            return 0;
        }
        delete[] name;
        zKlient->sendeEncrypted( "\1", 1 );
        return 1;
    }
    case 0x2: // Abbild Löschen
    {
        zKlient->sendeEncrypted( "\1", 1 );
        unsigned char l = 0;
        zKlient->getNachrichtEncrypted( (char*)&l, 1 );
        char *name = new char[ l + 1 ];
        name[ (int)l ] = 0;
        if( l )
            zKlient->getNachrichtEncrypted( name, l );
        if( !textLength( name ) )
        {
            delete[] name;
            fehler = "Du musst einen Namen angeben.";
            return 0;
        }
        if( !abbildRemove( name ) )
        {
            delete[] name;
            return 0;
        }
        delete[] name;
        zKlient->sendeEncrypted( "\1", 1 );
        return 1;
    }
    case 0x3: // Abbild Herstellen
    {
        zKlient->sendeEncrypted( "\1", 1 );
        unsigned char l = 0;
        zKlient->getNachrichtEncrypted( (char*)&l, 1 );
        char *name = new char[ l + 1 ];
        name[ (int)l ] = 0;
        if( l )
            zKlient->getNachrichtEncrypted( name, l );
        if( !textLength( name ) )
        {
            delete[] name;
            fehler = "Du musst einen Namen angeben.";
            return 0;
        }
        if( !abbildHerstellen( name ) )
        {
            delete[] name;
            return 0;
        }
        delete[] name;
        zKlient->sendeEncrypted( "\1", 1 );
        return 1;
    }
    case 0x4: // Karte Veröffentlichen
    {
        zKlient->sendeEncrypted( "\1", 1 );
        if( !startUpdate() )
            return 0;
        else
            zKlient->sendeEncrypted( "\1", 1 );
        return 1;
    }
    case 0x5: // get Abbild Liste
    {
        zKlient->sendeEncrypted( "\1", 1 );
        RCArray< Text > *name = new RCArray< Text >();
        RCArray< Zeit > *datum = new RCArray< Zeit >();
        getAbbildListe( name, datum );
        int anz = name->getEintragAnzahl();
        zKlient->sendeEncrypted( (char*)&anz, 4 );
        for( int i = 0; i < anz; i++ )
        {
            Text n = name->z( i )->getText();
            char len = (char)n.getLength();
            zKlient->sendeEncrypted( &len, 1 );
            zKlient->sendeEncrypted( n, len );
            int d = datum->z( i )->zDatum()->getJahr();
            zKlient->sendeEncrypted( (char*)&d, 4 );
            d = datum->z( i )->zDatum()->getMonat();
            zKlient->sendeEncrypted( (char*)&d, 4 );
            d = datum->z( i )->zDatum()->getTag();
            zKlient->sendeEncrypted( (char*)&d, 4 );
            d = datum->z( i )->zUhrzeit()->getStunde();
            zKlient->sendeEncrypted( (char*)&d, 4 );
            d = datum->z( i )->zUhrzeit()->getMinute();
            zKlient->sendeEncrypted( (char*)&d, 4 );
            d = datum->z( i )->zUhrzeit()->getSekunde();
            zKlient->sendeEncrypted( (char*)&d, 4 );
        }
        name->release();
        datum->release();
        return 1;
    }
    case 0x6: // Shop Seite Veröffentlichen
    {
        zKlient->sendeEncrypted( "\1", 1 );
        if( !startShopUpdate() )
            return 0;
        else
            zKlient->sendeEncrypted( "\1", 1 );
        return 1;
    }
    case 0x7: // Shop Seite Nachricht
    {
        zKlient->sendeEncrypted( "\1", 1 );
        if( !shop->prozessMessage( zKlient ) )
            fehler = shop->getLetzterFehler();
        else
            return 1;
        return 0;
    }
    case 0x8: // Shop Seite herunterladen
    {
        zKlient->sendeEncrypted( "\1", 1 );
        RCArray< Text > *list = new RCArray< Text >();
        Text pf = pfad->getText();
        pf += "/live/shop";
        int anz = findeDateien( pf, list );
        zKlient->sendeEncrypted( (char*)&anz, 4 );
        for( int i = 0; i < anz; i++ )
        {
            Text p = list->z( i )->getText();
            char l = (char)( p.getLength() - pf.getLength() );
            zKlient->sendeEncrypted( &l, 1 );
            zKlient->sendeEncrypted( (char*)p + pf.getLength(), l );
            Datei d;
            d.setDatei( p );
            __int64 size = d.getSize();
            zKlient->sendeEncrypted( (char*)&size, 8 );
            d.open( Datei::Style::lesen );
            char *buffer = new char[ 2048 ];
            while( size > 0 )
            {
                int l = size > 2048 ? 2048 : (int)size;
                d.lese( buffer, l );
                zKlient->sende( buffer, l );
                size -= l;
            }
            delete[] buffer;
            d.close();
        }
        list->release();
        return 1;
    }
    case 0x9: // Shop Daten laden
    {
        int es = 0, tp = 0, vp = 0;
        if( !db->getKarteShopDaten( id, es, tp, vp ) )
        {
            fehler = "Fehler beim laden der Shop Informationen.";
            return 0;
        }
        zKlient->sendeEncrypted( "\1", 1 );
        zKlient->sendeEncrypted( (char*)&es, 4 );
        zKlient->sendeEncrypted( (char*)&tp, 4 );
        zKlient->sendeEncrypted( (char*)&vp, 4 );
        return 1;
    }
    case 0xA: // Set Shop Daten
    {
        zKlient->sendeEncrypted( "\1", 1 );
        int es = 0, tp = 0, vp = 0;
        zKlient->getNachrichtEncrypted( (char*)&es, 4 );
        zKlient->getNachrichtEncrypted( (char*)&tp, 4 );
        zKlient->getNachrichtEncrypted( (char*)&vp, 4 );
        if( db->setKarteShopDaten( id, es, tp, vp ) )
        {
            zKlient->sendeEncrypted( "\1", 1 );
            return 1;
        }
        fehler = "Fehler beim setzen der Shop Werte.";
        return 0;
    }
    case 0xB: // Beschreibung Nachricht
    {
        zKlient->sendeEncrypted( "\1", 1 );
        if( !beschreibung->prozessMessage( zKlient ) )
            fehler = beschreibung->getLetzterFehler();
        else
            return 1;
        return 0;
    }
    case 0xC: // Teams Nachricht
    {
        zKlient->sendeEncrypted( "\1", 1 );
        if( !teams->prozessMessage( zKlient ) )
            fehler = teams->getLetzterFehler();
        else
            return 1;
        return 0;
    }
    case 0xD: // Dateien Nachicht
    {
        zKlient->sendeEncrypted( "\1", 1 );
        if( !dateien->prozessMessage( zKlient ) )
            fehler = dateien->getLetzterFehler();
        else
            return 1;
        return 0;
    }
    case 0xE: // Init Editor
    {
        if( editorDll )
        {
            if( editor )
                editor = editor->release();
            FreeLibrary( editorDll );
        }
        if( !ini->wertExistiert( "SpielePfad" ) )
        {
            fehler = "Diese Option ist noch nicht verfügbar. Error Code: " __FILE__ ":";
            fehler += __LINE__;
            return 0;
        }
        Text pf = ini->zWert( "SpielePfad" )->getText();
        pf += "/";
        int said = db->getSpielArtId( id );
        if( !said )
        {
            fehler = "Diese Option ist noch nicht verfügbar. Error Code: " __FILE__ ":";
            fehler += __LINE__;
            return 0;
        }
        pf += said;
        pf += "/bin/game";
        pf += ".so";
        editorDll = LoadLibrary( pf.getText() );
        if( !editorDll )
        {
            fehler = "Diese Option ist noch nicht verfügbar. Error Code: " __FILE__ ":";
            fehler += __LINE__;
            return 0;
        }
        DllStart getEditorKlasse = (DllStart)GetProcAddress( editorDll, "getEditorKlasse" );
        if( !getEditorKlasse )
        {
            fehler = "Diese Option ist noch nicht verfügbar. Error Code: " __FILE__ ":";
            fehler += __LINE__;
            FreeLibrary( editorDll );
            editorDll = 0;
            return 0;
        }
        editor = getEditorKlasse();
        if( !editor )
        {
            fehler = "Diese Option ist noch nicht verfügbar. Error Code: " __FILE__ ":";
            fehler += __LINE__;
            FreeLibrary( editorDll );
            editorDll = 0;
            return 0;
        }
        zKlient->sendeEncrypted( "\1", 1 );
        Text p = pfad->getText();
        p += "/live";
        editor->setPfad( p );
        editor->open();
        return 1;
    }
    case 0xF:
    { // Editor Nachricht
        if( !editor )
        {
            fehler = "Der Editor wurde nicht initialisiert. Error Code: " __FILE__ ":";
            fehler += __LINE__;
            editorDll = 0;
            return 0;
        }
        zKlient->sendeEncrypted( "\1", 1 );
        if( !editor->nachricht( zKlient ) )
            fehler = editor->getLetzterFehler();
        else
            return 1;
        return 0;
    }
    case 0x10:
    { // Get Verkauf Statistik
        Array< int > einkommen;
        Array< int > verkauft;
        Array< int > spiele;
        if( db->getLast30DaysIncomeStatistik( id, &einkommen, &verkauft ) && db->getLast30DaysSpielStatistik( id, &spiele ) )
        {
            zKlient->sendeEncrypted( "\1", 1 );
            int kaufAnz = db->getKaufAnz( id );
            zKlient->sendeEncrypted( (char*)&kaufAnz, 4 );
            char anz = (char)einkommen.getEintragAnzahl();
            zKlient->sendeEncrypted( &anz, 1 );
            for( int i = 0; i < anz; i++ )
            {
                int kupfer = einkommen.get( i );
                int kauf = verkauft.get( i );
                int games = spiele.get( i );
                zKlient->sendeEncrypted( (char*)&kupfer, 4 );
                zKlient->sendeEncrypted( (char*)&kauf, 4 );
                zKlient->sendeEncrypted( (char*)&games, 4 );
            }
            return 1;
        }
        fehler = "Die Statistik konnte nicht erstellt werden.";
        return 0;
    }
    default:
        fehler = "Unbekannte Nachricht. Eventuell ist der Client nicht auf dem neusten Stand.";
    }
    return 0;
}

void KarteEditor::removeError()
{
    fehler = "";
}

// constant
bool KarteEditor::inUpdate() const
{
    return db->istKarteInUpdate( id );
}

bool KarteEditor::istOk() const
{
    return fehler.getLength() == 0;
}

char *KarteEditor::getLetzterFehler() const
{
    return fehler;
}

char *KarteEditor::getPfad() const
{
    return pfad->getText();
}

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

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