#include "Datenbank.h"
#include <iostream>

// Inhalt der PSDatenbank Klasse aus Datenbank.h
// Konstruktor
PSDatenbank::PSDatenbank( InitDatei *zIni )
{
    datenbank = new Datenbank( zIni->zWert( "DBBenutzer" )->getText(), zIni->zWert( "DBPasswort" )->getText(),
                               zIni->zWert( "DBName" )->getText(), zIni->zWert( "DBIP" )->getText(),
                               (unsigned short)TextZuInt( zIni->zWert( "DBPort" )->getText(), 10 ) );
    if( !datenbank->istOk() )
    {
        std::cout << "PS: Die Verbindung zur Datenbank konnte nicht hergestellt werden.\nDas Programm wird beendet.";
        exit( 1 );
    }
    InitializeCriticalSection( &cs );
    ref = 1;
    Text befehl = "SELECT port, admin_port  FROM server WHERE id = ";
    befehl += zIni->zWert( "ServerId" )->getText();
    lock();
    datenbank->befehl( befehl );
    Result res = datenbank->getResult();
    unlock();
    if( res.zeilenAnzahl == 1 )
    {
        zIni->addWert( "ServerPort", res.values[ 0 ] );
        zIni->addWert( "AdminServerPort", res.values[ 1 ] );
    }
    res.destroy();
}

// Destruktor
PSDatenbank::~PSDatenbank()
{
    datenbank->release();
    DeleteCriticalSection( &cs );
}

// nicht constant
void PSDatenbank::lock()
{
    EnterCriticalSection( &cs );
}

void PSDatenbank::unlock()
{
    LeaveCriticalSection( &cs );
}

int PSDatenbank::istAdministrator( const char *name, const char *passwort )
{
    Text *befehl = new Text( "SELECT id FROM benutzer WHERE name = '" );
    Text n( name );
    n.ersetzen( "'", "''" );
    befehl->append( (char*)n );
    befehl->append( "' AND passwort = md5( '" );
    Text p( passwort );
    p.ersetzen( "'", "''" );
    befehl->append( (char*)p );
    befehl->append( "' )" );
    lock();
    datenbank->befehl( befehl->getText() );
    Result res = datenbank->getResult();
    unlock();
    befehl->release();
    int ret = 0;
    if( res.zeilenAnzahl > 0 )
        ret = TextZuInt( res.values[ 0 ].getText(), 10 );
    res.destroy();
    return ret;
}

bool PSDatenbank::adminHatRecht( int id, int recht )
{
    Text *befehl = new Text( "SELECT * FROM benutzer_rechte WHERE benutzer_id = " );
    befehl->append( id );
    befehl->append( " AND rechte_id = " );
    befehl->append( recht );
    lock();
    datenbank->befehl( befehl->getText() );
    int ret = datenbank->getZeilenAnzahl();
    unlock();
    befehl->release();
    return ret != 0;
}

bool PSDatenbank::proveKlient( int num, int sNum )
{
    Text *befehl = new Text( "SELECT * FROM server_client WHERE server_id = " );
    befehl->append( sNum );
    befehl->append( " AND client_id = " );
    befehl->append( num );
    lock();
    datenbank->befehl( befehl->getText() );
    Result res = datenbank->getResult();
    unlock();
    befehl->release();
    bool ret = 0;
    if( res.zeilenAnzahl == 1 )
        ret = 1;
    res.destroy();
    return ret;
}

Text *PSDatenbank::getKlientKey( int cId )
{
    lock();
    if( !datenbank->befehl( Text( "SELECT schluessel FROM client WHERE id = " ) += cId ) )
    {
        unlock();
        return 0;
    }
    Result res = datenbank->getResult();
    unlock();
    if( !res.zeilenAnzahl )
    {
        res.destroy();
        return 0;
    }
    Text *ret = new Text( res.values[ 0 ].getText() );
    res.destroy();
    return ret;
}

void PSDatenbank::unregisterKlient( int num, int sNum )
{
    Text *befehl = new Text( "DELETE FROM server_client WHERE client_id = " );
    befehl->append( num );
    befehl->append( " AND server_id = " );
    befehl->append( sNum );
    lock();
    datenbank->befehl( befehl->getText() );
    int za = datenbank->getZeilenAnzahl();
    unlock();
    if( za == 1 )
    {
        befehl->setText( "UPDATE server SET tasks = tasks - 1 WHERE id = " );
        befehl->append( sNum );
        lock();
        datenbank->befehl( befehl->getText() );
        unlock();
    }
    befehl->release();
}

bool PSDatenbank::setServerStatus( int id, int status )
{
    Text *befehl = new Text( "UPDATE server SET server_status_id = " );
    *befehl += status;
    *befehl += "WHERE id = ";
    *befehl += id;
    lock();
    if( !datenbank->befehl( befehl->getText() ) )
    {
        unlock();
        befehl->release();
        return 0;
    }
    bool ret = datenbank->getZeilenAnzahl() != 0;
    unlock();
    befehl->release();
    return ret;
}

bool PSDatenbank::setMaxClients( int id, int maxC )
{
    Text *befehl = new Text( "UPDATE server SET max_tasks = " );
    befehl->append( maxC );
    befehl->append( " WHERE id = " );
    befehl->append( id );
    lock();
    if( !datenbank->befehl( befehl->getText() ) )
    {
        unlock();
        befehl->release();
        return 0;
    }
    bool ret = datenbank->getZeilenAnzahl() > 0;
    unlock();
    befehl->release();
    return ret;
}

bool PSDatenbank::serverIstNichtPausiert( int id )
{
    Text *befehl = new Text( "SELECT server_status_id FROM server WHERE id = " );
    befehl->append( id );
    lock();
    if( !datenbank->befehl( befehl->getText() ) )
    {
        unlock();
        befehl->release();
        return 0;
    }
    Result res = datenbank->getResult();
    unlock();
    befehl->release();
    if( !res.zeilenAnzahl )
    {
        res.destroy();
        return 0;
    }
    bool ret = (int)res.values[ 0 ] == 3;
    res.destroy();
    return ret;
}

Result PSDatenbank::getSystemListe()
{
    lock();
    if( !datenbank->befehl( "SELECT id, name FROM system" ) )
    {
        unlock();
        Result res = { 0, 0, 0, 0 };
        return res;
    }
    Result res = datenbank->getResult();
    unlock();
    return res;
}

Result PSDatenbank::getDateiListe( int system, int gruppe )
{
    Text *befehl = new Text( "SELECT pfad, version FROM datei WHERE geloescht = false AND ( system_id = " );
    befehl->append( system );
    befehl->append( " OR system_id IS NULL ) AND datei_gruppe_id = " );
    befehl->append( gruppe );
    lock();
    if( !datenbank->befehl( befehl->getText() ) )
    {
        unlock();
        befehl->release();
        Result res = { 0, 0, 0, 0 };
        return res;
    }
    Result res = datenbank->getResult();
    unlock();
    befehl->release();
    return res;
}

Result PSDatenbank::getDateiListeSMP( int system, int gruppe )
{
    Text *befehl = new Text( "SELECT id, pfad, version, aenderung_dat FROM datei "
                             "WHERE geloescht = false AND system_id = " );
    befehl->append( system );
    befehl->append( " AND datei_gruppe_id = " );
    befehl->append( gruppe );
    lock();
    if( !datenbank->befehl( befehl->getText() ) )
    {
        unlock();
        befehl->release();
        Result res = { 0, 0, 0, 0 };
        return res;
    }
    Result res = datenbank->getResult();
    unlock();
    befehl->release();
    return res;
}

Result PSDatenbank::getDateiListeOhneOrdner( int system, int gruppe )
{
    Text *befehl = new Text( "SELECT pfad, version FROM datei "
                             "WHERE geloescht = false AND aenderung_dat IS NOT NULL AND ( system_id = " );
    befehl->append( system );
    befehl->append( " OR system_id IS NULL ) AND datei_gruppe_id = " );
    befehl->append( gruppe );
    lock();
    if( !datenbank->befehl( befehl->getText() ) )
    {
        unlock();
        befehl->release();
        Result res = { 0, 0, 0, 0 };
        return res;
    }
    Result res = datenbank->getResult();
    unlock();
    befehl->release();
    return res;
}

Result PSDatenbank::getDeletedFiles( int system, int gruppe )
{
    Text *befehl = new Text( "SELECT pfad FROM datei WHERE geloescht = true AND ( system_id = " );
    befehl->append( system );
    befehl->append( " OR system_id IS NULL ) AND datei_gruppe_id = " );
    befehl->append( gruppe );
    lock();
    if( !datenbank->befehl( befehl->getText() ) )
    {
        unlock();
        befehl->release();
        Result res = { 0, 0, 0, 0 };
        return res;
    }
    Result res = datenbank->getResult();
    unlock();
    befehl->release();
    return res;
}

Text *PSDatenbank::getSystemName( int system )
{
    Text *befehl = new Text( "SELECT name FROM system WHERE id = " );
    befehl->append( system );
    lock();
    if( !datenbank->befehl( befehl->getText() ) )
    {
        unlock();
        befehl->release();
        return 0;
    }
    Result res = datenbank->getResult();
    unlock();
    befehl->release();
    if( !res.zeilenAnzahl )
    {
        res.destroy();
        return 0;
    }
    Text *ret = new Text( res.values[ 0 ].getText() );
    res.destroy();
    return ret;
}

Text *PSDatenbank::getDateiGruppeName( int gruppe )
{
    Text *befehl = new Text( "SELECT name FROM datei_gruppe WHERE id = " );
    befehl->append( gruppe );
    lock();
    if( !datenbank->befehl( befehl->getText() ) )
    {
        unlock();
        befehl->release();
        return 0;
    }
    Result res = datenbank->getResult();
    unlock();
    befehl->release();
    if( !res.zeilenAnzahl )
    {
        res.destroy();
        return 0;
    }
    Text *ret = new Text( res.values[ 0 ].getText() );
    res.destroy();
    return ret;
}

bool PSDatenbank::supportedDateiGruppe( int server, int gruppe )
{
    Text *befehl = new Text( "SELECT * FROM server_patch_datei_gruppe a, datei_gruppe b "
                             "WHERE a.datei_gruppe_id = b.id AND a.version = b.version AND a.server_patch_id = " );
    befehl->append( server );
    befehl->append( " AND b.id = " );
    befehl->append( gruppe );
    lock();
    if( !datenbank->befehl( befehl->getText() ) )
    {
        unlock();
        befehl->release();
        return 0;
    }
    int zeilen = datenbank->getZeilenAnzahl();
    unlock();
    befehl->release();
    return zeilen != 0;
}

bool PSDatenbank::istDateigruppeStatusUpdate( int server, int gruppe )
{
    Text *befehl = new Text( "SELECT * FROM server_patch_datei_gruppe a, datei_gruppe b "
                             "WHERE a.datei_gruppe_id = b.id AND a.server_patch_datei_gruppe_status_id = 2 AND a.server_patch_id = " );
    befehl->append( server );
    befehl->append( " AND b.id = " );
    befehl->append( gruppe );
    lock();
    if( !datenbank->befehl( befehl->getText() ) )
    {
        unlock();
        befehl->release();
        return 0;
    }
    int zeilen = datenbank->getZeilenAnzahl();
    unlock();
    befehl->release();
    return zeilen != 0;
}

bool PSDatenbank::setDateiGruppe( int server, int gruppe, int version )
{
    Text befehl = "SELECT server_patch_id FROM server_patch_datei_gruppe WHERE server_patch_id = ";
    befehl += server;
    befehl += " AND datei_gruppe_id = ";
    befehl += gruppe;
    lock();
    if( !datenbank->befehl( befehl ) )
    {
        unlock();
        return 0;
    }
    unlock();
    if( !datenbank->getZeilenAnzahl() )
    {
        befehl = "INSERT INTO server_patch_datei_gruppe( server_patch_id, datei_gruppe_id, version, server_patch_datei_gruppe_status_id ) VALUES( ";
        befehl += server;
        befehl += ", ";
        befehl += gruppe;
        befehl += ", ";
        befehl += version;
        befehl += ", 1 )";
        lock();
        if( !datenbank->befehl( befehl ) )
        {
            unlock();
            return 0;
        }
        unlock();
    }
    else
    {
        befehl = "UPDATE server_patch_datei_gruppe SET server_patch_datei_gruppe_status_id = 1, version = ";
        befehl += version;
        befehl += " WHERE server_patch_id = ";
        befehl += server;
        befehl += " AND datei_gruppe_id = ";
        befehl += gruppe;
        lock();
        if( !datenbank->befehl( befehl ) )
        {
            unlock();
            return 0;
        }
        unlock();
    }
    return 1;
}

bool PSDatenbank::setDateiGruppe( int server, int gruppe, bool update )
{
    Text *befehl = new Text( "UPDATE server_patch_datei_gruppe SET server_patch_datei_gruppe_status_id = " );
    befehl->append( update ? 2 : 1 );
    befehl->append( " WHERE server_patch_id = " );
    befehl->append( server );
    befehl->append( " AND datei_gruppe_id = " );
    befehl->append( gruppe );
    lock();
    if( !datenbank->befehl( befehl->getText() ) )
    {
        unlock();
        befehl->release();
        return 0;
    }
    unlock();
    befehl->release();
    return 1;
}

bool PSDatenbank::dateiGruppeUpdateFertig( int server, int gruppe )
{
    Text *befehl = new Text( "UPDATE server_patch_datei_gruppe a SET version = b.version "
                             "FROM datei_gruppe b WHERE a.datei_gruppe_id = b.id AND a.server_patch_id = " );
    befehl->append( server );
    befehl->append( " AND b.id = " );
    befehl->append( gruppe );
    lock();
    if( !datenbank->befehl( befehl->getText() ) )
    {
        unlock();
        befehl->release();
        return 0;
    }
    unlock();
    befehl->release();
    setDateiGruppe( server, gruppe, false );
    return 1;
}

int PSDatenbank::getDateiGruppePatchServer( int client, int gruppe )
{
    Text *befehl = new Text( "select such_datei_gruppe_patch_server( " );
    befehl->append( client );
    befehl->append( ", " );
    befehl->append( gruppe );
    befehl->append( " )" );
    lock();
    if( !datenbank->befehl( befehl->getText() ) )
    {
        unlock();
        befehl->release();
        return 0;
    }
    Result res = datenbank->getResult();
    unlock();
    befehl->release();
    if( !res.zeilenAnzahl )
    {
        res.destroy();
        return 0;
    }
    int ret = TextZuInt( res.values[ 0 ].getText(), 10 );
    res.destroy();
    return ret;
}

bool PSDatenbank::getPatchServerPortIp( int server, int *port, Text *zIp )
{
    if( !server )
        return 0;
    Text *befehl = new Text( "SELECT port, ip FROM server WHERE id = " );
    befehl->append( server );
    lock();
    if( !datenbank->befehl( befehl->getText() ) )
    {
        unlock();
        befehl->release();
        return 0;
    }
    Result res = datenbank->getResult();
    unlock();
    befehl->release();
    if( !res.zeilenAnzahl )
    {
        res.destroy();
        return 0;
    }
    *port = TextZuInt( res.values[ 0 ].getText(), 10 );
    zIp->setText( res.values[ 1 ].getText() );
    res.destroy();
    return 1;
}

Result PSDatenbank::getGruppeIds()
{
    lock();
    if( !datenbank->befehl( "SELECT id FROM datei_gruppe" ) )
    {
        unlock();
        Result res = { 0, 0, 0, 0 };
        return res;
    }
    Result res = datenbank->getResult();
    unlock();
    return res;
}

Result PSDatenbank::getDateiGruppeListe()
{
    lock();
    if( !datenbank->befehl( "SELECT id, pfad, version FROM datei_gruppe" ) )
    {
        unlock();
        Result res = { 0, 0, 0, 0 };
        return res;
    }
    Result res = datenbank->getResult();
    unlock();
    return res;
}

int PSDatenbank::getDateiVersion( const char *pfad, int system, int gruppe )
{
    Text *befehl = new Text( "SELECT version FROM datei WHERE pfad = '" );
    Text p( pfad );
    p.ersetzen( "'", "''" );
    befehl->append( (char*)p );
    befehl->append( "' AND system_id = " );
    befehl->append( system );
    befehl->append( " AND datei_gruppe_id = " );
    befehl->append( gruppe );
    lock();
    if( !datenbank->befehl( befehl->getText() ) )
    {
        unlock();
        befehl->release();
        return 0;
    }
    Result res = datenbank->getResult();
    unlock();
    befehl->release();
    if( !res.zeilenAnzahl )
    {
        res.destroy();
        return 0;
    }
    int ret = TextZuInt( res.values[ 0 ].getText(), 10 );
    res.destroy();
    return ret;
}

int PSDatenbank::getDateiGruppeVersion( int gruppe )
{
    Text *befehl = new Text( "SELECT version FROM datei_gruppe WHERE id = " );
    befehl->append( gruppe );
    lock();
    if( !datenbank->befehl( befehl->getText() ) )
    {
        unlock();
        befehl->release();
        return 0;
    }
    Result res = datenbank->getResult();
    unlock();
    befehl->release();
    if( !res.zeilenAnzahl )
    {
        res.destroy();
        return 0;
    }
    int ret = TextZuInt( res.values[ 0 ].getText(), 10 );
    res.destroy();
    return ret;
}

bool PSDatenbank::getFileGroupInfoList( int serverId, Array< FileGroupInfo* > *list )
{
    Text befehl = "SELECT a.id, a.name, 'Fehlt' AS status, a.pfad, a.version FROM datei_gruppe a "
        "LEFT JOIN server_patch_datei_gruppe b ON a.id = b.datei_gruppe_id AND b.server_patch_id = ";
    befehl += serverId;
    befehl += " WHERE b.datei_gruppe_id IS NULL "
        "UNION "
        "SELECT a.id, a.name, 'Veraltet' AS status, a.pfad, a.version FROM datei_gruppe a, server_patch_datei_gruppe b "
        "WHERE a.id = b.datei_gruppe_id AND a.version != b.version AND b.server_patch_datei_gruppe_status_id = 1 AND b.server_patch_id = ";
    befehl += serverId;
    befehl += " UNION "
        "SELECT a.id, a.name, 'Update' AS status, a.pfad, a.version FROM datei_gruppe a, server_patch_datei_gruppe b "
        "WHERE a.id = b.datei_gruppe_id AND a.version != b.version AND b.server_patch_datei_gruppe_status_id = 2 AND b.server_patch_id = ";
    befehl += serverId;
    befehl += " UNION "
        "SELECT a.id, a.name, 'Aktuell' AS status, a.pfad, a.version FROM datei_gruppe a, server_patch_datei_gruppe b "
        "WHERE a.id = b.datei_gruppe_id AND a.version = b.version AND b.server_patch_id = ";
    befehl += serverId;
    lock();
    if( !datenbank->befehl( befehl ) )
    {
        unlock();
        return 0;
    }
    Result res = datenbank->getResult();
    unlock();
    for( int i = 0; i < res.zeilenAnzahl; i++ )
    {
        FileGroupInfo *info = new FileGroupInfo();
        info->id = res.values[ 0 + i * 5 ];
        info->name = (char*)res.values[ 1 + i * 5 ];
        info->status = (char*)res.values[ 2 + i * 5 ];
        info->pfad = (char*)res.values[ 3 + i * 5 ];
        info->version = res.values[ 4 + i * 5 ];
        list->add( info );
    }
    res.destroy();
    return 1;
}

bool PSDatenbank::updateDatei( const char *pfad, char *letzteAEnderung, int system, int gruppe )
{
    Text *befehl = new Text( "SELECT * FROM datei WHERE pfad = '" );
    Text p( pfad );
    p.ersetzen( "'", "''" );
    befehl->append( (char*)p );
    befehl->append( "' AND system_id = " );
    befehl->append( system );
    befehl->append( " AND datei_gruppe_id = " );
    befehl->append( gruppe );
    lock();
    if( !datenbank->befehl( befehl->getText() ) )
    {
        unlock();
        befehl->release();
        return 0;
    }
    int anz = datenbank->getZeilenAnzahl();
    unlock();
    if( anz && !letzteAEnderung ) // datei existiert nicht mehr
    {
        befehl->setText( "SELECT * FROM datei"
                         "WHERE geloescht = false AND pfad = '" );
        befehl->append( (char*)p );
        befehl->append( "' AND system_id = " );
        befehl->append( system );
        befehl->append( " AND datei_gruppe_id = " );
        befehl->append( gruppe );
        lock();
        if( !datenbank->befehl( befehl->getText() ) )
        {
            unlock();
            befehl->release();
            return 0;
        }
        int anz = datenbank->getZeilenAnzahl();
        unlock();
        if( anz ) // datei wurde noch nicht in die Tabelle datei_loeschen eingetragen
        {
            befehl->setText( "UPDATE datei SET geloescht = true WHERE pfad = '" );
            befehl->append( (char*)p );
            befehl->append( "' AND system_id = " );
            befehl->append( system );
            befehl->append( " AND datei_gruppe_id = " );
            befehl->append( gruppe );
            lock();
            if( datenbank->befehl( befehl->getText() ) )
            {
                unlock();
                befehl->release();
                return 1;
            }
            else
            {
                unlock();
                befehl->release();
                return 0;
            }
        }
    }
    else if( !anz ) // neue Datei erstellt
    {
        befehl->setText( "INSERT INTO datei( pfad, version, aenderung_dat, system_id, datei_gruppe_id ) VALUES( '" );
        befehl->append( (char*)p );
        befehl->append( "', 1, " );
        if( !letzteAEnderung )
        {
            befehl->append( "NULL, " );
        }
        else
        {
            befehl->append( "'" );
            Text l( letzteAEnderung );
            l.ersetzen( "'", "''" );
            befehl->append( (char*)l );
            befehl->append( "', " );
        }
        befehl->append( system );
        befehl->append( ", " );
        befehl->append( gruppe );
        befehl->append( " )" );
        lock();
        if( datenbank->befehl( befehl->getText() ) )
        {
            unlock();
            befehl->release();
            return 1;
        }
        else
        {
            unlock();
            befehl->release();
            return 0;
        }
    }
    else if( anz && letzteAEnderung ) // Datei existiert bereits
    {
        // Datei aus tabelle datei_loeschen entfernen, wenn sie vorhanden ist
        befehl->setText( "UPDATE datei SET geloescht = false WHERE pfad = '" );
        befehl->append( (char*)p );
        befehl->append( "' AND system_id = " );
        befehl->append( system );
        befehl->append( " AND datei_gruppe_id = " );
        befehl->append( gruppe );
        lock();
        if( !datenbank->befehl( befehl->getText() ) )
        {
            unlock();
            befehl->release();
            return 0;
        }
        unlock();
        befehl->setText( "SELECT * FROM datei WHERE pfad = '" );
        befehl->append( (char*)p );
        befehl->append( "' AND system_id = " );
        befehl->append( system );
        befehl->append( " AND datei_gruppe_id = " );
        befehl->append( gruppe );
        befehl->append( " AND aenderung_dat = '" );
        Text l( letzteAEnderung );
        l.ersetzen( "'", "''" );
        befehl->append( (char*)l );
        befehl->append( "'" );
        lock();
        if( !datenbank->befehl( befehl->getText() ) )
        {
            unlock();
            befehl->release();
            return 0;
        }
        anz = datenbank->getZeilenAnzahl();
        unlock();
        if( !anz ) // Datei wurde geändert
        {
            befehl->setText( "UPDATE datei SET version = version + 1, aenderung_dat = '" );
            befehl->append( (char*)l );
            befehl->append( "' WHERE pfad = '" );
            befehl->append( (char*)p );
            befehl->append( "' AND system_id = " );
            befehl->append( system );
            befehl->append( " AND datei_gruppe_id = " );
            befehl->append( gruppe );
            lock();
            if( datenbank->befehl( befehl->getText() ) )
            {
                unlock();
                befehl->release();
                return 1;
            }
            else
            {
                unlock();
                befehl->release();
                return 0;
            }
        }
    }
    befehl->release();
    return 1;
}

bool PSDatenbank::dateiGruppeUpdate( int gruppe )
{
    Text *befehl = new Text( "UPDATE datei_gruppe SET version = version + 1 WHERE id = " );
    befehl->append( gruppe );
    lock();
    if( !datenbank->befehl( befehl->getText() ) )
    {
        unlock();
        befehl->release();
        return 0;
    }
    int z = datenbank->getZeilenAnzahl();
    unlock();
    befehl->release();
    return z != 0;
}

// constant
Text *PSDatenbank::getLetzterFehler() const
{
    return datenbank->getLetzterFehler();
}

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

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