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

// Inhalt der LSDatenbank Klasse aus Datenbank.h
// Konstruktor
MSDatenbank::MSDatenbank( InitDatei *zIni )
    : ReferenceCounter()
{
    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 << "MS: Die Verbindung zur Datenbank konnte nicht hergestellt werden.\nDas Programm wird beendet.";
        exit( 1 );
    }
    InitializeCriticalSection( &cs );
    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
MSDatenbank::~MSDatenbank()
{
    datenbank->release();
    DeleteCriticalSection( &cs );
}

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

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

int MSDatenbank::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 MSDatenbank::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 MSDatenbank::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 *MSDatenbank::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 MSDatenbank::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 MSDatenbank::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 MSDatenbank::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 MSDatenbank::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;
}

int MSDatenbank::getAccountId( int clientNumber )
{
    Text befehl = "SELECT account_id FROM account_client WHERE client_id = ";
    befehl += clientNumber;
    lock();
    if( !datenbank->befehl( befehl ) )
    {
        unlock();
        return 0;
    }
    Result res = datenbank->getResult();
    unlock();
    if( !res.zeilenAnzahl )
        return 0;
    int ret = res.values[ 0 ];
    res.destroy();
    return ret;
}

Text *MSDatenbank::getAccountName( int accountId )
{
    Text befehl = "SELECT ruf_name FROM account WHERE id = ";
    befehl += accountId;
    lock();
    if( !datenbank->befehl( befehl ) )
    {
        unlock();
        return 0;
    }
    Result res = datenbank->getResult();
    unlock();
    if( !res.zeilenAnzahl )
        return 0;
    Text *ret = new Text( res.values[ 0 ].getText() );
    res.destroy();
    return ret;
}

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

int MSDatenbank::getMinigameOptionList( char *minigame, RCArray< Text > *zOptionList )
{
    Text befehl = "SELECT a.options FROM minigame_option a, minigame b WHERE a.minigame_id = b.id AND b.name = '";
    Text m( minigame );
    m.ersetzen( "'", "''" );
    befehl += m;
    befehl += "' ORDER BY a.options";
    lock();
    if( !datenbank->befehl( befehl ) )
    {
        unlock();
        return 0;
    }
    Result res = datenbank->getResult();
    unlock();
    int anz = res.zeilenAnzahl;
    for( int i = 0; i < anz; i++ )
        zOptionList->add( new Text( (char *)res.values[ i ] ) );
    res.destroy();
    return anz;
}

int MSDatenbank::getMinigameBestscore( char *options, char *minigame, Text *zPlayer )
{
    Text o( options );
    o.ersetzen( "'", "''" );
    Text m( minigame );
    m.ersetzen( "'", "''" );
    Text befehl = "SELECT b.score, a.ruf_name FROM account a, minigame_bestscore b, minigame_option o, minigame m "
        "WHERE m.id = o.minigame_id AND o.id = b.minigame_options_id AND a.id = b.account_id AND m.name = '";
    befehl += o;
    befehl += "' AND o.options = '";
    befehl += m;
    befehl += "'";
    lock();
    if( !datenbank->befehl( befehl ) )
    {
        unlock();
        return 0;
    }
    Result res = datenbank->getResult();
    unlock();
    if( !res.zeilenAnzahl )
    {
        res.destroy();
        return 0;
    }
    int ret = (int)res.values[ 0 ];
    zPlayer->setText( res.values[ 1 ] );
    res.destroy();
    return ret;
}

int MSDatenbank::getMinigameBestscore( char *minigame, Array< int > *zScoreList, RCArray< Text > *zPlayerList, RCArray< Text > *zOptionList )
{
    Text m( minigame );
    m.ersetzen( "'", "''" );
    Text befehl = "SELECT b.score, a.ruf_name, o.options FROM account a, minigame_bestscore b, minigame_option o, minigame m "
        "WHERE m.id = o.minigame_id AND o.id = b.minigame_options_id AND a.id = b.account_id AND m.name = '";
    befehl += m;
    befehl += "' ORDER BY o.options, b.score";
    lock();
    if( !datenbank->befehl( befehl ) )
    {
        unlock();
        return 0;
    }
    Result res = datenbank->getResult();
    unlock();
    if( !res.zeilenAnzahl )
    {
        res.destroy();
        return 0;
    }
    int anz = res.zeilenAnzahl;
    for( int i = 0; i < anz; i++ )
    {
        zScoreList->add( (int)res.values[ i * 3 ] );
        zPlayerList->add( new Text( res.values[ i * 3 + 1 ] ) );
        zOptionList->add( new Text( res.values[ i * 3 + 2 ] ) );
    }
    res.destroy();
    return anz;
}

bool MSDatenbank::updateMinigameScore( int score, char *option, int cId, char *minigame, int sId, Text *zFileName )
{
    Text o( option );
    o.ersetzen( "'", "''" );
    Text m( minigame );
    m.ersetzen( "'", "''" );
    Text befehl = "UPDATE minigame SET games_played = games_played + 1 WHERE name = '";
    befehl += m;
    befehl += "'";
    lock();
    if( !datenbank->befehl( befehl ) )
    {
        unlock();
        return 0;
    }
    unlock();
    befehl = "SELECT a.id FROM minigame_option a, minigame b WHERE a.minigame_id = b.id AND b.name = '";
    befehl += m;
    befehl += "' AND a.options = '";
    befehl += o;
    befehl += "'";
    lock();
    if( !datenbank->befehl( befehl ) )
    {
        unlock();
        return 0;
    }
    Result res = datenbank->getResult();
    unlock();
    if( !res.zeilenAnzahl )
    {
        res.destroy();
        return 0;
    }
    int optionId = res.values[ 0 ];
    *zFileName = optionId;
    *zFileName += ".mgc";
    res.destroy();
    befehl = "SELECT score FROM minigame_bestscore WHERE minigame_options_id = ";
    befehl += optionId;
    lock();
    if( !datenbank->befehl( befehl ) )
    {
        unlock();
        return 0;
    }
    res = datenbank->getResult();
    unlock();
    int anz = res.zeilenAnzahl;
    bool ret = !anz || (int)res.values[ 0 ] < score;
    res.destroy();
    if( ret )
    {
        if( anz )
        {
            befehl = "UPDATE minigame_bestscore b SET score = ";
            befehl += score;
            befehl += ", account_id = a.account_id, counter = counter + 1, minigame_server_id = ";
            befehl += sId;
            befehl += " FROM account_client a WHERE a.client_id = ";
            befehl += cId;
            befehl += " AND b.minigame_options_id = ";
            befehl += optionId;
        }
        else
        {
            befehl = "INSERT INTO minigame_bestscore( account_id, score, minigame_options_id, minigame_server_id ) SELECT a.account_id AS account_id, ";
            befehl += score;
            befehl += " AS score, ";
            befehl += optionId;
            befehl += " AS minigame_options_id, ";
            befehl += sId;
            befehl += " AS minigame_server_id FROM account_client a WHERE a.client_id = ";
            befehl += cId;
        }
        lock();
        if( !datenbank->befehl( befehl ) )
        {
            unlock();
            return 0;
        }
        unlock();
        befehl = "UPDATE account b SET kupfer = b.kupfer + a.counter FROM minigame_bestscore a WHERE a.account_id = b.id AND a.minigame_options_id = ";
        befehl += optionId;
        lock();
        if( !datenbank->befehl( befehl ) )
        {
            unlock();
            return 0;
        }
        unlock();
    }
    return ret;
}

Text *MSDatenbank::getMinigameCaptureFileName( char *option, char *minigame )
{
    Text o( option );
    o.ersetzen( "'", "''" );
    Text m( minigame );
    m.ersetzen( "'", "''" );
    Text befehl = "SELECT a.id FROM minigame_option a, minigame b WHERE a.minigame_id = b.id AND b.name = '";
    befehl += m;
    befehl += "' AND a.options = '";
    befehl += o;
    befehl += "'";
    lock();
    if( !datenbank->befehl( befehl ) )
    {
        unlock();
        return 0;
    }
    Result res = datenbank->getResult();
    unlock();
    if( !res.zeilenAnzahl )
    {
        res.destroy();
        return 0;
    }
    int optionId = res.values[ 0 ];
    Text *fileName = new Text();
    fileName->append( optionId );
    fileName->append( ".mgc" );
    res.destroy();
    return fileName;
}

int MSDatenbank::getMinigameServer( char *minigame, char *option )
{
    Text o( option );
    o.ersetzen( "'", "''" );
    Text m( minigame );
    m.ersetzen( "'", "''" );
    Text befehl = "SELECT a.id FROM server a, minigame_bestscore b, minigame_option c, minigame d WHERE a.id = b.minigame_server_id AND d.id = c.minigame_id AND b.minigame_options_id = c.id AND c.options = '";
    befehl += o;
    befehl += "' AND d.name = '";
    befehl += m;
    befehl += "'";
    lock();
    if( !datenbank->befehl( befehl ) )
    {
        unlock();
        return 0;
    }
    Result res = datenbank->getResult();
    unlock();
    int ret = 0;
    if( res.zeilenAnzahl )
        ret = res.values[ 0 ];
    res.destroy();
    return ret;
}

bool MSDatenbank::getMinigameServer( char *minigame, char *option, Text *zIp, Text *zPort )
{
    Text o( option );
    o.ersetzen( "'", "''" );
    Text m( minigame );
    m.ersetzen( "'", "''" );
    Text befehl = "SELECT a.ip, a.port FROM server a, minigame_bestscore b, minigame_option c, minigame d WHERE a.id = b.minigame_server_id AND d.id = c.minigame_id AND b.minigame_options_id = c.id AND c.options = '";
    befehl += o;
    befehl += "' AND d.name = '";
    befehl += m;
    befehl += "'";
    lock();
    if( !datenbank->befehl( befehl ) )
    {
        unlock();
        return 0;
    }
    Result res = datenbank->getResult();
    unlock();
    if( res.zeilenAnzahl )
    {
        zIp->setText( (char *)res.values[ 0 ] );
        zPort->setText( (char *)res.values[ 1 ] );
        res.destroy();
        return 1;
    }
    res.destroy();
    return 0;
}

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