/***************************************************************************
                          krecfile.cpp  -  description
                             -------------------
    copyright            : (C) 2003 by Arnold Krille
    email                : arnold@arnoldarts.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; version 2 of the License.               *
 *                                                                         *
 ***************************************************************************/

#include "krecfile.h"
#include "krecfile.moc"

#include "krecglobal.h"

#include "krecnewproperties.h"

#include <kdebug.h>
#include <kapplication.h>
#include <ktempdir.h>
#include <ktempfile.h>
#include <ktar.h>
#include <kio/job.h>
#include <klocale.h>
#include <ksimpleconfig.h>
#include <qdir.h>
#include <qfileinfo.h>
#include <kmessagebox.h>
#include <qtimer.h>

#include <qdatastream.h>
#include <math.h>

void KRecFile::init() {
	_pos = 0;
	_size = 0;
	_filename = QString::null;
	_currentBuffer = 0;
	_dir = new KTempDir();
	_config = new KSimpleConfig( _dir->name()+"project.rc", false );
}

KRecFile::KRecFile( QObject* p, const char* n )
  : QObject( p,n )
  , _saved( false )
{
	init();
	kdDebug( 60005 ) << k_funcinfo << "_dir->name(): " << _dir->name() << endl;
	_dir->setAutoDelete( true );
	KRecNewProperties* dialog = new KRecNewProperties( KRecGlobal::the()->mainWidget() );

	if ( dialog->usedefaults() )
		KRecGlobal::the()->message( i18n( "Using default properties for the new file" ) );
	else
		dialog->exec();

	_samplerate = dialog->samplerate();
	_channels = dialog->channels();
	_bits = dialog->bits();

	saveProps();

	delete dialog;
}
KRecFile::KRecFile( QString filename, QObject* p, const char* n )
  : QObject( p,n )
  , _saved( true )
{
	init();
	_filename = filename;
	kdDebug( 60005 ) << k_funcinfo << "_dir->name(): " << _dir->name() << endl;
	_dir->setAutoDelete( true );

	KTar *tar = new KTar( _filename, "application/x-gzip" );
	tar->open( IO_ReadOnly );
	int i=0; while ( _filename.find( '/', i ) != -1 ) i++; // Find last '/'
	QString basename = _filename.right( _filename.length()-i );
	basename = basename.left( basename.length()-5 );

	const KArchiveDirectory *dir = dynamic_cast<const KArchiveDirectory*>( tar->directory()->entry( basename ) );

	dir->copyTo( _dir->name() );

	delete _config;
	_config = new KSimpleConfig( _dir->name()+"project.rc", false );
	loadProps();
	int c = _config->readNumEntry( "Files" );
	//kdDebug( 60005 ) << c << " Files to load" << endl;
	for ( int i=0; i<c; i++ ) {
		//kdDebug( 60005 ) << "Loading file " << i << endl;
		_config->setGroup( "File-" + QString::number( i ) );
		newBuffer( KRecBuffer::fromConfig( _config, _dir->qDir(), this ) );
	}
	KRecGlobal::the()->message( i18n( "\'%1\' loaded." ).arg( filename ) );

	_saved = true;
	//kdDebug( 60005 ) << "Buffers opened: " << _buffers.count() << endl;
}
KRecFile::~KRecFile() {
	kdDebug( 60005 ) << k_funcinfo << endl;
	QValueList<KRecBuffer*>::iterator it;
	for ( it = _buffers.begin(); it != _buffers.end(); ++it )
		delete ( *it );
	_buffers.clear();
	delete _dir;
        delete _config;
	//kdDebug( 60005 ) << k_funcinfo << "done." << endl;
}

QString KRecFile::filename() { return _filename; }
void KRecFile::filename( QString n ) {
	if ( _filename!=n ) {
		_filename = n;
		emit filenameChanged( _filename );
	}
}

void KRecFile::writeData( Arts::mcopbyte* /*data*/, uint /*length*/ ) {
	kdDebug( 60005 ) << k_funcinfo << endl;
}
void KRecFile::writeData( QByteArray* data ) {
	kdDebug( 60005 ) << k_funcinfo << endl;
	if ( _currentBuffer!=-1 ) _buffers[ _currentBuffer ]->writeData( data );
	_saved = false;
}
void KRecFile::writeData( QByteArray& data ) {
	if ( _currentBuffer!=-1 ) _buffers[ _currentBuffer ]->writeData( data );
	_saved = false;
}

void KRecFile::save( QString fname ) {
	//kdDebug( 60005 ) << k_funcinfo << filename << endl;
	if ( !saved() ) {
		KRecGlobal::the()->message( i18n( "Saving in progress..." ) );
		filename( fname );
		QString tmpname;
		{
			KTempFile *tmp = new KTempFile();
			tmp->setAutoDelete( true );
			tmpname = tmp->name();
			delete tmp;
		}
		saveProps();

		KTar *tar = new KTar( tmpname, "application/x-gzip" );
		tar->open( IO_WriteOnly );
		int i=0; while ( fname.find( '/', i ) != -1 ) i++; // Find last '/'
		QString basename = fname.right( fname.length()-i );
		if ( basename.endsWith( ".krec" ) )
			basename = basename.left( basename.length()-5 );
			else filename( fname += ".krec" );

		tar->addLocalDirectory( _dir->name(), basename );

		delete tar;
		KIO::file_move( tmpname, fname, -1, true, false, true );

		KRecGlobal::the()->message( i18n( "Saving \"%1\" was successful." ).arg( filename() ) );
		_saved = true;
	} else KRecGlobal::the()->message( i18n( "No need to save." ) );
}
void KRecFile::exportwave( QString filename ) {
	kdDebug( 60005 ) << k_funcinfo << filename << endl;
}

QByteArray* KRecFile::getData( int ) {
	kdDebug( 60005 ) << k_funcinfo << endl;
	return 0;
}

void KRecFile::getData( QByteArray& data ) {
	KRecBuffer* current = getTopBuffer_buffer( _pos );
	if ( current ) {
	//kdDebug( 60005 ) << "_pos=" << _pos << "(" << samplesToOffset( _pos ) << ") current->startpos()=" << current->startpos() << "(" << samplesToOffset( current->startpos() ) << ") that is " << samplesToOffset( _pos - current->startpos() ) << endl;
		current->setPos( samplesToOffset( _pos - current->startpos() ) );
		current->getData( data );
	} else {
		for ( uint i=0; i<data.size(); i++ )
			data[ i ] = 0;
	}
	newPos( _pos + offsetToSamples( data.size() ) );
	if ( position() >= size() ) emit endReached();
}


// * * * Position * * *
void KRecFile::newPos( int p ) {
	if ( _pos != p ) {
		_pos = p;
		emit posChanged( _pos );
	}
}
void KRecFile::newPos( KRecBuffer* buffer, QIODevice::Offset p ) {
	newPos( buffer->startpos() + offsetToSamples( p ) );
}
void KRecFile::newSize( KRecBuffer* buffer, QIODevice::Offset p ) {
	if ( buffer->startpos() + offsetToSamples( p ) > _size ) {
		_size = buffer->startpos() + offsetToSamples( p );
	}
	emit sizeChanged( _size );
}


/// * * * Frames <-> Offset and more * * *
int KRecFile::offsetToSamples( QIODevice::Offset n ) const {
	int out = n / channels();
	if ( bits() == 16 ) out /= 2;
	return out;
}
QIODevice::Offset KRecFile::samplesToOffset( int n ) const {
	QIODevice::Offset out = n * this->channels();
	if ( bits() == 16 ) out *= 2;
	return out;
}

/// * * * Properties <-> KConfig * * *
void KRecFile::saveProps() {
	kdDebug( 60005 ) << k_funcinfo << endl;
	_config->setGroup( "General" );
	_config->writeEntry( "Samplerate", _samplerate );
	_config->writeEntry( "Bits", _bits );
	_config->writeEntry( "Channels", _channels );

	_config->writeEntry( "Files", _buffers.count() );
	for ( uint i=0; i<_buffers.count(); i++ ) {
		//kdDebug( 60005 ) << "Writing config " << i << endl;
		_config->setGroup( "File-" + QString::number( i ) );
		_buffers[ i ]->writeConfig( _config );
	}
	_config->sync();
}
void KRecFile::loadProps() {
	kdDebug( 60005 ) << k_funcinfo << endl;
	_config->setGroup( "General" );
	_samplerate = _config->readNumEntry( "Samplerate", 44100 );
	_bits = _config->readNumEntry( "Bits", 16 );
	_channels = _config->readNumEntry( "Channels", 2 );
}


/// * * * NewBuffer * * *
void KRecFile::newBuffer( QString filename ) {
	kdDebug( 60005 ) << k_funcinfo << filename << endl;
	newBuffer( new KRecBuffer( filename, _pos, true, this ) );
}
void KRecFile::newBuffer( KRecBuffer* buffer ) {
	kdDebug( 60005 ) << k_funcinfo << endl;
	connect( buffer, SIGNAL( posChanged( KRecBuffer*, QIODevice::Offset ) ), this, SLOT( newPos( KRecBuffer*, QIODevice::Offset ) ) );
	connect( buffer, SIGNAL( sizeChanged( KRecBuffer*, QIODevice::Offset ) ), this, SLOT( newSize( KRecBuffer*, QIODevice::Offset ) ) );
	connect( buffer, SIGNAL( deleteSelf( KRecBuffer* ) ), this, SLOT( deleteBuffer( KRecBuffer* ) ) );
	_buffers.append( buffer );
	newSize( buffer, buffer->size() );
	_currentBuffer = _buffers.findIndex( buffer );
	emit sNewBuffer( buffer );
	_saved = false;
}
void KRecFile::newBuffer() {
	kdDebug( 60005 ) << k_funcinfo << endl;
	newBuffer( _dir->name() + "file" + QString::number( _buffers.count() ) + ".raw" );
}

void KRecFile::deleteBuffer( KRecBuffer* b ) {
	kdDebug( 60005 ) << k_funcinfo << b << endl;
	emit sDeleteBuffer( b );
	delete b;
	if ( _buffers.remove( b ) )
		_currentBuffer = -1;
	KRecGlobal::the()->message( i18n( "Part deleted." ) );
	_saved = false;
}

/// * * * helper * * *
KRecBuffer* KRecFile::getTopBuffer_buffer( int pos ) {
	//kdDebug( 60005 ) << k_funcinfo << pos << endl;
	QValueList<KRecBuffer*>::iterator it = _buffers.begin();
	KRecBuffer* out = 0;
	while ( it != _buffers.end() ) {
		if ( ( *it )->startpos() <= pos && offsetToSamples( ( *it )->size() ) + ( *it )->startpos() > pos && ( *it )->active() )
			out = ( *it );
		++it;
	}
	return out;
}
int KRecFile::getTopBuffer_int( int pos ) {
	return _buffers.findIndex( getTopBuffer_buffer( pos ) );
}


/// * * * KRecBuffer * * *
KRecBuffer::KRecBuffer( QString filename, int startpos, bool a, KRecFile* p, const char* n )
  : QObject( p,n )
  , _krecfile( p )
  , _file( new QFile( filename ) )
  , _stream( new QDataStream( _file ) )
  , _fileinfo( new QFileInfo( filename ) )
  , _active( a )
  , _pos( 0 ), _start( startpos )
  , _title( _fileinfo->fileName() )
  , _comment( QString::null )
{
	kdDebug( 60005 ) << k_funcinfo << filename << " " << startpos << endl;
	_open = _file->open( IO_Raw | IO_ReadWrite );
	setPos( _file->at() );
	if ( _open ) kdDebug( 60005 ) << k_funcinfo << "Open successfull!" << endl;
		else kdDebug( 60005 ) << endl << k_funcinfo << "Could not open file!" << endl << endl;
}
KRecBuffer::~KRecBuffer() {
	kdDebug( 60005 ) << k_funcinfo << endl;
	if ( _open ) {
		_file->close();
		_open = false;
		_file->remove();
	}
	//kdDebug( 60005 ) << k_funcinfo << "done." << endl;
}

void KRecBuffer::writeConfig( KConfig* config ) {
	//kdDebug( 60005 ) << k_funcinfo << config << endl;
	config->writeEntry( "Filename", _fileinfo->fileName() );
	config->writeEntry( "StartPos", _start );
	config->writeEntry( "Activated", _active );
	config->writeEntry( "Title", _title );
	config->writeEntry( "Comment", _comment );
}

KRecBuffer* KRecBuffer::fromConfig( KConfig* config, QDir* dir, KRecFile* p, const char* n ) {
	kdDebug( 60005 ) << k_funcinfo << config << endl;
	KRecBuffer* tmp = new KRecBuffer( dir->path() + "/" + config->readEntry( "Filename" ),
	                       config->readNumEntry( "StartPos" ),
	                       config->readBoolEntry( "Activated", true ),
	                       p, n );
	tmp->setTitle( config->readEntry( "Title", tmp->filename() ) );
	tmp->setComment( config->readEntry( "Comment", QString::null ) );
	return tmp;
}

void KRecBuffer::writeData( Arts::mcopbyte* /*data*/, uint /*length*/ ) {
	kdDebug( 60005 ) << k_funcinfo << endl;
}
void KRecBuffer::writeData( QByteArray* data ) {
kdDebug( 60005 ) << k_funcinfo << endl;
	writeData( *data );
/*	if ( _open ) {
		_file->at( _pos );
		_file->writeBlock( *data );
		setPos( _file->at() );
		emit sizeChanged( this, size() );
	}*/
}
void KRecBuffer::writeData( QByteArray& data ) {
	//kdDebug( 60005 ) << k_funcinfo << endl;
	if ( _open ) {
		_file->at( _pos );
		_file->writeBlock( data );
		setPos( _file->at() );
		emit sizeChanged( this, size() );
	}
}

void KRecBuffer::getData( QByteArray& data ) {
	//kdDebug( 60005 ) << k_funcinfo << "data.size()" << data.size() << " _pos" << _pos << endl;
	if ( _pos > _file->size() )
		kdWarning() << "Trying to access behind file!" << endl;
	else {
		if ( _open ) {
			_file->at( _pos );
			//kdDebug( 60005 ) << "Reading " << _file->readBlock( ( char* )data.data(), data.size() ) << "bytes into data." << endl;
			//kdDebug( 60005 ) << "data.size()" << data.size() << endl;
			for ( uint i=0; i<data.size(); i++ ) {
				if ( !_file->atEnd() )
					data.data()[ i ] = _file->getch();
				else
					data.data()[ i ] = 0;
			}
			//setPos( _file->at() );
		}
	}
}

void KRecBuffer::setPos( QIODevice::Offset p ) {
	if ( p!=_pos ) {
		_pos = p;
		emit posChanged( this, _pos );
//kdDebug( 60005 ) << k_funcinfo << _pos << endl;
	}
}

int KRecBuffer::startpos() const { return _start; }
QIODevice::Offset KRecBuffer::size() const { return _file->size(); }

QString KRecBuffer::filename() const { return _fileinfo->fileName(); }
QString KRecBuffer::title() const { return _title; }
QString KRecBuffer::comment() const { return _comment; }

void KRecBuffer::setTitle( QString n ) {
	if ( _title != n ) {
		_title = n;
		emit somethingChanged();
	}
}

void KRecBuffer::setComment( QString n ) {
	if ( _comment != n ) {
		_comment = n;
		emit somethingChanged();
	}
}

bool KRecBuffer::active() const { return _active; }
void KRecBuffer::setActive( bool n ) {
	if ( _active != n ) {
		_active = n;
		emit activeChanged( _active );
		emit somethingChanged();
	}
}

void KRecBuffer::deleteBuffer() {
	if ( KMessageBox::questionYesNo( KRecGlobal::the()->mainWidget(), i18n( "Do you really want to delete the selected part '%1'?" ).arg( this->filename() ), "Delete part?" ) == KMessageBox::Yes )
		_krecfile->deleteBuffer( this );
}

float KRecBuffer::getSample( int pos, int /*channel*/ ) {
	Q_INT16 tmp16;
	Q_INT8  tmp8;
	float out;
	_file->at( _krecfile->samplesToOffset( pos ) );
	if ( _krecfile->bits() == 16 ) {
		*_stream >> tmp16;
		out = tmp16 / 65535.0;
	}
	else {
		*_stream >> tmp8;
		out = tmp8 / 65535.0;
	}
	return out;
}
float* KRecBuffer::getsamples( int start, int end, int channel ) {
	float* tmp = new float[ end-start ];
	for ( int i=start; i<end; ++i )
		tmp[ i ] = getSample( i, channel );
	return tmp;
}

