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

// Inhalt der ASDatenbank Klasse aus Datenbank.h
// Konstruktor
ASDatenbank::ASDatenbank( 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 << "AS: Die Verbindung zur Datenbank konnte nicht hergestellt werden.\nDas Programm wird beendet.";
        exit( 1 );
    }
    InitializeCriticalSection( &cs );
    ref = 1;
    Text befehl = "SELECT port, admin_port, ip  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 ] );
        zIni->addWert( "ServerIp", res.values[ 2 ] );
    }
    res.destroy();
}

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

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

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

int ASDatenbank::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 ASDatenbank::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 ASDatenbank::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() );
    int ret = datenbank->getZeilenAnzahl();
    unlock();
    befehl->release();
    return ret == 1;
}

Text *ASDatenbank::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 ASDatenbank::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 ASDatenbank::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 ASDatenbank::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 ASDatenbank::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;
}

bool ASDatenbank::getChatServer( int accountId, Text *ip, int *port )
{
    Text *befehl = new Text( "SELECT a.port, a.ip FROM server a, server_client b, account_client c "
        "WHERE a.server_typ_name = 'chat' AND c.client_id = b.client_id AND a.id = b.server_id AND c.account_id = " );
    befehl->append( accountId );
    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 );
    ip->setText( res.values[ 1 ].getText() );
    res.destroy();
    return 1;
}

int ASDatenbank::gruppeErstellen( int accountId, int karteId, int *gruppe_id )
{
    Text *befehl = new Text( "SELECT gruppe_erstellen( " );
    befehl->append( accountId );
    befehl->append( ", " );
    befehl->append( karteId );
    befehl->append( " )" );
    lock();
    if( !datenbank->befehl( befehl->getText() ) )
    {
        unlock();
        befehl->release();
        return 6;
    }
    Result res = datenbank->getResult();
    unlock();
    befehl->release();
    int ret = TextZuInt( res.values[ 0 ].getText(), 10 );
    if( res.zeilenAnzahl > 1 )
        *gruppe_id = TextZuInt( res.values[ 1 ].getText(), 10 );
    res.destroy();
    return ret;
}

bool ASDatenbank::gruppeAnmelden( int accountId, int gruppeId )
{
    Text *befehl = new Text( "SELECT * FROM gruppe WHERE id = " );
    befehl->append( gruppeId );
    befehl->append( " AND admin_account_id = " );
    befehl->append( accountId );
    lock();
    datenbank->befehl( befehl->getText() );
    if( !datenbank->getZeilenAnzahl() )
    {
        unlock();
        befehl->release();
        return 0;
    }
    unlock();
    befehl->setText( "SELECT gruppe_anmelden( " );
    befehl->append( gruppeId );
    befehl->append( " )" );
    lock();
    if( !datenbank->befehl( befehl->getText() ) )
    {
        unlock();
        befehl->release();
        return 0;
    }
    unlock();
    befehl->release();
    return 1;
}

bool ASDatenbank::gruppeAbmelden( int accountId, int gruppeId )
{
    Text *befehl = new Text( "SELECT * FROM gruppe WHERE id = " );
    befehl->append( gruppeId );
    befehl->append( " AND admin_account_id = " );
    befehl->append( accountId );
    lock();
    datenbank->befehl( befehl->getText() );
    if( !datenbank->getZeilenAnzahl() )
    {
        unlock();
        befehl->release();
        return 0;
    }
    unlock();
    befehl->setText( "SELECT gruppe_abmelden( " );
    befehl->append( gruppeId );
    befehl->append( " )" );
    lock();
    if( !datenbank->befehl( befehl->getText() ) )
    {
        unlock();
        befehl->release();
        return 0;
    }
    unlock();
    befehl->release();
    return 1;
}

bool ASDatenbank::setGruppeSpielStarten( int accountId, int gruppeId, bool starten )
{
    Text *befehl = new Text( "SELECT * FROM gruppe WHERE id = " );
    befehl->append( gruppeId );
    befehl->append( " AND admin_account_id = " );
    befehl->append( accountId );
    lock();
    datenbank->befehl( befehl->getText() );
    if( !datenbank->getZeilenAnzahl() )
    {
        unlock();
        befehl->release();
        return 0;
    }
    befehl->setText( "UPDATE gruppe SET spiel_starten = " );
    if( starten )
        befehl->append( "TRUE" );
    else
        befehl->append( "FALSE" );
    befehl->append( " WHERE id = " );
    befehl->append( gruppeId );
    datenbank->befehl( "BEGIN;" );
    datenbank->befehl( "LOCK TABLE lock_table;" );
    if( !datenbank->befehl( befehl->getText() ) )
    {
        datenbank->befehl( "ROLLBACK;" );
        unlock();
        befehl->release();
        return 0;
    }
    datenbank->befehl( "COMMIT;" );
    unlock();
    befehl->release();
    return 1;
}

bool ASDatenbank::getGruppeSpielStarten( int gruppeId )
{
    Text *befehl = new Text( "SELECT spiel_starten FROM gruppe WHERE id = " );
    befehl->append( gruppeId );
    lock();
    datenbank->befehl( befehl->getText() );
    if( !datenbank->getZeilenAnzahl() )
    {
        unlock();
        befehl->release();
        return 0;
    }
    Result res = datenbank->getResult();
    unlock();
    befehl->release();
    if( !res.zeilenAnzahl )
    {
        res.destroy();
        return 0;
    }
    bool ret = res.values[ 0 ].istGleich( "t" );
    res.destroy();
    return ret;
}

bool ASDatenbank::proveEinladung( int vonAccount, int zuAccount, int gruppeId )
{
    Text *befehl = new Text( "SELECT * FROM gruppe WHERE id = " );
    befehl->append( gruppeId );
    befehl->append( " AND admin_account_id = " );
    befehl->append( vonAccount );
    lock();
    datenbank->befehl( befehl->getText() );
    if( !datenbank->getZeilenAnzahl() )
    {
        unlock();
        return 0;
    }
    befehl->setText( "SELECT spieler_kann_spielen( " );
    befehl->append( zuAccount );
    befehl->append( " )" );
    if( !datenbank->befehl( befehl->getText() ) )
    {
        unlock();
        befehl->release();
        return 0;
    }
    Result res = datenbank->getResult();
    unlock();
    befehl->release();
    bool ret = TextZuInt( res.values[ 0 ].getText(), 10 ) == 6;
    res.destroy();
    return ret;
}

int ASDatenbank::gruppeBetreten( int accountId, int gruppeId )
{
    Text *befehl = new Text( "SELECT spieler_betritt_gruppe( " );
    befehl->append( accountId );
    befehl->append( ", " );
    befehl->append( gruppeId );
    befehl->append( " )" );
    lock();
    if( !datenbank->befehl( befehl->getText() ) )
    {
        unlock();
        befehl->release();
        return 6;
    }
    Result res = datenbank->getResult();
    unlock();
    befehl->release();
    int ret = TextZuInt( res.values[ 0 ].getText(), 10 );
    res.destroy();
    return ret;
}

int ASDatenbank::gruppeVerlassen( int accountId, int gruppeId )
{
    Text *befehl = new Text( "SELECT spieler_verlaesst_gruppe( " );
    befehl->append( accountId );
    befehl->append( ", " );
    befehl->append( gruppeId );
    befehl->append( " )" );
    lock();
    if( !datenbank->befehl( befehl->getText() ) )
    {
        unlock();
        befehl->release();
        return 0;
    }
    Result res = datenbank->getResult();
    unlock();
    befehl->release();
    int ret = TextZuInt( res.values[ 0 ].getText(), 10 );
    res.destroy();
    return ret;
}

bool ASDatenbank::spielerKannKicken( int accountId, int gruppeId )
{
    Text *befehl = new Text( "SELECT * FROM gruppe WHERE id = " );
    befehl->append( gruppeId );
    befehl->append( " AND admin_account_id = " );
    befehl->append( accountId );
    bool ret = 1;
    lock();
    datenbank->befehl( befehl->getText() );
    if( !datenbank->getZeilenAnzahl() )
        ret = 0;
    unlock();
    return ret;
}

bool ASDatenbank::getSpielerInGruppe( int gruppeId, Array< int > *accountId, int *anzahl )
{
    Text *befehl = new Text( "SELECT account_id FROM gruppe_spieler WHERE gruppe_id = " );
    befehl->append( gruppeId );
    lock();
    if( !datenbank->befehl( befehl->getText() ) )
    {
        unlock();
        befehl->release();
        return 0;
    }
    Result res = datenbank->getResult();
    unlock();
    befehl->release();
    *anzahl = res.zeilenAnzahl;
    for( int i = 0; i < *anzahl; i++ )
        accountId->add( TextZuInt( res.values[ i ].getText(), 10 ), i );
    res.destroy();
    return 1;
}

int ASDatenbank::getSpielerInGruppeAnzahl( int gruppeId )
{
    Text *befehl = new Text( "SELECT account_id FROM gruppe_spieler WHERE gruppe_id = " );
    befehl->append( gruppeId );
    lock();
    if( !datenbank->befehl( befehl->getText() ) )
    {
        unlock();
        befehl->release();
        return 0;
    }
    int ret = datenbank->getZeilenAnzahl();
    unlock();
    befehl->release();
    return ret;
}

int ASDatenbank::getGruppeAdminId( int gruppeId )
{
    Text *befehl = new Text( "SELECT admin_account_id FROM gruppe WHERE id = " );
    befehl->append( gruppeId );
    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 ASDatenbank::gruppeSpielerEinladen( int vonAccount, int zuAccount, int gruppeId )
{
    if( proveEinladung( vonAccount, zuAccount, gruppeId ) )
    {
        Text *befehl = new Text( "INSERT INTO gruppe_einladung( gruppe_id, account_id ) VALUES( " );
        befehl->append( gruppeId );
        befehl->append( ", " );
        befehl->append( zuAccount );
        befehl->append( " )" );
        lock();
        if( !datenbank->befehl( befehl->getText() ) )
        {
            unlock();
            befehl->release();
            return 0;
        }
        unlock();
        befehl->release();
        return 1;
    }
    return 0;
}

bool ASDatenbank::gruppeEinladungAbbrechen( int admin, int gruppeId, int accountId )
{
    if( admin == getGruppeAdminId( gruppeId ) )
    {
        Text *befehl = new Text( "DELETE FROM gruppe_einladung WHERE gruppe_id =  " );
        befehl->append( gruppeId );
        befehl->append( " AND account_id = " );
        befehl->append( accountId );
        lock();
        if( !datenbank->befehl( befehl->getText() ) )
        {
            unlock();
            befehl->release();
            return 0;
        }
        unlock();
        befehl->release();
        return 1;
    }
    return 0;
}

bool ASDatenbank::gruppeSpielerIstEingeladen( int gruppeId, int accountId )
{
    Text *befehl = new Text( "SELECT * FROM gruppe_einladung WHERE gruppe_id = " );
    befehl->append( gruppeId );
    befehl->append( " AND account_id = " );
    befehl->append( accountId );
    lock();
    if( !datenbank->befehl( befehl->getText() ) )
    {
        unlock();
        befehl->release();
        return 0;
    }
    bool ret = datenbank->getZeilenAnzahl() != 0;
    unlock();
    befehl->release();
    return ret;
}

bool ASDatenbank::gruppeIstInWarteschlange( int gruppeId )
{
    Text *befehl = new Text( "SELECT * FROM gruppe_anmeldung_fuer_spiel WHERE gruppe_id = " );
    befehl->append( gruppeId );
    lock();
    if( !datenbank->befehl( befehl->getText() ) )
    {
        unlock();
        befehl->release();
        return 0;
    }
    bool ret = datenbank->getZeilenAnzahl() != 0;
    unlock();
    befehl->release();
    return ret;
}

int ASDatenbank::getAccountId( int clientId )
{
    Text *befehl = new Text( "SELECT account_id FROM account_client WHERE client_id = " );
    befehl->append( clientId );
    lock();
    if( !datenbank->befehl( befehl->getText() ) )
    {
        unlock();
        befehl->release();
        return 0;
    }
    if( !datenbank->getZeilenAnzahl() )
    {
        unlock();
        befehl->release();
        return 0;
    }
    Result res = datenbank->getResult();
    unlock();
    befehl->release();
    int ret = TextZuInt( res.values[ 0 ].getText(), 10 );
    res.destroy();
    return ret;
}

int ASDatenbank::accountAnmelden( int accountId, int karteId )
{
    Text *befehl = new Text( "SELECT account_anmelden( " );
    befehl->append( accountId );
    befehl->append( ", " );
    befehl->append( karteId );
    befehl->append( " )" );
    lock();
    if( !datenbank->befehl( befehl->getText() ) )
    {
        unlock();
        befehl->release();
        return 6;
    }
    Result res = datenbank->getResult();
    unlock();
    befehl->release();
    int ret = TextZuInt( res.values[ 0 ].getText(), 10 );
    res.destroy();
    return ret;
}

void ASDatenbank::accountAbmelden( int accountId )
{
    Text *befehl = new Text( "SELECT account_abmelden( " );
    befehl->append( accountId );
    befehl->append( " )" );
    lock();
    datenbank->befehl( befehl->getText() );
    unlock();
    befehl->release();
}

bool ASDatenbank::spielerBesitztKarte( int accountId, int karteId )
{
    Text *befehl = new Text( "SELECT a.account_id FROM spiel_art_account a, karte_account b, karte c WHERE a.account_id = " );
    befehl->append( accountId );
    befehl->append( " AND a.spiel_art_id = c.spiel_art_id AND b.karte_id = c.id AND b.account_id = a.account_id AND c.id = " );
    befehl->append( karteId );
    lock();
    if( !datenbank->befehl( befehl->getText() ) )
    {
        unlock();
        befehl->release();
        return 0;
    }
    int zeilen = datenbank->getZeilenAnzahl();
    unlock();
    befehl->release();
    return zeilen != 0;
}

int ASDatenbank::getGruppenKarte( int gruppeId )
{
    Text *befehl = new Text( "SELECT karte_id FROM gruppe WHERE id = " );
    befehl->append( gruppeId );
    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;
}

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

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

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

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