#include "GSLDatei.h"
#ifdef WIN32
#include "Sound.h"
#else
#include "Sound.h"
#endif

using namespace GSL;

// Inhalt der GSLDatei Klasse aus GSLDatei.h
// Konstruktor
GSLDatei::GSLDatei()
{
	sounds = new Array< SoundKopf >();
	pfad = new Text();
	ref = 1;
}

// Destruktor
GSLDatei::~GSLDatei()
{
	sounds->release();
	pfad->release();
}

// Datei open
void GSLDatei::setDatei( Framework::Text *txt )
{
    cs.lock();
	pfad->setText( txt );
    cs.unlock();
}

void GSLDatei::setDatei( char *txt )
{
    cs.lock();
	pfad->setText( txt );
    cs.unlock();
}

bool GSLDatei::leseDaten()
{
    cs.lock();
	sounds->leeren();
	Datei d;
	d.setDatei( *pfad );
    if( !d.getSize() )
    {
        cs.unlock();
        return 1;
    }
	if( !d.open( Datei::Style::lesen ) )
	{
        cs.unlock();
		return 0;
	}
	unsigned char sAnz = 0;
	d.lese( (char*)&sAnz, 1 );
	// Datei Sound - Köpfe einlesen
	for( int i = 0; i < sAnz; i++ )
	{
		SoundKopf kpf;
		bool bit = 0;
		char len = 0;
		// Namenslänge lesen
		for( int i = 0; i < 4; i++ )
		{
			d.getNextBit( bit );
			len = (char)( len | ( ( (char)bit << ( 3 - i ) ) & ( 1 << ( 3 - i ) ) ) );
		}
		len &= 15;
		// Name lesen
		for( int i = 0; i < len; i++ )
		{
			char zeichen = 0;
			for( int j = 0; j < 5; j++ )
			{
				d.getNextBit( bit );
				zeichen = (char)( zeichen | ( ( (char)bit << ( 4 - j ) ) & ( 1 << ( 4 - j ) ) ) );
			}
			zeichen &= 31;
			if( zeichen == 27 )
				zeichen = 'ü';
			if( zeichen == 28 )
				zeichen = 'ö';
			if( zeichen == 29 )
				zeichen = 'ä';
			if( zeichen == 30 )
				zeichen = 'ß';
			if( zeichen == 31 )
				zeichen = '.';
			if( zeichen < 27 )
				zeichen = (char)( zeichen + 'a' );
			kpf.name.append( &zeichen, 1 );
		} // Chanel Anzahl lesen
		d.getNextBit( bit );
		kpf.channels = (char)( bit + 1 );
		// Sample Rate lesen
		d.lese( (char*)&kpf.sampleRate, 4 );
		// Position des Sounds in der Datei lesen
		d.lese( (char*)&kpf.datPos, 8 );
		// Länge des Sounds lesen
		int sLen = 0;
		d.lese( (char*)&sLen, 4 );
		kpf.datEnd = kpf.datPos + sLen;
		kpf.pfad = pfad->getText();
		sounds->add( kpf );
	}
	d.close();
    cs.unlock();
	return 1;
}

int GSLDatei::getSoundAnzahl()
{
	return sounds->getEintragAnzahl();
}

Text *GSLDatei::getSoundName( int num )
{
	if( !sounds->hat( num ) )
		return 0;
	return new Text( sounds->get( num ).name );
}

// Laden
GSLSoundV *GSLDatei::getSound( Framework::Text *name )
{
	GSLSoundV *ret = getSound( *name );
	name->release();
	return ret;
}

GSLSoundV *GSLDatei::getSound( char *name )
{
    cs.lock();
	int anz = sounds->getEintragAnzahl();
	for( int i = 0; i < anz; i++ )
	{
		if( sounds->get( i ).name.istGleich( name ) )
		{
            cs.unlock();
			return new GSLSound( sounds->get( i ) );
		}
	}
    cs.unlock();
	return 0;
}

// Speichern
bool GSLDatei::speicherSound( GSLSoundV *zSound, Framework::Text *name )
{
	bool ret = speicherSound( zSound, *name );
	name->release();
	return ret;
}

bool GSLDatei::speicherSound( GSLSoundV *zSound, char *name )
{
	Text kName;
	for( char *c = name; *c; ++c )
	{
		if( *c >= 'A' && *c <= 'Z' )
			kName.append( (char)( *c + 32 ) );
		if( *c >= 'a' && *c <= 'z' )
			kName.append( *c );
		if( *c == 'Ü' || *c == 'ü' )
			kName.append( 'ü' );
		if( *c == 'Ö' || *c == 'ö' )
			kName.append( 'ö' );
		if( *c == 'Ä' || *c == 'ä' )
			kName.append( 'ä' );
		if( *c == 'ß' )
			kName.append( 'ß' );
		if( *c == '.' )
			kName.append( '.' );
		if( kName.getLength() == 15 )
			break;
	}
	name = kName;
    cs.lock();
	// Prüfen ob bereits vorhanden
	int anz = sounds->getEintragAnzahl();
	for( int i = 0; i < anz; i++ )
	{
		if( sounds->get( i ).name.istGleich( name ) )
		{
            cs.unlock();
			return 0;
		}
	}
	Text tmpPf = pfad->getText();
	tmpPf += "_";
	GetFreePfad( &tmpPf );
	Datei tmp;
	tmp.setDatei( tmpPf );
	tmp.erstellen();
	if( !tmp.open( Datei::Style::schreiben ) )
	{
        cs.unlock();
		return 0;
	}
	char *buffer = new char[ 0x4000 ];
	// Neuen Sound in Temporäre Datei schreiben
	zSound->open();
	while( 1 )
	{
		int len = 0;
		if( ( len = zSound->getDaten( buffer, 0x4000 ) ) < 0 )
			break;
		tmp.schreibe( buffer, len );
	}
	zSound->close();
	delete buffer;
	tmp.close();
	int hLen = 1;
	unsigned char sAnz = (unsigned char)sounds->getEintragAnzahl();
	// Sound Kopf Länge errechnen
	for( int i = 0; i < sAnz; i++ )
	{
		SoundKopf kpf = sounds->get( i );
		hLen += ( 140 + 5 * kpf.name.getLength() ) / 8;
	}
	Datei alt;
	alt.setDatei( *pfad );
	// Sound Kopf Erstellen
	SoundKopf kpf;
	kpf.channels = (char)( !zSound->istMono() + 1 );
	kpf.name = name;
	kpf.pfad = pfad->getText();
	kpf.sampleRate = zSound->getSampleRate();
	kpf.datPos = ( alt.getSize() <= 0 ? 1 : alt.getSize() );
	kpf.datEnd = tmp.getSize() + kpf.datPos;
	sounds->add( kpf );
	Text neuPf = pfad->getText();
	neuPf += "_";
	GetFreePfad( &neuPf );
	Datei neu;
	neu.setDatei( neuPf );
	neu.erstellen();
	if( !neu.open( Datei::Style::schreiben ) )
	{
		sounds->remove( sAnz );
		tmp.remove();
        cs.unlock();
		return 0;
	}
	sAnz = (unsigned char)sounds->getEintragAnzahl();
	neu.schreibe( (char*)&sAnz, 1 );
	// Sound Köpfe speichern
	for( int i = 0; i < sAnz; i++ )
	{
		SoundKopf k = sounds->get( i );
		k.datPos += ( 140 + 5 * kpf.name.getLength() ) / 8;
		k.datEnd += ( 140 + 5 * kpf.name.getLength() ) / 8;
		int l = k.name.getLength();
		// Namenslänge speichern
		neu.setNextBit( ( l & 8 ) != 0 );
		neu.setNextBit( ( l & 4 ) != 0 );
		neu.setNextBit( ( l & 2 ) != 0 );
		neu.setNextBit( ( l & 1 ) != 0 );
		for( int j = 0; j < l; j++ )
		{ // Name speichern
			char c = k.name.getText()[ j ];
			if( c == 'ü' )
				c = 27;
			if( c == 'ö' )
				c = 28;
			if( c == 'ä' )
				c = 29;
			if( c == 'ß' )
				c = 30;
			if( c == '.' )
				c = 31;
			if( c >= 'a' && c <= 'z' )
				c = (char)( c - 'a' );
			neu.setNextBit( ( c & 16 ) != 0 );
			neu.setNextBit( ( c & 8 ) != 0 );
			neu.setNextBit( ( c & 4 ) != 0 );
			neu.setNextBit( ( c & 2 ) != 0 );
			neu.setNextBit( ( c & 1 ) != 0 );
		} // Chanels speichern
		neu.setNextBit( ( k.channels - 1 ) == 1 );
		// Sample Rate speichern
		neu.schreibe( (char*)&k.sampleRate, 4 );
		// Datei Position schreiben
		neu.schreibe( (char*)&k.datPos, 8 );
		// Länge des Sounds Speichern
		int sLen = (int)( k.datEnd - k.datPos );
		neu.schreibe( (char*)&sLen, 4 );
	}
	// alte Sounds kopieren
	char *byte = new char[ 2048 ];
	if( sAnz > 1 )
	{
		if( !alt.open( Datei::Style::lesen ) )
		{
			tmp.remove();
			neu.close();
			neu.remove();
			sounds->remove( sAnz );
            cs.unlock();
			return 0;
		}
		alt.setLPosition( hLen, 0 );
		for( __int64 i = hLen; i < alt.getSize(); )
		{
			int l = 2048;
			if( l > alt.getSize() - i )
				l = (int)( alt.getSize() - i );
			alt.lese( byte, l );
			neu.schreibe( byte, l );
			i += l;
		}
		alt.close();
	}
	// Neuen Sound kopieren
	if( !tmp.open( Datei::Style::lesen ) )
	{
		tmp.remove();
		neu.close();
		neu.remove();
		sounds->remove( sAnz );
        cs.unlock();
		return 0;
	}
	for( int i = 0; i < (int)tmp.getSize(); )
	{
		int l = 2048;
		if( l > tmp.getSize() - i )
			l = (int)( tmp.getSize() - i );
		tmp.lese( byte, l );
		neu.schreibe( byte, l );
		i += l;
	}
	delete[] byte;
	tmp.close();
	neu.close();
	// Dateien Umbenennen und remove
	tmp.remove();
	alt.remove();
	neu.umbenennen( *pfad );
	leseDaten();
    cs.unlock();
	return 1;
}

// Löschen
bool GSLDatei::removeSound( Framework::Text *name )
{
	bool ret = removeSound( *name );
	name->release();
	return ret;
}

bool GSLDatei::removeSound( char *name )
{
    cs.lock();
	// Prüfen ob vorhanden
	int anz = sounds->getEintragAnzahl();
	int num = -1;
	for( int i = 0; i < anz; i++ )
	{
		if( sounds->get( i ).name.istGleich( name ) )
		{
			num = i;
			break;
		}
	}
	if( num < 0 )
	{
        cs.unlock();
		return 1;
	}
	Text neuPf = pfad->getText();
	neuPf += "_";
	GetFreePfad( &neuPf );
	Datei neu;
	neu.setDatei( neuPf );
	neu.erstellen();
	if( !neu.open( Datei::Style::schreiben ) )
	{
        cs.unlock();
		return 0;
	}
	SoundKopf kpf = sounds->get( num );
	unsigned char sAnz = (unsigned char)( anz - 1 );
	neu.schreibe( (char*)&sAnz, 1 );
	// Sound Köpfe speichern
	for( int i = 0; i <= sAnz; i++ )
	{
		if( i == num )
			continue;
		SoundKopf k = sounds->get( i );
		k.datPos -= ( 140 + 5 * kpf.name.getLength() ) / 8;
		k.datEnd -= ( 140 + 5 * kpf.name.getLength() ) / 8;
		if( i > num )
		{
			k.datPos -= kpf.datEnd - kpf.datPos;
			k.datEnd -= kpf.datEnd - kpf.datPos;
		}
		int l = k.name.getLength();
		// Namenslänge speichern
		neu.setNextBit( ( l & 8 ) != 0 );
		neu.setNextBit( ( l & 4 ) != 0 );
		neu.setNextBit( ( l & 2 ) != 0 );
		neu.setNextBit( ( l & 1 ) != 0 );
		for( int j = 0; j < l; j++ )
		{ // Name speichern
			char c = k.name.getText()[ j ];
			if( c == 'ü' )
				c = 27;
			if( c == 'ö' )
				c = 28;
			if( c == 'ä' )
				c = 29;
			if( c == 'ß' )
				c = 30;
			if( c == '.' )
				c = 31;
			if( c >= 'a' && c <= 'z' )
				c = (char)( c - 'a' );
			neu.setNextBit( ( c & 16 ) != 0 );
			neu.setNextBit( ( c & 8 ) != 0 );
			neu.setNextBit( ( c & 4 ) != 0 );
			neu.setNextBit( ( c & 2 ) != 0 );
			neu.setNextBit( ( c & 1 ) != 0 );
		} // Chanels speichern
		neu.setNextBit( ( k.channels - 1 ) == 1 );
		// Sample Rate speichern
		neu.schreibe( (char*)&k.sampleRate, 4 );
		// Datei Position schreiben
		neu.schreibe( (char*)&k.datPos, 8 );
		// Länge des Sounds Speichern
		int sLen = (int)( k.datEnd - k.datPos );
		neu.schreibe( (char*)&sLen, 4 );
	}
	// Alte Sounds kopieren
	Datei alt;
	alt.setDatei( *pfad );
	if( sAnz )
	{
		if( !alt.open( Datei::Style::lesen ) )
		{
			neu.close();
			neu.remove();
            cs.unlock();
			return 0;
		}
		alt.setLPosition( neu.getSPosition() + ( 140 + 5 * kpf.name.getLength() ) / 8, 0 );
		char *byte = new char[ 2048 ];
		for( __int64 i = alt.getLPosition(); i < kpf.datPos; )
		{
			int l = 2048;
			if( l > kpf.datPos - i )
				l = (int)( kpf.datPos - i );
			alt.lese( byte, l );
			neu.schreibe( byte, l );
			i += l;
		}
		alt.setLPosition( kpf.datEnd, 0 );
		for( __int64 i = alt.getLPosition(); i < alt.getSize(); )
		{
			int l = 2048;
			if( l > alt.getSize() - i )
				l = (int)( alt.getSize() - i );
			alt.lese( byte, l );
			neu.schreibe( byte, l );
			i += l;
		}
		delete[] byte;
		alt.close();
	}
	neu.close();
	// Dateien Umbenennen und remove
	alt.remove();
	neu.umbenennen( *pfad );
	leseDaten();
    cs.unlock();
	return 1;
}

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

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