
#include "filelist.h"
#include "cdmanager.h"
#include "tagengine.h"
#include "options.h"
#include "convert.h"
#include "optionseditor.h"
#include "outputdirectory.h"
#include "configuration.h"
#include "logger.h"
#include "convertpluginloader.h" // NOTE DEBUG

#include <tdelocale.h>
#include <tdepopupmenu.h>
#include <tdeaction.h>
#include <tdeactioncollection.h>
#include <kmimetype.h>
#include <kurl.h>
#include <kmountpoint.h>
#include <kstandarddirs.h>
#include <kurldrag.h>
#include <tdeapplication.h>

#include <tqlayout.h>
#include <tqfileinfo.h>
#include <tqsimplerichtext.h>
#include <tqpainter.h>
#include <tqapplication.h>
#include <tqdragobject.h>
#include <tqheader.h>
#include <tqdir.h>
#include <kprogress.h>
#include <kuser.h>

// TODO when stopping items by using the context menu, the queue mode restarts that item

// TODO when dropping items, don't let the user select the position

// ### soundkonverter 0.4: draw tooltip like bubble info box

FileListItem::FileListItem( TDEListView* parent, FileListItem* after )
    : TDEListViewItem( parent, after )
{
    tags = 0;
    converting = false;
    time = 0;
    ripping = false;
}

FileListItem::FileListItem( TDEListView* parent )
    : TDEListViewItem( parent )
{
    tags = 0;
    converting = false;
    time = 0;
    ripping = false;
}

FileListItem::~FileListItem()
{}

void FileListItem::paintCell( TQPainter *p, const TQColorGroup &cg, int column, int width, int alignment )
{
    // NOTE speed up this function

    if( column == ((FileList*)listView())->columnByName(i18n("Input")) || column == ((FileList*)listView())->columnByName(i18n("Output")) )
    {
        int margin = listView()->itemMargin();
        int w = width - 2*margin;
        int h = height();
        TQRect textRect = p->boundingRect( margin, 0, w, h, alignment, text(column) );

        if( textRect.width() > w ) {
            alignment = TQt::AlignRight | TQt::SingleLine;
        }

        /*if ( textRect.width() <= w ) {
            p->drawText( margin, 0, w, h, alignment | TQt::SingleLine | TQt::ExpandTabs, text(column), -1 );
        }
        else {
            textRect = p->boundingRect( margin, 0, w, h, TQt::AlignLeft, "... " );
            p->drawText( margin, 0, textRect.width(), h, TQt::AlignLeft | TQt::SingleLine | TQt::ExpandTabs, "...", -1 );
            p->drawText( margin+textRect.width(), 0, w-textRect.width(), h, TQt::AlignRight | TQt::SingleLine | TQt::ExpandTabs, text(column), -1 );
        }*/
    }

    if( converting ) {
        TQColorGroup _cg( cg );
        if ( isSelected() ) {
            _cg.setColor( TQColorGroup::Highlight, shiftColorToRed(_cg.highlight(), 40) );
        } else {
            _cg.setColor( TQColorGroup::Base, shiftColorToRed(backgroundColor(column), 40));
        }
        TQListViewItem::paintCell( p, _cg, column, width, alignment );
    } else {
        TDEListViewItem::paintCell( p, cg, column, width, alignment );
    }
}

/*void FileListItem::updateOutputCell()
{
    setText( ((FileList*)listView())->columnByName(i18n("Output")), OutputDirectory::calcPath(this) ); // FIXME no config !!!
}

void FileListItem::updateOptionsCell()
{
    setText( ((FileList*)listView())->columnByName(i18n("Quality")), ((FileList*)listView())->config->getProfileName(options) );
}*/

FileList::FileList( CDManager* _cdManager, TagEngine* _tagEngine, Config* _config, Options* _options, Logger* _logger, TQWidget* parent, const char* name )
    : TDEListView( parent, name )
{
    cdManager = _cdManager;
    tagEngine = _tagEngine;
    config = _config;
    options = _options;
    logger = _logger;
    optionsEditor = 0;

    queue = false;
    notify = "";

    setAcceptDrops( true );
    setDragEnabled( true );

    addColumn( i18n("State"), 120 );
    addColumn( i18n("Input"), 180 );
    addColumn( i18n("Output"), 180 );
    addColumn( i18n("Quality") );

    header()->setClickEnabled( false );

    setSelectionMode( TQListView::Extended );
    setAllColumnsShowFocus( true );
    setResizeMode( LastColumn );
    setSorting( -1 ); // NOTE if commented out, items aren't moveable anymore

    setMinimumHeight( 200 );

    TQGridLayout* grid = new TQGridLayout( this, 2, 1, 11, 6 );
    grid->setRowStretch( 0, 1 );
    grid->setRowStretch( 2, 1 );
    grid->setColStretch( 0, 1 );
    grid->setColStretch( 2, 1 );
    pScanStatus = new KProgress( this, "pScanStatus" );
    pScanStatus->setMinimumHeight( pScanStatus->height() );
    pScanStatus->setFormat( "%v / %m" );
    pScanStatus->hide();
    grid->addWidget( pScanStatus, 1, 1 );
    grid->setColStretch( 1, 2 );

    contextMenu = new TDEPopupMenu( this );
    connect( this, TQ_SIGNAL(contextMenuRequested( TQListViewItem*, const TQPoint&, int )),
               this, TQ_SLOT(showContextMenu( TQListViewItem*, const TQPoint&, int ))
             );

    // we haven't got access to the action collection of soundKonverter, so let's create a new one
    actionCollection = new TDEActionCollection( this );

    edit = new TDEAction( i18n("Edit options ..."), "view_text", 0, this, TQ_SLOT(showOptionsEditorDialog()), actionCollection, "edit_options" );
    start = new TDEAction( i18n("Start conversion"), "system-run", 0, this, TQ_SLOT(convertSelectedItems()), actionCollection, "start_conversion" );
    stop = new TDEAction( i18n("Stop conversion"), "process-stop", 0, this, TQ_SLOT(stopSelectedItems()), actionCollection, "stop_conversion" );
    remove = new TDEAction( i18n("Remove"), "edittrash", Key_Delete, this, TQ_SLOT(removeSelectedItems()), actionCollection, "remove" );
    paste = new TDEAction( i18n("Paste"), "edit-paste", 0, this, 0, actionCollection, "paste" );  // TODO paste

    connect( this, TQ_SIGNAL(selectionChanged()),
               this, TQ_SLOT(itemsSelected())
             );

//     connect( this, TQ_SIGNAL(clicked(TQListViewItem*,const TQPoint&,int)),
//                this, TQ_SLOT(clickedSomewhere(TQListViewItem*,const TQPoint&,int))
//              );

    bubble = new TQSimpleRichText( i18n( "<div align=center>"
                                 "<h3>File List</h3>"
                                 "Select your desired output options in the form above and add some files.<br/>"
                                 "You can add files by clicking on \"Add files ...\" or dropping them here."
//                                  "<br/><a href=\"documenation:about_compression\">Learn more about audio compression ...</a>"
                                 "</div>" ), TQApplication::font() );

    connect( header(), TQ_SIGNAL(sizeChange( int, int, int )),
               TQ_SLOT(columnResizeEvent( int, int, int ))
             );
    connect( this, TQ_SIGNAL( dropped(TQDropEvent*, TQListViewItem*, TQListViewItem*) ),
               TQ_SLOT( slotDropped(TQDropEvent*, TQListViewItem*, TQListViewItem*) )
             );

//     if( TQFile::exists(locateLocal("data","soundkonverter/filelist.autosave.xml")) ) load( true );

//     debug(); // NOTE DEBUG
}

FileList::~FileList()
{
    delete optionsEditor;
}

int FileList::columnByName( const TQString& name )
{
    for( int i = 0; i < columns(); ++i ) {
        if( columnText( i ) == name ) return i;
    }
    return -1;
}

void FileList::viewportPaintEvent( TQPaintEvent* e )
{
    TDEListView::viewportPaintEvent( e );

    // the bubble help
    if( childCount() == 0 ) {
        TQPainter p( viewport() );

        bubble->setWidth( width() - 50 );

        const uint w = bubble->width() + 20;
        const uint h = bubble->height() + 20;

        p.setBrush( colorGroup().background() );
        p.drawRoundRect( 15, 15, w, h, (8*200)/w, (8*200)/h );
        bubble->draw( &p, 20, 20, TQRect(), colorGroup() );
    }
}

void FileList::viewportResizeEvent( TQResizeEvent* )
{
    // needed for correct redraw of bubble help
    triggerUpdate();
}

void FileList::columnResizeEvent( int, int, int )
{
    // needed for correct redraw of bubble help
    triggerUpdate();
}

// void FileList::clickedSomewhere( TQListViewItem*, const TQPoint& pos, int )
// {
// /*    if( childCount() == 0 ) {
//         kdDebug() << "clicked: `" << bubble->anchorAt(mapFromGlobal(pos)-TQPoint(24,0)) << " (" << pos.x() << " | " << pos.y() << ")'" << endl;
//     }*/
// }

bool FileList::acceptDrag( TQDropEvent* e ) const
{
    return ( e->source() == viewport() || KURLDrag::canDecode(e) ); // TODO verify the files
}

void FileList::slotDropped( TQDropEvent* e, TQListViewItem*, TQListViewItem* itemAfter )
{
    TQString file;
    KURL::List list;
    TQStringList files;
    if( KURLDrag::decode( e, list ) ) // TODO local?
    {
        save( true );
        for( KURL::List::Iterator it = list.begin(); it != list.end(); ++it )
        {
            // TODO verify the files (necessary when multiple files are being dropped)
            // TODO implement cdda:/
            file = TQDir::convertSeparators( (*it).pathOrURL() ); // TODO implement that in the url/file dialog, too?
            TQFileInfo fileInfo( file );
            if( fileInfo.isDir() )
            {
                addDir( file );
            }
            else
            {
                files.append( (*it).url() );
            }
        }
        addFiles( files, (FileListItem*)itemAfter, true );
        save( true );
    }
}

void FileList::showContextMenu( TQListViewItem* item, const TQPoint& point, int )
{
    // if item is null, we can abort here
    if( !item ) return;

    // remove all items from the context menu
    contextMenu->clear();

    // add a tilte to our context manu
    //contextMenu->insertTitle( static_cast<FileListItem*>(item)->fileName ); // TODO sqeeze or something else

    // TODO implement pasting, etc.

    // is this file (of our item) beeing converted at the moment?
    if( !static_cast<FileListItem*>(item)->converting ) {
        edit->plug( contextMenu );
        contextMenu->insertSeparator();
        remove->plug( contextMenu );
        //paste->plug( contextMenu );
        contextMenu->insertSeparator();
        start->plug( contextMenu );
    }
    else {
        stop->plug( contextMenu );
        //contextMenu->insertSeparator();
        //remove->plug( contextMenu );
        //paste->plug( contextMenu );
    }

    // show the popup menu
    contextMenu->popup( point );
}

void FileList::removeSelectedItems()
{
    FileListItem *item = firstChild(), *nextitem = 0;

    while( item != 0 ) {
        if( item->isSelected() && !item->converting ) {
            nextitem = item->nextSibling();
            emit decreaseTime( item->time );
            delete item;
            item = nextitem;
        }
        else {
            item = item->nextSibling();
        }
    }
    emit fileCountChanged( childCount() );
    itemsSelected();
}

void FileList::convertSelectedItems()
{
    FileListItem* item = firstChild();

    while( item != 0 ) {
        if( item->isSelected() && !item->converting ) {
            emit convertItem( item );
        }
        item = item->nextSibling();
    }
    itemsSelected();
    emit startedConversion();
}

void FileList::stopSelectedItems()
{
    FileListItem* item = firstChild();

    while( item != 0 ) {
        if( item->isSelected() && item->converting ) {
            emit stopItem( item );
        }
        item = item->nextSibling();
    }
}

int FileList::listDir( const TQString& directory, TQStringList filter, bool recursive, bool fast, int count )
{ // NOTE speed up?
    TQDir dir( directory );
    dir.setFilter( TQDir::Files | TQDir::Dirs | TQDir::NoSymLinks | TQDir::Readable );

    TQStringList list = dir.entryList();

    for( TQStringList::Iterator it = list.begin(); it != list.end(); ++it ) {
        if( *it == "." || *it == ".." ) continue;
        TQFileInfo fileInfo( directory + "/" + *it );
        if( fast ) {
            if( fileInfo.isDir() && recursive ) {
                count = listDir( fileInfo.filePath(), filter, recursive, fast, count );
            }
            else if( !fileInfo.isDir() || !recursive ) { // NOTE checking for isFile may not work with all file names
                // NOTE filter feature
                for( TQStringList::Iterator jt = filter.begin(); jt != filter.end(); ++jt ) {
                    if( (*it).endsWith("."+(*jt),false) ) {
                        count++;
                        pScanStatus->setTotalSteps( count );
                        break;
                    }
                }
                if( filter.first() == "" ) {
                    count++;
                    pScanStatus->setTotalSteps( count );
                }
            }
        }
        else {
            if( fileInfo.isDir() && recursive ) {
                count = listDir( fileInfo.filePath(), filter, recursive, fast, count );
            }
            else if( !fileInfo.isDir() || !recursive ) { // NOTE checking for isFile may not work with all file names
                // NOTE filter feature
                for( TQStringList::Iterator jt = filter.begin(); jt != filter.end(); ++jt ) {
                    if( (*it).endsWith("."+(*jt),false) ) {
                        addFiles( KURL::encode_string(directory + "/" + *it) );
                        count++;
                        pScanStatus->setProgress( count );
                        break;
                    }
                }
                if( filter.first() == "" ) {
                    addFiles( KURL::encode_string(directory + "/" + *it) );
                    count++;
                    pScanStatus->setProgress( count );
                }
            }
        }
    }

    return count;
}

// NOTE progressbar when adding files?
void FileList::addFiles( TQStringList fileList, FileListItem* after, bool enabled )
{
    // TODO test if everything works with remote files (http://) and local files (media://)
    FileListItem* lastListItem;
    if( !after && !enabled ) lastListItem = lastItem();
    else lastListItem = after;
    TQString filePathName;
    TQString device;

    for( TQStringList::Iterator it = fileList.begin(); it != fileList.end(); ++it ) {
        FileListItem* newItem = new FileListItem( this, lastListItem );
        lastListItem = newItem;
        newItem->options = options->getCurrentOptions(); // FIXME speed up
        newItem->notify = notify;
        newItem->local = false;
        newItem->track = -1;
        newItem->url = *it;

        if( (*it).left( 1 ) == "/" ) {
            filePathName = *it;
            newItem->local = true;
        }
        else if( (*it).left( 7 ) == "file://" ) {
            filePathName = *it;
            filePathName.remove( 0, 7 );
            newItem->local = true;
        }
        else if( (*it).left( 13 ) == "system:/home/" ) {
            filePathName = *it;
            filePathName.remove( 0, 13 );
            filePathName = TQDir::homeDirPath() + "/" + filePathName;
            newItem->local = true;
        }
        else if( (*it).left( 14 ) == "system:/users/" || (*it).left( 6 ) == "home:/" ) {
            int length = ( (*it).left(6) == "home:/" ) ? 6 : 14;
            TQString username = *it;
            username.remove( 0, length );
            username = username.left( username.find("/") );
            filePathName = *it;
            filePathName.remove( 0, length + username.length() );
            KUser user( username );
            filePathName = user.homeDir() + filePathName;
            newItem->local = true;
        }
        else if( (*it).left( 14 ) == "system:/media/" || (*it).left( 7 ) == "media:/" ) {
            int length = ( (*it).left(7) == "media:/" ) ? 7 : 14;
            device = *it;
            device.remove( 0, length );
            device = "/dev/" + device.left( device.find( "/" ) );

            KMountPoint::List mountPoints = KMountPoint::possibleMountPoints();

            for( KMountPoint::List::ConstIterator jt = mountPoints.begin(); jt != mountPoints.end(); ++jt )
            {
                const TDESharedPtr<KMountPoint> mp = *jt;
                logger->log( 1000, mp->mountedFrom() + " : " + mp->mountPoint() );
                if( mp->mountedFrom() == device )
                {
                    filePathName = ( mp->mountPoint() == "/" ) ? mp->mountPoint() : mp->mountPoint() + "/";
                    filePathName += (*it).right( (*it).length() - device.length() - length + 4 );
                }
            }

            newItem->local = true;
        }
//         else if( (*it).left( 14 ) == "system:/trash/" || (*it).left( 7 ) == "trash:/" ) {
//         }

        if( newItem->local == true ) {
//             logger->log( 1000, i18n("Adding file") + ": " + filePathName );
            newItem->mimeType = KMimeType::findByFileContent( filePathName )->name();
            newItem->fileFormat = KMimeType::findByFileContent( filePathName )->patterns().first().lower();
            newItem->fileFormat.remove( 0, 2 );
//             logger->log( 1000, " " + i18n("Mime type") + ": " + newItem->mimeType + " (" + i18n("Format") + ": " + newItem->fileFormat + ")" );
//             newItem->mimeType="";
            if( newItem->mimeType.isEmpty() || newItem->mimeType == "application/octet-stream" || newItem->mimeType == "text/plain" ) {
                newItem->mimeType = KMimeType::findByURL( filePathName.lower() )->name();
                newItem->fileFormat = KMimeType::findByURL( filePathName.lower() )->patterns().first().lower();
                newItem->fileFormat.remove( 0, 2 );
//             logger->log( 1000, " " + i18n("Mime type") + ": " + newItem->mimeType + " (" + i18n("Format") + ": " + newItem->fileFormat + ")" );
//             newItem->mimeType="";
                // HACK last choise is to use the extension without KDE's help
                if( newItem->mimeType.isEmpty() || newItem->mimeType == "application/octet-stream" || newItem->mimeType == "text/plain" ) {
                    newItem->fileFormat = filePathName.right( filePathName.length() - filePathName.findRev(".") - 1 );
                    FormatItem *formatItem = config->getFormatItem( newItem->fileFormat );
                    if( formatItem ) newItem->mimeType = formatItem->mime_types.first();
//             logger->log( 1000, " " + i18n("Mime type") + ": " + newItem->mimeType + " (" + i18n("Format") + ": " + newItem->fileFormat + ")" );
                }
            }
//             logger->log( 1000, " " + i18n("Mime type") + ": " + newItem->mimeType + " (" + i18n("Format") + ": " + newItem->fileFormat + ")" );
            // check whether the mime type has a decoder registered
            if( !config->acceptFile( newItem->mimeType ) || ( newItem->fileFormat == "wav" && newItem->options.encodingOptions.sFormat == "wav" ) ) {
                delete newItem;
                continue;
            }
            newItem->options.filePathName = filePathName;
            TQFileInfo fileInfo( filePathName );
            newItem->fileName = fileInfo.fileName();
            newItem->tags = tagEngine->readTags( KURL::decode_string(filePathName) );
            if( newItem->tags == 0 ) {
//                 logger->log( 1000, " " + i18n("Reading tags failed") );
                // FIXME check for wav files
                FormatItem* formatItem = config->getFormatItem( newItem->mimeType );
                if( formatItem && formatItem->size > 0 ) {
                    newItem->time = fileInfo.size() / formatItem->size;
                }
                else {
                    newItem->time = 210;
                }
            }
            else {
//                 logger->log( 1000, " " + i18n("Tags successfully read") );
                newItem->time = newItem->tags->length;
            }
        }
        else {
//             logger->log( 1000, " File is remote (not yet implemented) (" + *it + ")" );
            filePathName = *it;
            newItem->fileName = filePathName.right( filePathName.length() - filePathName.findRev("/") - 1 );
            newItem->fileFormat = newItem->fileName.right( newItem->fileName.length() - newItem->fileName.findRev(".") - 1 ).lower();
            // NOTE http will not work with KMimeType - this just works
            if( filePathName.startsWith("http://") ) {
                newItem->mimeType = KMimeType::findByURL( "file:///" + newItem->fileName.lower() )->name();
            }
            else {
                newItem->mimeType = KMimeType::findByURL( filePathName.lower() )->name();
            }
            // check whether the mime type has a decoder registered
            if( newItem->mimeType == "" ) {
                newItem->mimeType = newItem->fileFormat;
            }
            if( !config->acceptFile( newItem->mimeType ) || ( newItem->fileFormat == "wav" && newItem->options.encodingOptions.sFormat == "wav" ) ) {
                delete newItem;
                continue;
            }
            newItem->options.filePathName = filePathName;
            newItem->tags = 0;
            newItem->time = 210; // NOTE we can't neither get the length nor guess it without accessing the file
                                 // TODO get the file size and guess the length
        }

        updateItem( newItem );
        emit increaseTime( newItem->time );
    }
    emit fileCountChanged( childCount() );
}

void FileList::addDir( const TQString& directory, const TQStringList& filter, bool recursive )
{
    pScanStatus->setProgress( 0 );
    pScanStatus->setTotalSteps( 0 );
    pScanStatus->show(); // show the status while scanning the directories
    kapp->processEvents();

    int count = listDir( directory, filter, recursive, true );
    listDir( directory, filter, recursive );

    pScanStatus->hide(); // hide the status bar, when the scan is done
}

void FileList::addTracks( const TQString& device, TQValueList<int> trackList )
{
    for( TQValueList<int>::Iterator it = trackList.begin(); it != trackList.end(); ++it ) {
//         logger->log( 1000, i18n("Adding track") + TQString().sprintf(" %02i",*it) );
        FileListItem* newItem = new FileListItem( this, lastItem() );
        newItem->options = options->getCurrentOptions(); // FIXME speed up
//         if( newItem->options.outputOptions.mode != OutputDirectory::Specify ) newItem->options.outputOptions.mode = OutputDirectory::MetaData;
        newItem->notify = notify;
        newItem->track = (*it);
        newItem->fileName = i18n("Audio CD (%1)").arg(device) + ": " + i18n("Track") + TQString().sprintf(" %02i",newItem->track);
        newItem->mimeType = "application/x-cda";
        newItem->fileFormat = "cda";
        newItem->local = true;
        newItem->device = device;
        newItem->tags = cdManager->getTags( device, newItem->track );
        if( newItem->tags == 0 ) { // NOTE shouldn't happen
//             logger->log( 1000, " " + i18n("Reading tags failed") );
            newItem->time = 210;
            newItem->options.filePathName = TQDir::homeDirPath() + "/" + i18n("Track") + TQString().sprintf(" %02i",newItem->track) + "." + newItem->fileFormat;
        }
        else {
//             logger->log( 1000, " " + i18n("Tags successfully read") );
            newItem->time = newItem->tags->length;
            newItem->options.filePathName = TQDir::homeDirPath() + "/" + TQString().sprintf("%02i - ",newItem->track) + newItem->tags->title + "." + newItem->fileFormat;
            newItem->fileName = i18n("Audio CD (%1)").arg(device) + ": " + TQString().sprintf("%02i - ",newItem->track) + newItem->tags->title;
        }
        updateItem( newItem );
        emit increaseTime( newItem->time );
    }
    emit fileCountChanged( childCount() );
}

void FileList::addDisc( const TQString& device )
{
//     logger->log( 1000, i18n("Adding full audio CD (%1)").arg(device) );
    FileListItem* newItem = new FileListItem( this, lastItem() );
    newItem->options = options->getCurrentOptions();
//     if( newItem->options.outputOptions.mode != OutputDirectory::Specify ) newItem->options.outputOptions.mode = OutputDirectory::MetaData;
    newItem->notify = notify;
    newItem->track = 0;
    newItem->fileName = i18n("Full audio CD (%1)").arg(device);
    newItem->mimeType = "application/x-cda";
    newItem->fileFormat = "cda";
    newItem->local = true;
    newItem->device = device;
    newItem->tags = cdManager->getTags( device, 0 );
    if( newItem->tags == 0 ) { // NOTE shouldn't happen
//         logger->log( 1000, " " + i18n("Reading tags failed") );
        newItem->time = 3600;
        newItem->options.filePathName = TQDir::homeDirPath() + "/" + i18n("Audio CD") + "." + newItem->fileFormat;
    }
    else {
//         logger->log( 1000, " " + i18n("Tags successfully read") );
        newItem->time = newItem->tags->length;
        newItem->options.filePathName = TQDir::homeDirPath() + newItem->tags->title + "." + newItem->fileFormat;
        newItem->fileName = i18n("Full audio CD (%1)").arg(device) + ": " + newItem->tags->title;
    }
    updateItem( newItem );
    emit increaseTime( newItem->time );
    emit fileCountChanged( childCount() );
}

void FileList::itemFinished( FileListItem* item, int state )
{
    if( state == 0 ) {
        if( item ) delete item;
        itemsSelected();
    }
    else if( state == 1 ) {
        item->setText( columnByName(i18n("State")), i18n("Stopped") );
    }
    else {
        item->setText( columnByName(i18n("State")), i18n("Failed") );
    }

    save( true );

    FileListItem* it = firstChild();
    int waitingCount = 0, convertingCount = 0;

    while( it != 0 ) {
        if( it->text(columnByName(i18n("State"))) != i18n("Failed") && it->text(columnByName(i18n("State"))) != i18n("Stopped") && it->text(columnByName(i18n("State"))) != i18n("Will be skipped") ) {
            if( it->text(columnByName(i18n("State"))) == i18n("Waiting") /*|| it->text(columnByName(i18n("State"))) == i18n("Stopped")*/ ) {
                waitingCount++;
            }
            else {
                convertingCount++;
            }
        }
        it = it->nextSibling();
    }

    if( waitingCount > 0 && queue ) {
        convertNextItem();
    }
    else if( convertingCount == 0 ) {
        queue = false;
        float time = 0;
        it = firstChild();
        while( it != 0 ) {
//             it->setText( columnByName(i18n("State")), i18n("Waiting") );
            updateItem( it );
            time += it->time;
            it = it->nextSibling();
        }
        emit finished( time );
        emit stoppedConversion();
        emit fileCountChanged( childCount() );
    }
}

void FileList::rippingFinished( const TQString& device )
{
    if( !queue ) return;

    int count = 0;
    FileListItem *item = firstChild();
    while( item != 0 ) {
        if( item->converting ) {
            count++;
        }
        item = item->nextSibling();
    }

    // look for "Waiting" files first ...
    item = firstChild();
    while( item != 0 && count < config->data.general.numFiles ) {
        if( !item->converting && item->text(columnByName(i18n("State"))) == i18n("Waiting") ) {
            if( item->track >= 0 && item->device == device ) {
                convertItem( item );
                itemsSelected();
                return;
            }
        }
        item = item->nextSibling();
    }

    // ... then convert the stopped, too
/*    item = firstChild();
    while( item != 0 && count < config->data.general.numFiles ) {
        if( !item->converting && item->text(columnByName(i18n("State"))) == i18n("Stopped") ) {
            if( item->track >= 0 && item->device == device ) {
                convertItem( item );
                itemsSelected();
                return;
            }
        }
        item = item->nextSibling();
    }*/
}

void FileList::updateItem( FileListItem* item )
{
    if( !item ) return;

    if( !item->converting ) {
        if( config->data.general.conflictHandling == 1 && TQFile::exists(KURL::decode_string(OutputDirectory::calcPath(item,config))) ) { // was: .replace("%2f","%252f")
            item->setText( columnByName(i18n("State")), i18n("Will be skipped") );
        }
        else {
            item->setText( columnByName(i18n("State")), i18n("Waiting") );
        }
    }
    else {
        item->setText( columnByName(i18n("State")), i18n("Converting") );
    }

    if( item->track >= 0 ) item->setText( columnByName(i18n("Input")), KURL::decode_string(item->fileName).replace("%2f","/").replace("%%","%") );
    else item->setText( columnByName(i18n("Input")), KURL::decode_string(item->options.filePathName).replace("%2f","/").replace("%%","%") );

    item->setText( columnByName(i18n("Output")), KURL::decode_string(OutputDirectory::uniqueFileName(OutputDirectory::calcPath(item,config))).replace("%2f","/").replace("%%","%") );

    item->setText( columnByName(i18n("Quality")), config->getProfileName(item->options) );
}

void FileList::showOptionsEditorDialog()
{
    // FIXME options are set to defaults

    if( optionsEditor == 0 ) {
         optionsEditor = new OptionsEditor( tagEngine, config, this, 0, "optionsEditor" );
         if( optionsEditor == 0 ) {
             // TODO error message
             return;
         }
//     }
    connect( this, TQ_SIGNAL(editItems(TQValueList<FileListItem*>)),
               optionsEditor, TQ_SLOT(itemsSelected(TQValueList<FileListItem*>))
             );
    connect( this, TQ_SIGNAL(setPreviousItemEnabled(bool)),
               optionsEditor, TQ_SLOT(setPreviousEnabled(bool))
             );
    connect( this, TQ_SIGNAL(setNextItemEnabled(bool)),
               optionsEditor, TQ_SLOT(setNextEnabled(bool))
             );
    connect( optionsEditor, TQ_SIGNAL(user2Clicked()),
               this, TQ_SLOT(selectPreviousItem())
             );
    connect( optionsEditor, TQ_SIGNAL(user1Clicked()),
               this, TQ_SLOT(selectNextItem())
             );
    /*connect( this, TQ_SIGNAL(moveEditor(int,int)),
               optionsEditor, TQ_SLOT(moveWindow(int,int))
             );*/
}
    itemsSelected();
    optionsEditor->show();
}

// FIXME still makes some troubles (wreaks some items) and crashes
void FileList::selectPreviousItem()
{
    if( selectedFiles.first() == 0 ) return;
    FileListItem* item = selectedFiles.first()->itemAbove();
    if( item != 0 ) {
        item->setSelected( true );
        repaintItem( item );
        ensureItemVisible( item );
    }

    for( TQValueList<FileListItem*>::Iterator it = selectedFiles.begin(); it != selectedFiles.end(); ++it ) {
        (*it)->setSelected( false );
        repaintItem( *it );
    }

    itemsSelected();
}

void FileList::selectNextItem()
{
    if( selectedFiles.last() == 0 ) return;
    FileListItem* item = selectedFiles.last()->itemBelow();
    if( item != 0 ) {
        item->setSelected( true );
        repaintItem( item );
        ensureItemVisible( item );
    }

    for( TQValueList<FileListItem*>::Iterator it = selectedFiles.begin(); it != selectedFiles.end(); ++it ) {
        (*it)->setSelected( false );
        repaintItem( *it );
    }

    itemsSelected();
}

void FileList::itemsSelected()
{
    selectedFiles.clear();
    for( FileListItem *item = firstChild(); item != NULL; item = item->nextSibling() ) {
        if( item->isSelected() ) {
            selectedFiles.append( item );
        }
    }

    if( selectedFiles.count() > 0 ) {
        if( selectedFiles.first()->itemAbove() != 0 ) emit setPreviousItemEnabled( true );
        else emit setPreviousItemEnabled( false );
        if( selectedFiles.last()->itemBelow() != 0 ) emit setNextItemEnabled( true );
        else emit setNextItemEnabled( false );
    }
    else {
        emit setPreviousItemEnabled( false );
        emit setNextItemEnabled( false );
    }

    emit editItems( selectedFiles );
}

/*void FileList::moveOptionsEditor( int x, int y )
{
    emit moveEditor( x, y );
}*/

void FileList::startConversion()
{
    // iterate through all items and set the state to "Waiting"
    FileListItem* it = firstChild();
    while( it != 0 ) {
//         it->setText( columnByName(i18n("State")), i18n("Waiting") );
        if( !it->converting && it->text(columnByName(i18n("State"))) != i18n("Will be skipped") ) {
            it->setText( columnByName(i18n("State")), i18n("Waiting") );
        }
        it = it->nextSibling();
    }
    queue = true;
    emit startedConversion();
    convertNextItem();
}

void FileList::stopConversion()
{
    queue = false;
    emit stopClicked();
}

void FileList::continueConversion()
{
    queue = true;
    emit continueClicked();
}

void FileList::killConversion()
{
    queue = false;

    FileListItem *item = firstChild();

    while( item != 0 ) {
        if( item->converting ) {
            emit stopItem( item );
        }
        item = item->nextSibling();
    }
}

void FileList::convertNextItem()
{
    if( !queue ) return;

    TQStringList devices;

    // look for tracks that are being ripped
    int count = 0;
    FileListItem *item = firstChild();
    while( item != 0 ) {
        if( item->converting ) {
            count++;
            if( item->ripping ) {
                devices += item->device;
            }
        }
        item = item->nextSibling();
    }

    // look for "Waiting" files first ...
    item = firstChild();
    while( item != 0 && count < config->data.general.numFiles ) {
        if( !item->converting && item->text(columnByName(i18n("State"))) == i18n("Waiting") ) {
            if( item->track >= 0 && devices.findIndex(item->device) == -1 ) {
                convertItem( item );
                count++;
                devices += item->device;
            }
            else if( item->track < 0 ) {
                convertItem( item );
                count++;
            }
        }
        item = item->nextSibling();
    }

    // ... then convert the stopped, too
//     item = firstChild();
//     while( item != 0 && count < config->data.general.numFiles ) {
//         if( !item->converting && item->text(columnByName(i18n("State"))) == i18n("Stopped") ) {
//             if( item->track >= 0 && devices.findIndex(item->device) == -1 ) {
//                 convertItem( item );
//                 count++;
//                 devices += item->device;
//             }
//             else if( item->track < 0 ) {
//                 convertItem( item );
//                 count++;
//             }
//         }
//         item = item->nextSibling();
//     }

    itemsSelected();

    FileListItem* it = firstChild();
    int waitingCount = 0, convertingCount = 0;

    while( it != 0 ) {
        if( it->text(columnByName(i18n("State"))) != i18n("Failed") && it->text(columnByName(i18n("State"))) != i18n("Stopped") && it->text(columnByName(i18n("State"))) != i18n("Will be skipped") ) {
            if( it->text(columnByName(i18n("State"))) == i18n("Waiting") /*|| it->text(columnByName(i18n("State"))) == i18n("Stopped")*/ ) {
                waitingCount++;
            }
            else {
                convertingCount++;
            }
        }
        it = it->nextSibling();
    }

    if( waitingCount == 0 && convertingCount == 0 ) itemFinished( 0, 0 );
}

void FileList::load( bool autosave )
{
    // NOTE clear the file list befor adding the saved items?

    int version;
    TQString name;
    FileListItem* lastListItem = lastItem();
    TQString filename;
    if( autosave ) filename = locateLocal("data","soundkonverter/filelist.autosave.xml");
    else filename = locateLocal("data","soundkonverter/filelist.xml");

    TQDomDocument domTree;
    TQFile opmlFile( filename );
    if( !opmlFile.open( IO_ReadOnly ) ) return;
    if( !domTree.setContent( &opmlFile ) ) return;
    opmlFile.close();

    TQDomElement root = domTree.documentElement();
    if( root.attribute("type") != "filelist" ) return;
    TQDomNode node, sub1Node, sub2Node;
    node = root.firstChild();

    while( !node.isNull() ) {
        if( node.isElement() && node.nodeName() == "info" ) {

            version = node.toElement().attribute("version").toInt();

        }
        else if( node.isElement() && node.nodeName() == "file" ) {

            FileListItem* item = new FileListItem( this, lastListItem );
            lastListItem = item;

            item->fileName = node.toElement().attribute("fileName");
            item->mimeType = node.toElement().attribute("mimeType");
            item->fileFormat = node.toElement().attribute("fileFormat");
            item->local = node.toElement().attribute("local").toInt();
            item->track = node.toElement().attribute("track").toInt();
            item->time = node.toElement().attribute("time").toInt();

            item->options.filePathName = node.toElement().attribute("filePathName");
            item->options.outputFilePathName = node.toElement().attribute("outputFilePathName");

            sub1Node = node.toElement().firstChild();
            while( !sub1Node.isNull() ) {
                if( sub1Node.isElement() && sub1Node.nodeName() == "encodingOptions" ) {

                    item->options.encodingOptions.sFormat = sub1Node.toElement().attribute("sFormat");
                    item->options.encodingOptions.sQualityMode = sub1Node.toElement().attribute("sQualityMode");
                    item->options.encodingOptions.iQuality = sub1Node.toElement().attribute("iQuality").toInt();
                    item->options.encodingOptions.sBitrateMode = sub1Node.toElement().attribute("sBitrateMode");
                    item->options.encodingOptions.bBitrateRange = sub1Node.toElement().attribute("bBitrateRange").toInt();
                    item->options.encodingOptions.iMinBitrate = sub1Node.toElement().attribute("iMinBitrate").toInt();
                    item->options.encodingOptions.iMaxBitrate = sub1Node.toElement().attribute("iMaxBitrate").toInt();
                    item->options.encodingOptions.sInOutFiles = sub1Node.toElement().attribute("sInOutFiles");

                    sub2Node = sub1Node.toElement().firstChild();
                    while( !sub2Node.isNull() ) {
                        if( sub2Node.isElement() && sub2Node.nodeName() == "samplingRate" ) {

                            item->options.encodingOptions.samplingRate.bEnabled = sub2Node.toElement().attribute("bEnabled").toInt();
                            item->options.encodingOptions.samplingRate.iSamplingRate = sub2Node.toElement().attribute("iSamplingRate").toInt();

                        }
                        else if( sub2Node.isElement() && sub2Node.nodeName() == "channels" ) {

                            item->options.encodingOptions.channels.bEnabled = sub2Node.toElement().attribute("bEnabled").toInt();
                            item->options.encodingOptions.channels.sChannels = sub2Node.toElement().attribute("sChannels");

                        }
                        else if( sub2Node.isElement() && sub2Node.nodeName() == "replaygain" ) {

                            item->options.encodingOptions.replaygain.bEnabled = sub2Node.toElement().attribute("bEnabled").toInt();

                        }
                        sub2Node = sub2Node.nextSibling();
                    }

                }
                else if( sub1Node.isElement() && sub1Node.nodeName() == "outputOptions" ) {

                    item->options.outputOptions.mode = (OutputDirectory::Mode)sub1Node.toElement().attribute("mode").toInt();
                    item->options.outputOptions.directory = sub1Node.toElement().attribute("directory");

                }
                else if( sub1Node.isElement() && sub1Node.nodeName() == "tags" ) {

                    item->tags = new TagData();

                    item->tags->artist = sub1Node.toElement().attribute("artist");
                    item->tags->composer = sub1Node.toElement().attribute("composer");
                    item->tags->album = sub1Node.toElement().attribute("album");
                    item->tags->title = sub1Node.toElement().attribute("title");
                    item->tags->genre = sub1Node.toElement().attribute("genre");
                    item->tags->comment = sub1Node.toElement().attribute("comment");
                    item->tags->track = sub1Node.toElement().attribute("track").toInt();
                    item->tags->disc = sub1Node.toElement().attribute("disc").toInt();
                    item->tags->year = sub1Node.toElement().attribute("year").toInt();
                    item->tags->track_gain = sub1Node.toElement().attribute("track_gain").toFloat();
                    item->tags->album_gain = sub1Node.toElement().attribute("album_gain").toFloat();
                    item->tags->length = sub1Node.toElement().attribute("length").toInt();
                    item->tags->fileSize = sub1Node.toElement().attribute("fileSize").toInt();
                    item->tags->bitrate = sub1Node.toElement().attribute("bitrate").toInt();
                    item->tags->samplingRate = sub1Node.toElement().attribute("samplingRate").toInt();

                }
                sub1Node = sub1Node.nextSibling();
            }
            updateItem( item );
            emit increaseTime( item->time );
        }
        node = node.nextSibling();
    }
    emit fileCountChanged( childCount() );
}

void FileList::save( bool autosave )
{
    // NOTE filenames should be encoded before saving - but it works fine without it (hm...)

    TQTime time;
    time.start();

    TQDomDocument domTree;
    TQDomElement root = domTree.createElement( "soundkonverter" );
    root.setAttribute( "type", "filelist" );
    domTree.appendChild( root );

    TQDomElement info = domTree.createElement( "info" );
    info.setAttribute( "version", "300" );
    root.appendChild( info );

    for( FileListItem* item = firstChild(); item != 0; item = item->nextSibling() ) {
        TQDomElement file = domTree.createElement( "file" );
        file.setAttribute( "fileName", item->fileName );
        file.setAttribute( "mimeType", item->mimeType );
        file.setAttribute( "fileFormat", item->fileFormat );
        file.setAttribute( "local", item->local );
        file.setAttribute( "track", item->track );
        file.setAttribute( "device", item->device );
        file.setAttribute( "time", item->time );

        file.setAttribute( "filePathName", item->options.filePathName );
        file.setAttribute( "outputFilePathName", item->options.outputFilePathName );

            TQDomElement encodingOptions = domTree.createElement( "encodingOptions" );

                encodingOptions.setAttribute( "sFormat", item->options.encodingOptions.sFormat );
                encodingOptions.setAttribute( "sQualityMode", item->options.encodingOptions.sQualityMode );
                encodingOptions.setAttribute( "iQuality", item->options.encodingOptions.iQuality );
                encodingOptions.setAttribute( "sBitrateMode", item->options.encodingOptions.sBitrateMode );
                encodingOptions.setAttribute( "bBitrateRange", item->options.encodingOptions.bBitrateRange );
                encodingOptions.setAttribute( "iMinBitrate", item->options.encodingOptions.iMinBitrate );
                encodingOptions.setAttribute( "iMaxBitrate", item->options.encodingOptions.iMaxBitrate );

                TQDomElement samplingRate = domTree.createElement( "samplingRate" );

                    samplingRate.setAttribute( "bEnabled", item->options.encodingOptions.samplingRate.bEnabled );
                    samplingRate.setAttribute( "iSamplingRate", item->options.encodingOptions.samplingRate.iSamplingRate );

                encodingOptions.appendChild( samplingRate );

                TQDomElement channels = domTree.createElement( "channels" );

                    channels.setAttribute( "bEnabled", item->options.encodingOptions.channels.bEnabled );
                    channels.setAttribute( "sChannels", item->options.encodingOptions.channels.sChannels );

                encodingOptions.appendChild( channels );

                TQDomElement replaygain = domTree.createElement( "replaygain" );

                    replaygain.setAttribute( "bEnabled", item->options.encodingOptions.replaygain.bEnabled );

                encodingOptions.appendChild( replaygain );

                encodingOptions.setAttribute( "sInOutFiles", item->options.encodingOptions.sInOutFiles );

            file.appendChild( encodingOptions );

            TQDomElement outputOptions = domTree.createElement( "outputOptions" );

                outputOptions.setAttribute( "mode", int(item->options.outputOptions.mode) );
                outputOptions.setAttribute( "directory", item->options.outputOptions.directory );

            file.appendChild( outputOptions );

            if( item->tags )
            {
                TQDomElement tags = domTree.createElement( "tags" );

                    tags.setAttribute( "artist", item->tags->artist );
                    tags.setAttribute( "composer", item->tags->composer );
                    tags.setAttribute( "album", item->tags->album );
                    tags.setAttribute( "title", item->tags->title );
                    tags.setAttribute( "genre", item->tags->genre );
                    tags.setAttribute( "comment", item->tags->comment );
                    tags.setAttribute( "track", item->tags->track );
                    tags.setAttribute( "disc", item->tags->disc );
                    tags.setAttribute( "year", item->tags->year );
                    tags.setAttribute( "track_gain", item->tags->track_gain );
                    tags.setAttribute( "album_gain", item->tags->album_gain );
                    tags.setAttribute( "length", item->tags->length );
                    tags.setAttribute( "fileSize", item->tags->fileSize );
                    tags.setAttribute( "bitrate", item->tags->bitrate );
                    tags.setAttribute( "samplingRate", item->tags->samplingRate );

                file.appendChild( tags );
            }

        root.appendChild( file );
    }

    TQString filename;
    if( autosave ) filename = locateLocal("data","soundkonverter/filelist.autosave.xml");
    else filename = locateLocal("data","soundkonverter/filelist.xml");

    TQFile opmlFile( filename );
    if( !opmlFile.open( IO_WriteOnly ) ) return;

    TQTextStream ts( &opmlFile );
    ts << domTree.toString();

    opmlFile.close();

    logger->log( 1000, "save file list: " + TQString::number(time.elapsed()) );
}

void FileList::debug() // NOTE DEBUG
{
    logger->log( 1000, "DEBUG begin" );

    ConversionOptions conversionOptions;

    TQStringList formats = config->allEncodableFormats();

    for( TQStringList::Iterator a = formats.begin(); a != formats.end(); ++a )
    {
        logger->log( 1000, "format: " + (*a) );
        FormatItem* formatItem = config->getFormatItem( *a );
        if( formatItem == 0 ) continue;

        for( TQValueList<ConvertPlugin*>::Iterator b = formatItem->encoders.begin(); b != formatItem->encoders.end(); ++b ) {
            logger->log( 1000, " encoder: " + (*b)->enc.bin );
            formatItem->encoder = (*b);
            if( (*b)->enc.strength.enabled ) {
                formatItem->compressionLevel = (*b)->enc.strength.default_value;
            }
            if( (*b)->enc.replaygain.enabled ) {
                formatItem->internalReplayGain = true;
            }

            if( (*b)->enc.lossless.enabled ) {
                options->setProfile( "Lossless" );
                options->setFormat( *a );
                options->setOutputDirectory( "/home/daniel/soundKonverter/test_output" );
                conversionOptions = options->getCurrentOptions();
                conversionOptions.encodingOptions.sInOutFiles = debug_params( conversionOptions, formatItem );
                options->setCurrentOptions( conversionOptions );
                addFiles( "/home/daniel/soundKonverter/test.mp3" );
            }
            if( (*b)->enc.lossy.enabled ) {
                options->setProfile( "Medium" );
                if( (*b)->enc.lossy.bitrate.cbr.enabled ) {
                    options->setFormat( *a );
                    conversionOptions = options->getCurrentOptions();
                    conversionOptions.encodingOptions.sQualityMode = i18n("Bitrate");
                    conversionOptions.encodingOptions.sBitrateMode = "cbr";
                    conversionOptions.encodingOptions.iQuality = 128;
                    conversionOptions.encodingOptions.sInOutFiles = debug_params( conversionOptions, formatItem );
                    options->setCurrentOptions( conversionOptions );
                    options->setOutputDirectory( "/home/daniel/soundKonverter/test_output" );
                    addFiles( "/home/daniel/soundKonverter/test.mp3" );
                }
                if( (*b)->enc.lossy.bitrate.abr.enabled ) {
                    options->setFormat( *a );
                    conversionOptions = options->getCurrentOptions();
                    conversionOptions.encodingOptions.sQualityMode = i18n("Bitrate");
                    conversionOptions.encodingOptions.sBitrateMode = "abr";
                    conversionOptions.encodingOptions.iQuality = 128;
                    conversionOptions.encodingOptions.sInOutFiles = debug_params( conversionOptions, formatItem );
                    options->setCurrentOptions( conversionOptions );
                    options->setOutputDirectory( "/home/daniel/soundKonverter/test_output" );
                    addFiles( "/home/daniel/soundKonverter/test.mp3" );
                    if( (*b)->enc.lossy.bitrate.abr.bitrate_range.enabled ) {
                        options->setFormat( *a );
                        conversionOptions = options->getCurrentOptions();
                        conversionOptions.encodingOptions.sQualityMode = i18n("Bitrate");
                        conversionOptions.encodingOptions.sBitrateMode = "abr";
                        conversionOptions.encodingOptions.iQuality = 128;
                        conversionOptions.encodingOptions.bBitrateRange = true;
                        conversionOptions.encodingOptions.iMinBitrate = 64;
                        conversionOptions.encodingOptions.iMaxBitrate = 192;
                        conversionOptions.encodingOptions.sInOutFiles = debug_params( conversionOptions, formatItem );
                        options->setCurrentOptions( conversionOptions );
                        options->setOutputDirectory( "/home/daniel/soundKonverter/test_output" );
                        addFiles( "/home/daniel/soundKonverter/test.mp3" );
                    }
                }
                if( (*b)->enc.lossy.quality.enabled ) {
                    options->setFormat( *a );
                    conversionOptions = options->getCurrentOptions();
                    conversionOptions.encodingOptions.sQualityMode = i18n("Quality");
                    conversionOptions.encodingOptions.iQuality = 40;
                    conversionOptions.encodingOptions.sInOutFiles = debug_params( conversionOptions, formatItem );
                    options->setCurrentOptions( conversionOptions );
                    options->setOutputDirectory( "/home/daniel/soundKonverter/test_output" );
                    addFiles( "/home/daniel/soundKonverter/test.mp3" );
                }
            }
        }

        for( TQValueList<ConvertPlugin*>::Iterator b = formatItem->decoders.begin(); b != formatItem->decoders.end(); ++b ) {
            formatItem->decoder = (*b);
        }

        for( TQValueList<ReplayGainPlugin*>::Iterator b = formatItem->replaygains.begin(); b != formatItem->replaygains.end(); ++b ) {
            formatItem->replaygain = (*b);
        }
    }

    logger->log( 1000, "DEBUG end" );
}

TQString FileList::debug_params( ConversionOptions conversionOptions, FormatItem* formatItem ) // NOTE DEBUG
{
    ConvertPlugin* plugin = formatItem->encoder;

    TQString sStrength;
    TQString sBitrate;
    TQString sQuality;
    TQString sMinBitrate;
    TQString sMaxBitrate;
    TQString sSamplingRate;

    int t_int;
    float t_float;

    TQString param = "";
    if( !plugin->enc.param.isEmpty() ) param.append( " " + plugin->enc.param );
    if( !plugin->enc.overwrite.isEmpty() ) param.append( " " + plugin->enc.overwrite );

    if( plugin->enc.strength.enabled ) {
        param.append( " " + plugin->enc.strength.param );
        int compressionLevel = formatItem->compressionLevel;

        if( plugin->enc.strength.profiles.empty() ) {
            if( plugin->enc.strength.step < 1 ) {
                if( plugin->enc.strength.range_max >= plugin->enc.strength.range_min )
                    sStrength = TQString::number( compressionLevel );
                else
                    sStrength = TQString::number( plugin->enc.strength.range_min - compressionLevel );
            }
            else {
                if( plugin->enc.strength.range_max >= plugin->enc.strength.range_min )
                    sStrength = TQString::number( int(compressionLevel) );
                else
                    sStrength = TQString::number( int(plugin->enc.strength.range_min - compressionLevel) );
            }
            if( plugin->enc.strength.separator != '.' ) sStrength.replace( TQChar('.'), plugin->enc.strength.separator );
        }
        else {
            TQStringList::Iterator it = plugin->enc.strength.profiles.at( compressionLevel );
            sStrength = *it;
        }
    }

    if( conversionOptions.encodingOptions.sQualityMode == i18n("Bitrate") ) {
        if( conversionOptions.encodingOptions.sBitrateMode == "cbr" && plugin->enc.lossy.bitrate.cbr.enabled ) {
            param.append( " " + plugin->enc.lossy.bitrate.cbr.param );
            sBitrate = TQString::number( conversionOptions.encodingOptions.iQuality );
        }
        else if( conversionOptions.encodingOptions.sBitrateMode == "abr" && plugin->enc.lossy.bitrate.abr.enabled ) {
            param.append( " " + plugin->enc.lossy.bitrate.abr.param );
            sBitrate = TQString::number( conversionOptions.encodingOptions.iQuality );
            if( conversionOptions.encodingOptions.bBitrateRange && plugin->enc.lossy.bitrate.abr.bitrate_range.enabled ) {
                param.append( " " + plugin->enc.lossy.bitrate.abr.bitrate_range.param_min );
                sMinBitrate = TQString::number( conversionOptions.encodingOptions.iMinBitrate );
                param.append( " " + plugin->enc.lossy.bitrate.abr.bitrate_range.param_max );
                sMaxBitrate = TQString::number( conversionOptions.encodingOptions.iMaxBitrate );
            }
        }
    }
    else if( conversionOptions.encodingOptions.sQualityMode == i18n("Quality") && plugin->enc.lossy.quality.enabled ) {
        param.append( " " + plugin->enc.lossy.quality.param );
        if( plugin->enc.lossy.quality.profiles.empty() ) {
            if( plugin->enc.lossy.quality.step < 1 ) {
                if( plugin->enc.lossy.quality.range_max >= plugin->enc.lossy.quality.range_min)
                    t_float = ( (float)conversionOptions.encodingOptions.iQuality * ( plugin->enc.lossy.quality.range_max - plugin->enc.lossy.quality.range_min ) / 100 ) + plugin->enc.lossy.quality.range_min;
                else
                    t_float = ( (100.0f - (float)conversionOptions.encodingOptions.iQuality) * ( plugin->enc.lossy.quality.range_min - plugin->enc.lossy.quality.range_max ) / 100 ) + plugin->enc.lossy.quality.range_max;
                //t_float -= t_float%plugin->enc.quality.step;
                //sQuality = TQString().sprintf( "%.2f", t_float );
                sQuality = TQString::number( t_float );
            }
            else {
                if( plugin->enc.lossy.quality.range_max >= plugin->enc.lossy.quality.range_min)
                    t_int = ( conversionOptions.encodingOptions.iQuality * (int)( plugin->enc.lossy.quality.range_max - plugin->enc.lossy.quality.range_min ) / 100) + (int)plugin->enc.lossy.quality.range_min;
                else
                    t_int = ( (100 - conversionOptions.encodingOptions.iQuality) * (int)( plugin->enc.lossy.quality.range_min - plugin->enc.lossy.quality.range_max ) / 100) + (int)plugin->enc.lossy.quality.range_max;
                //t_int -= t_int%plugin->enc.quality.step;
                sQuality = TQString::number( t_int );
            }
            if( plugin->enc.bin == "oggenc" ) sQuality.replace(TQChar('.'),TDEGlobal::locale()->decimalSymbol());
            else if( plugin->enc.lossy.quality.separator != '.' ) sQuality.replace(TQChar('.'),plugin->enc.lossy.quality.separator);
        }
        else {
            TQStringList::Iterator it = plugin->enc.lossy.quality.profiles.at( int(conversionOptions.encodingOptions.iQuality*plugin->enc.lossy.quality.range_max/100) );
            sQuality = *it;
        }
    }
    else if( conversionOptions.encodingOptions.sQualityMode == i18n("Lossless") && plugin->enc.lossless.enabled ) {
        param.append( " " + plugin->enc.lossless.param );
    }
    else if( conversionOptions.encodingOptions.sQualityMode == i18n("Hybrid") && plugin->enc.hybrid.enabled ) {
        param.append( " " + plugin->enc.hybrid.param );
        sBitrate = TQString::number( conversionOptions.encodingOptions.iQuality );
    }

    if( conversionOptions.encodingOptions.samplingRate.bEnabled && plugin->enc.lossy.samplingrate.enabled ) {
        param.append( " " + plugin->enc.lossy.samplingrate.param );
        if( plugin->enc.lossy.samplingrate.unit == PluginLoaderBase::Hz ) {
            sSamplingRate = TQString::number( conversionOptions.encodingOptions.samplingRate.iSamplingRate );
        }
        else {
            sSamplingRate = TQString::number( (float)conversionOptions.encodingOptions.samplingRate.iSamplingRate/1000 );
        }
    }

    if( conversionOptions.encodingOptions.channels.bEnabled ) {
        if( conversionOptions.encodingOptions.channels.sChannels == i18n("Mono") && plugin->enc.lossy.channels.mono_enabled ) {
            param.append( " " + plugin->enc.lossy.channels.mono_param );
        }
        else if( conversionOptions.encodingOptions.channels.sChannels == i18n("Stereo") && plugin->enc.lossy.channels.stereo_enabled ) {
            param.append( " " + plugin->enc.lossy.channels.stereo_param );
        }
        else if( conversionOptions.encodingOptions.channels.sChannels == i18n("Joint-Stereo") && plugin->enc.lossy.channels.joint_stereo_enabled ) {
            param.append( " " + plugin->enc.lossy.channels.joint_stereo_param );
        }
        else if( conversionOptions.encodingOptions.channels.sChannels == i18n("Forced Joint-Stereo") && plugin->enc.lossy.channels.forced_joint_stereo_enabled ) {
            param.append( " " + plugin->enc.lossy.channels.forced_joint_stereo_param );
        }
        else if( conversionOptions.encodingOptions.channels.sChannels == i18n("Dual Channels") && plugin->enc.lossy.channels.dual_channels_enabled ) {
            param.append( " " + plugin->enc.lossy.channels.dual_channels_param );
        }
    }

    if( conversionOptions.encodingOptions.replaygain.bEnabled && plugin->enc.replaygain.enabled && !plugin->enc.replaygain.use.isEmpty() && formatItem->internalReplayGain ) {
        param.append( " " + plugin->enc.replaygain.use );
    }
    else if( plugin->enc.replaygain.enabled && !plugin->enc.replaygain.avoid.isEmpty() ) {
        param.append( " " + plugin->enc.replaygain.avoid );
    }

//     if( !tagEngine->canWrite(conversionOptions.encodingOptions.sFormat) && item->fileListItem->tags && plugin->enc.tag.enabled ) {
    if( plugin->enc.tag.enabled && conversionOptions.encodingOptions.sFormat != "aac" ) { // HACK don't write metadata to aac
        if( !plugin->enc.tag.param.isEmpty() ) param.append( " " + plugin->enc.tag.param );
        if( !plugin->enc.tag.artist.isEmpty() ) param.append( " " + plugin->enc.tag.artist );
        if( !plugin->enc.tag.album.isEmpty() ) param.append( " " + plugin->enc.tag.album );
        if( !plugin->enc.tag.comment.isEmpty() ) param.append( " " + plugin->enc.tag.comment );
        if( !plugin->enc.tag.disc.isEmpty() ) param.append( " " + plugin->enc.tag.disc );
        if( !plugin->enc.tag.genre.isEmpty() ) param.append( " " + plugin->enc.tag.genre );
        if( !plugin->enc.tag.track.isEmpty() ) param.append( " " + plugin->enc.tag.track );
        if( !plugin->enc.tag.composer.isEmpty() ) param.append( " " + plugin->enc.tag.composer );
        if( !plugin->enc.tag.title.isEmpty() ) param.append( " " + plugin->enc.tag.title );
        if( !plugin->enc.tag.year.isEmpty() ) param.append( " " + plugin->enc.tag.year );
    }

    param.replace( "%c", sStrength );
    param.replace( "%b", sBitrate );
    param.replace( "%q", sQuality );
    param.replace( "%m", sMinBitrate );
    param.replace( "%M", sMaxBitrate );
    param.replace( "%s", sSamplingRate );

    return conversionOptions.encodingOptions.sInOutFiles.replace( "%p", param );
}

#include "filelist.moc"
