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

// Inhalt der CSDatenbank Klasse aus Datenbank.h
// Konstruktor
CSDatenbank::CSDatenbank( 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 << "CS: Die Verbindung zur Datenbank konnte nicht hergestellt werden.\nDas Programm wird beendet.";
        exit( 1 );
    }
	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
CSDatenbank::~CSDatenbank()
{
	datenbank->release();
}

// nicht constant
void CSDatenbank::lock()
{
    cs.lock();
}

void CSDatenbank::unlock()
{
    cs.unlock();
}

int CSDatenbank::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 CSDatenbank::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 CSDatenbank::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 *CSDatenbank::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 CSDatenbank::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 CSDatenbank::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 CSDatenbank::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 CSDatenbank::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 CSDatenbank::getKlientAccountId( int klientId )
{
	Text *befehl = new Text( "SELECT account_id FROM account_client WHERE client_id = " );
	befehl->append( klientId );
	lock();
	datenbank->befehl( befehl->getText() );
	Result res = datenbank->getResult();
	unlock();
	befehl->release();
	if( res.zeilenAnzahl )
	{
		int ret = TextZuInt( res.values[ 0 ].getText(), 10 );
		res.destroy();
		return ret;
	}
	res.destroy();
	return 0;
}

int CSDatenbank::getAccountFreunde( int accountId, Array< int > *fAccountId )
{
	Text *befehl = new Text( "SELECT account_id AS freundId FROM freund WHERE freund_account_id = " );
	befehl->append( accountId );
	befehl->append( " UNION SELECT freund_account_id AS freundId FROM freund WHERE account_id = " );
	befehl->append( accountId );
	lock();
	datenbank->befehl( befehl->getText() );
	Result res = datenbank->getResult();
	unlock();
	befehl->release();
	int anzahl = res.zeilenAnzahl;
	for( int i = 0; i < anzahl; i++ )
		fAccountId->add( TextZuInt( res.values[ i ].getText(), 10 ), i );
	res.destroy();
	return anzahl;
}

int CSDatenbank::getAccountOnlineFreunde( int accountId, Array< int > *fAccountId )
{
	Text *befehl = new Text( "SELECT freund.account_id AS freundId FROM freund, account_client WHERE freund.freund_account_id = " );
	befehl->append( accountId );
	befehl->append( " AND freund.account_id = account_client.account_id "
					  "UNION SELECT freund.freund_account_id AS freundId FROM freund, account_client WHERE freund.account_id = " );
	befehl->append( accountId );
	befehl->append( " AND freund.freund_account_id = account_client.account_id " );
	lock();
	datenbank->befehl( befehl->getText() );
	Result res = datenbank->getResult();
	unlock();
	befehl->release();
	int anzahl = res.zeilenAnzahl;
	for( int i = 0; i < anzahl; i++ )
		fAccountId->add( TextZuInt( res.values[ i ].getText(), 10 ), i );
	res.destroy();
	return anzahl;
}

bool CSDatenbank::accountNameChange( int accountId, const char *name )
{
	if( !name )
		return 1;
	Text *befehl = new Text( "UPDATE account SET ruf_name = '" );
	Text n( name );
	n.ersetzen( "'", "''" );
	befehl->append( (char*)n );
	befehl->append( "' WHERE id = " );
	befehl->append( accountId );
	bool ret = 0;
	lock();
	ret = datenbank->befehl( befehl->getText() );
	unlock();
	befehl->release();
	return ret;
}

bool CSDatenbank::beendeFreundschaft( int accountId1, int accountId2 )
{
	Text *befehl = new Text( "DELETE FROM freund WHERE ( account_id = " );
	befehl->append( accountId1 );
	befehl->append( " AND freund_account_id = " );
	befehl->append( accountId2 );
	befehl->append( " ) OR ( freund_account_id = " );
	befehl->append( accountId1 );
	befehl->append( " AND account_id = " );
	befehl->append( accountId2 );
	befehl->append( " )" );
	lock();
	if( !datenbank->befehl( befehl->getText() ) )
	{
		unlock();
		return 0;
	}
	unlock();
	befehl->release();
	return 1;
}

bool CSDatenbank::proveFreundschaftsAnfrage( int vonAccountId, int zuAccountId )
{
	if( vonAccountId == zuAccountId )
		return 0;
	Text *befehl = new Text( "SELECT account_id AS freundId FROM freund WHERE freund_account_id = " );
	befehl->append( vonAccountId );
	befehl->append( " AND freund.account_id = " );
	befehl->append( zuAccountId );
	befehl->append( "UNION SELECT freund_account_id AS freundId FROM freund WHERE freund_account_id = " );
	befehl->append( zuAccountId );
	befehl->append( " AND freund.account_id = " );
	befehl->append( vonAccountId );
	lock();
	datenbank->befehl( befehl->getText() );
	Result res = datenbank->getResult();
	unlock();
	befehl->release();
	bool ret = res.zeilenAnzahl == 0;
	res.destroy();
	return ret;
}

bool CSDatenbank::saveFreundschaftsAnfrage( int vonAccountId, int zuAccountId )
{
	Text *befehl = new Text( "SELECT account_id FROM freund_anfrage WHERE ( account_id = " );
	befehl->append( vonAccountId );
	befehl->append( " AND freund_id = " );
	befehl->append( zuAccountId );
	befehl->append( " ) OR ( freund_id = " );
	befehl->append( vonAccountId );
	befehl->append( " AND account_id = " );
	befehl->append( zuAccountId );
	befehl->append( " )" );
	lock();
	if( !datenbank->befehl( befehl->getText() ) )
	{
		unlock();
		befehl->release();
		return 0;
	}
	Result res = datenbank->getResult();
	unlock();
	if( res.zeilenAnzahl )
	{
		res.destroy();
		befehl->release();
		return 1;
	}
	res.destroy();
	befehl->setText( "INSERT INTO freund_anfrage( account_id, freund_id ) VALUES( " );
	befehl->append( vonAccountId );
	befehl->append( ", " );
	befehl->append( zuAccountId );
	befehl->append( " )" );
	lock();
	if( !datenbank->befehl( befehl->getText() ) )
	{
		unlock();
		befehl->release();
		return 0;
	}
	unlock();
	befehl->release();
	return 1;
}

int CSDatenbank::getFreundschaftsAnfragen( int accountId, Array< int > *vonAccountIds )
{
	Text *befehl = new Text( "SELECT account_id FROM freund_anfrage WHERE freund_id = " );
	befehl->append( accountId );
	lock();
	if( !datenbank->befehl( befehl->getText() ) )
	{
		unlock();
		befehl->release();
		return 0;
	}
	Result res = datenbank->getResult();
	unlock();
	befehl->setText( "DELETE FROM freund_anfrage WHERE freund_id = " );
	befehl->append( accountId ); lock();
	if( !datenbank->befehl( befehl->getText() ) )
	{
		unlock();
		befehl->release();
		res.destroy();
		return 0;
	}
	unlock();
	befehl->release();
	int anzahl = res.zeilenAnzahl;
	for( int i = 0; i < anzahl; i++ )
		vonAccountIds->set( res.values[ i ], i );
	res.destroy();
	return anzahl;
}

bool CSDatenbank::neueFreundschaft( int accountId1, int accountId2 )
{
	Text *befehl = new Text( "INSERT INTO freund VALUES( " );
	befehl->append( accountId1 );
	befehl->append( ", " );
	befehl->append( accountId2 );
	befehl->append( " )" );
	lock();
	bool ret = datenbank->befehl( befehl->getText() );
	unlock();
	befehl->release();
	return ret;
}

Text *CSDatenbank::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();
	Text *ret = new Text( res.values[ 0 ].getText() );
	res.destroy();
	return ret;
}

bool CSDatenbank::accountIstOnline( int accountId )
{
	Text *befehl = new Text( "SELECT * FROM account_client WHERE account_id = " );
	befehl->append( accountId );
	lock();
	datenbank->befehl( befehl->getText() );
	int res = datenbank->getZeilenAnzahl();
	unlock();
	befehl->release();
	return res != 0;
}

bool CSDatenbank::accountIstImSpiel( int accountId )
{
	Text *befehl = new Text( "SELECT b.id FROM spiel_spieler a, spiel b WHERE b.spiel_status_id = 2 "
							 "AND a.spiel_spieler_status_id = 5 AND a.spiel_id = b.id AND a.account_id = " );
	befehl->append( accountId );
	lock();
	datenbank->befehl( befehl->getText() );
	int res = datenbank->getZeilenAnzahl();
	unlock();
	befehl->release();
	return res != 0;
}

int CSDatenbank::getChatServerId( int accountId )
{
	Text *befehl = new Text( "SELECT a.server_id FROM server_client a, account_client b, server c "
							 "WHERE a.client_id = b.client_id AND a.server_id = c.id AND c.server_typ_name = 'chat' AND b.account_id = " );
	befehl->append( accountId );
	lock();
	datenbank->befehl( befehl->getText() );
	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 CSDatenbank::getChatServerIpPort( int serverId, unsigned short *port, char **ip )
{
	Text *befehl = new Text( "SELECT port, ip FROM server WHERE id = " );
	befehl->append( serverId );
	lock();
	datenbank->befehl( befehl->getText() );
	Result res = datenbank->getResult();
	unlock();
	befehl->release();
	if( !res.zeilenAnzahl )
	{
		res.destroy();
		return 0;
	}
	*port = (unsigned short)TextZuInt( res.values[ 0 ].getText(), 10 );
	int len = res.values[ 1 ].getLength();
	char *ipTmp = new char[ len + 1 ];
	ipTmp[ len ] = 0;
	int i = 0;
	for( char *c = res.values[ 1 ].getText(); i < len; c++ )
	{
		ipTmp[ i ] = *c;
		i++;
	}
	res.destroy();
	return 1;
}

int CSDatenbank::getChatNachrichten( int accountId, Array< int > *vonAccount, RCArray< Text > *nachricht )
{
	Text *befehl = new Text( "SELECT von_account_id, nachricht FROM chat_nachricht WHERE zu_account_id = " );
	befehl->append( accountId );
	lock();
	datenbank->befehl( befehl->getText() );
	Result res = datenbank->getResult();
	unlock();
	if( !res.zeilenAnzahl )
	{
		befehl->release();
		res.destroy();
		return 0;
	}
	int ret = res.zeilenAnzahl;
	for( int i = 0; i < ret; i++ )
	{
		vonAccount->add( TextZuInt( res.values[ i * 2 ].getText(), 10 ), i );
		nachricht->add( new Text( res.values[ i * 2 + 1 ].getText() ), i );
	}
	res.destroy();
	befehl->setText( "DELETE FROM chat_nachricht WHERE zu_account_id = " );
	befehl->append( accountId );
	lock();
	datenbank->befehl( befehl->getText() );
	unlock();
	befehl->release();
	return ret;
}

bool CSDatenbank::speicherChatNachricht( int vonAccount, int zuAccount, const char *nachricht )
{
	if( !nachricht )
		return 1;
	Text *befehl = new Text( "INSERT INTO chat_nachricht VALUES( " );
	befehl->append( vonAccount );
	befehl->append( ", " );
	befehl->append( zuAccount );
	befehl->append( ", '" );
	Text n( nachricht );
	n.ersetzen( "'", "''" );
	befehl->append( (char*)n );
	befehl->append( "' )" );
	lock();
	bool ret = datenbank->befehl( befehl->getText() );
	unlock();
	befehl->release();
	return ret;
}

int CSDatenbank::getChatroomAccount( int chatroomId, Array< int > *accountId )
{
	Text *befehl = new Text( "SELECT account_id FROM chatroom_spieler WHERE chatroom_id = " );
	befehl->append( chatroomId );
	lock();
	datenbank->befehl( befehl->getText() );
	Result res = datenbank->getResult();
	unlock();
	befehl->release();
	if( !res.zeilenAnzahl )
	{
		res.destroy();
		return 0;
	}
	int ret = res.zeilenAnzahl;
	for( int i = 0; i < ret; i++ )
		accountId->add( TextZuInt( res.values[ i ].getText(), 10 ), i );
	res.destroy();
	return ret;
}

int CSDatenbank::chatroomErstellen( int accountId, const char *name )
{
	Text *befehl = new Text( "INSERT INTO chatroom( admin_account_id, name ) VALUES( " );
	befehl->append( accountId );
	befehl->append( ", '" );
	Text n( name );
	n.ersetzen( "'", "''" );
	befehl->append( (char*)n );
	befehl->append( "' ) RETURNING id" );
	lock();
	if( !datenbank->befehl( befehl->getText() ) )
	{
		unlock();
		befehl->release();
		return 0;
	}
	Result res = datenbank->getResult();
	unlock();
	befehl->setText( "INSERT INTO chatroom_spieler VALUES( ( SELECT id FROM chatroom WHERE name = '" );
	befehl->append( (char*)n );
	befehl->append( "' ), " );
	befehl->append( accountId );
	befehl->append( " )" );
	lock();
	datenbank->befehl( befehl->getText() );
	unlock();
	befehl->release();
	if( !res.zeilenAnzahl )
	{
		res.destroy();
		return 0;
	}
	int ret = TextZuInt( res.values[ 0 ].getText(), 10 );
	res.destroy();
	return ret;
}

bool CSDatenbank::proveChatroomEinladung( int vonAccount, int zuAccount, int chatroomId )
{
	Text *befehl = new Text( "SELECT pruef_chatroom_einladung( " );
	befehl->append( chatroomId );
	befehl->append( ", " );
	befehl->append( vonAccount );
	befehl->append( ", " );
	befehl->append( zuAccount );
	befehl->append( " )" );
	lock();
	datenbank->befehl( befehl->getText() );
	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 CSDatenbank::chatroomBeitreten( int accountId, int chatroomId )
{
	Text *befehl = new Text( "INSERT INTO chatroom_spieler values( " );
	befehl->append( chatroomId );
	befehl->append( " , " );
	befehl->append( accountId );
	befehl->append( " )" );
	lock();
	bool ret = datenbank->befehl( befehl->getText() );
	unlock();
	befehl->release();
	return ret;
}

int CSDatenbank::chatroomVerlassen( int accountId, int chatroomId )
{
	Text *befehl = new Text( "SELECT chatroom_verlassen( " );
	befehl->append( chatroomId );
	befehl->append( ", " );
	befehl->append( accountId );
	befehl->append( " )" );
	lock();
	datenbank->befehl( befehl->getText() );
	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 CSDatenbank::getChatroomAdmin( int chatroomId )
{
	Text *befehl = new Text( "SELECT admin_account_id FROM chatroom WHERE id = " );
	befehl->append( chatroomId );
	lock();
	datenbank->befehl( befehl->getText() );
	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;
}

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

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

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