/// (c) Pierpaolo Di Panfilo 2004
// (c) Alexandre Pereira de Oliveira 2005
// (c) 2005 Isaiah Damron <xepo@trifault.net>
// (c) 2006 Peter C. Ndikuwera <pndiku@gmail.com>
// See COPYING file for licensing information

#define DEBUG_PREFIX "SmartPlaylistEditor"

#include "amarok.h" //foreach
#include "debug.h"
#include "collectiondb.h"
#include "metabundle.h"
#include "mountpointmanager.h"
#include "smartplaylisteditor.h"

#include <kcombobox.h>
#include <klineedit.h>
#include <tdelocale.h>
#include <kmountpoint.h>
#include <knuminput.h>

#include <tqcheckbox.h>
#include <tqdatetime.h>
#include <tqdatetimeedit.h>    //loadEditWidgets()
#include <tqframe.h>
#include <tqhbox.h>
#include <tqlabel.h>
#include <tqlayout.h>
#include <tqobjectlist.h>
#include <tqstringlist.h>
#include <tqtoolbutton.h>
#include <tqvbox.h>
#include <tqvgroupbox.h>

enum Fields
{
    FArtist = 0,
    FComposer,
    FAlbum,
    FGenre,
    FTitle,
    FLength,
    FTrack,
    FYear,
    FComment,
    FPlayCounter,
    FScore,
    FRating,
    FFirstPlay,
    FLastPlay,
    FModfiedDate,
    FFilePath,
    FBPM,
    FMountPoint,
    FBitRate,
    FLabel
};


TQStringList m_fields;
TQStringList m_dbFields;
TQStringList m_expandableFields;
TQStringList m_expandableDbFields;



SmartPlaylistEditor::SmartPlaylistEditor( TQString defaultName, TQWidget *parent, const char *name )
    : KDialogBase( parent, name, true, i18n("Create Smart Playlist"),
      Ok|Cancel, Ok, true )
{
    init(defaultName);
    addCriteriaAny();
    addCriteriaAll();
}


SmartPlaylistEditor::SmartPlaylistEditor( TQWidget *parent, TQDomElement xml, const char *name)
    : KDialogBase( parent, name, true, i18n("Edit Smart Playlist"),
      Ok|Cancel, Ok, true )
{
    init( xml.attribute( "name" ) );
    // matches
    TQDomNodeList matchesList =  xml.elementsByTagName( "matches" );
    bool matchedANY = false, matchedALL = false;

    m_matchAllCheck->setChecked( true );
    m_matchAnyCheck->setChecked( true );

    for (int i = 0, m = matchesList.count(); i<m; i++) {
        TQDomElement matches = matchesList.item(i).toElement();
        TQDomNodeList criteriaList =  matches.elementsByTagName( "criteria" );

        if ( criteriaList.count() ) {
            for (int j = 0, c=criteriaList.count() ; j<c; ++j ) {
                TQDomElement criteria = criteriaList.item(j).toElement();

                if (matches.attribute( "glue" ) == "OR") {
                    matchedANY = true;
                    addCriteriaAny( criteria );
                }
                else {
                    matchedALL = true;
                    addCriteriaAll( criteria );
                }
            }
        }
    }

    if ( !matchedALL ) {
        addCriteriaAll();
        m_matchAllCheck->setChecked( false );
    }

    if ( !matchedANY ) {
        m_matchAnyCheck->setChecked( false );
        addCriteriaAny( );
    }

    // orderby
    TQDomNodeList orderbyList =  xml.elementsByTagName( "orderby" );
    if ( orderbyList.count() ) {
        m_orderCheck->setChecked( true );
        TQDomElement orderby = orderbyList.item(0).toElement(); // we only allow one orderby node

        //random is always the last one.
        int dbfield = orderby.attribute( "field" ) == "random" ? m_dbFields.count() : m_dbFields.findIndex( orderby.attribute( "field" ) );

        m_orderCombo->setCurrentItem( dbfield );
        updateOrderTypes( dbfield );
        if ( orderby.attribute( "order" ) == "DESC" || orderby.attribute( "order" ) == "weighted" )
            m_orderTypeCombo->setCurrentItem( 1 );
        else if ( orderby.attribute( "order" ) == "ratingweighted" )
            m_orderTypeCombo->setCurrentItem( 2 );
        else
            m_orderTypeCombo->setCurrentItem( 0 );
    }
    // limit
    if  ( xml.hasAttribute( "maxresults" ) ) {
        m_limitCheck->setChecked( true );
        m_limitSpin->setValue( xml.attribute( "maxresults" ).toInt() );
    }

    // expand by
    TQDomNodeList expandbyList =  xml.elementsByTagName( "expandby" );
    if ( expandbyList.count() ) {
        m_expandCheck->setChecked( true );
        TQDomElement expandby = expandbyList.item(0).toElement(); // we only allow one orderby node

        int dbfield = m_expandableFields.findIndex( expandby.attribute( "field" ) );
        m_expandCombo->setCurrentItem( dbfield );
    }
}


void SmartPlaylistEditor::init(TQString defaultName)
{
    makeVBoxMainWidget();

    m_fields.clear();
    m_fields << i18n("Artist") << i18n("Composer") << i18n("Album") << i18n("Genre") << i18n("Title") << i18n("Length")
             << i18n("Track #") << i18n("Year") << i18n("Comment") << i18n("Play Counter")
             << i18n("Score") << i18n( "Rating" ) << i18n("First Play")
             << i18n("Last Play") << i18n("Modified Date") << i18n("File Path")
             << i18n("BPM") << i18n("Mount Point") << i18n( "Bitrate" ) << i18n( "Label" );

    m_dbFields.clear();
    m_dbFields << "artist.name" << "composer.name" << "album.name" << "genre.name" << "tags.title" << "tags.length"
               << "tags.track" << "year.name" << "tags.comment" << "statistics.playcounter"
               << "statistics.percentage" << "statistics.rating" << "statistics.createdate"
               << "statistics.accessdate" << "tags.createdate" << "tags.url"
               << "tags.bpm" << "devices.lastmountpoint" << "tags.bitrate" << "labels.name";

    m_expandableFields.clear();
    m_expandableFields << i18n("Artist") << i18n("Composer") << i18n("Album") << i18n("Genre") <<  i18n("Year") << i18n( "Label" );

    m_expandableDbFields.clear();
    m_expandableDbFields << "artist.name" << "composer.name" << "album.name" << "genre.name" << "year.name" << "labels.name";

    TQHBox *hbox = new TQHBox( mainWidget() );
    hbox->setSpacing( 5 );
    new TQLabel( i18n("Playlist name:"), hbox );
    m_nameLineEdit = new KLineEdit( defaultName, hbox );

    TQFrame *sep = new TQFrame( mainWidget() );
    sep->setFrameStyle( TQFrame::HLine | TQFrame::Sunken );

    //match box (any)
    TQHBox *matchAnyBox = new TQHBox( mainWidget() );
    m_matchAnyCheck = new TQCheckBox( i18n("Match Any of the following conditions" ), matchAnyBox );
    matchAnyBox->setStretchFactor( new TQWidget( matchAnyBox ), 1 );

    //criteria box
    m_criteriaAnyGroupBox = new TQVGroupBox( TQString(), mainWidget() );

    //match box (all)
    TQHBox *matchAllBox = new TQHBox( mainWidget() );
    m_matchAllCheck = new TQCheckBox( i18n("Match All of the following conditions" ), matchAllBox );
    matchAllBox->setStretchFactor( new TQWidget( matchAllBox ), 1 );

    //criteria box
    m_criteriaAllGroupBox = new TQVGroupBox( TQString(), mainWidget() );

    //order box
    TQHBox *hbox2 = new TQHBox( mainWidget() );
    m_orderCheck = new TQCheckBox( i18n("Order by"), hbox2 );
    TQHBox *orderBox = new TQHBox( hbox2 );
    orderBox->setSpacing( 5 );
    //fields combo
    m_orderCombo = new KComboBox( orderBox );
    m_orderCombo->insertStringList( m_fields );
    m_orderCombo->insertItem( i18n("Random") );
    //order type
    m_orderTypeCombo = new KComboBox( orderBox );
    updateOrderTypes(0); // populate the new m_orderTypeCombo
    hbox2->setStretchFactor( new TQWidget( hbox2 ), 1 );

    //limit box
    TQHBox *hbox1 = new TQHBox( mainWidget() );
    m_limitCheck = new TQCheckBox( i18n("Limit to"), hbox1 );
    TQHBox *limitBox = new TQHBox( hbox1 );
    limitBox->setSpacing( 5 );
    m_limitSpin = new KIntSpinBox( limitBox );
    m_limitSpin->setMinValue( 1 );
    m_limitSpin->setMaxValue( 100000 );
    m_limitSpin->setValue( 15 );
    new TQLabel( i18n("tracks"), limitBox );
    hbox1->setStretchFactor( new TQWidget( hbox1 ), 1 );

    //Expand By
    TQHBox *hbox3 = new TQHBox( mainWidget() );
    m_expandCheck = new TQCheckBox( i18n("Expand by"), hbox3 );
    TQHBox *expandBox = new TQHBox( hbox3 );
    expandBox->setSpacing( 5 );
    m_expandCombo = new KComboBox( expandBox );
    m_expandCombo->insertStringList( m_expandableFields );
    hbox3->setStretchFactor( new TQWidget( hbox3 ), 1 );

    //add stretch
    static_cast<TQHBox *>(mainWidget())->setStretchFactor(new TQWidget(mainWidget()), 1);

    connect( m_matchAnyCheck, TQ_SIGNAL( toggled(bool) ), m_criteriaAnyGroupBox, TQ_SLOT( setEnabled(bool) ) );
    connect( m_matchAllCheck, TQ_SIGNAL( toggled(bool) ), m_criteriaAllGroupBox, TQ_SLOT( setEnabled(bool) ) );
    connect( m_orderCheck, TQ_SIGNAL( toggled(bool) ), orderBox, TQ_SLOT( setEnabled(bool) ) );
    connect( m_limitCheck, TQ_SIGNAL( toggled(bool) ), limitBox, TQ_SLOT(  setEnabled(bool) ) );
    connect( m_expandCheck, TQ_SIGNAL( toggled(bool) ), expandBox, TQ_SLOT( setEnabled(bool) ) );
    connect( m_orderCombo, TQ_SIGNAL( activated(int) ), this, TQ_SLOT( updateOrderTypes(int) ) );

    m_criteriaAnyGroupBox->setEnabled( false );
    m_criteriaAllGroupBox->setEnabled( false );

    orderBox->setEnabled( false );
    limitBox->setEnabled( false );
    expandBox->setEnabled( false );

    m_nameLineEdit->setFocus();

    resize( 550, 200 );
}


void SmartPlaylistEditor::addCriteriaAny()
{
    CriteriaEditor *criteria= new CriteriaEditor( this, m_criteriaAnyGroupBox, criteriaAny );
    m_criteriaEditorAnyList.append( criteria );
    m_criteriaEditorAnyList.first()->enableRemove( m_criteriaEditorAnyList.count() > 1 );
}

void SmartPlaylistEditor::addCriteriaAll()
{
    CriteriaEditor *criteria= new CriteriaEditor( this, m_criteriaAllGroupBox, criteriaAll );
    m_criteriaEditorAllList.append( criteria );
    m_criteriaEditorAllList.first()->enableRemove( m_criteriaEditorAllList.count() > 1 );
}

void SmartPlaylistEditor::addCriteriaAny( TQDomElement &xml )
{
    CriteriaEditor *criteria = new CriteriaEditor( this, m_criteriaAnyGroupBox, criteriaAny, xml );
    m_criteriaEditorAnyList.append( criteria );
    m_criteriaEditorAnyList.first()->enableRemove( m_criteriaEditorAnyList.count() > 1 );
}

void SmartPlaylistEditor::addCriteriaAll( TQDomElement &xml )
{
    CriteriaEditor *criteria = new CriteriaEditor( this, m_criteriaAllGroupBox, criteriaAll, xml );
    m_criteriaEditorAllList.append( criteria );
    m_criteriaEditorAllList.first()->enableRemove( m_criteriaEditorAllList.count() > 1 );
}

void SmartPlaylistEditor::removeCriteriaAny( CriteriaEditor *criteria )
{
    m_criteriaEditorAnyList.remove( criteria );
    criteria->deleteLater();
    resize( size().width(), sizeHint().height() );

    if( m_criteriaEditorAnyList.count() == 1 )
	m_criteriaEditorAnyList.first()->enableRemove( false );
}

void SmartPlaylistEditor::removeCriteriaAll( CriteriaEditor *criteria )
{
    m_criteriaEditorAllList.remove( criteria );
    criteria->deleteLater();
    resize( size().width(), sizeHint().height() );

    if( m_criteriaEditorAllList.count() == 1 )
	m_criteriaEditorAllList.first()->enableRemove( false );
}

void SmartPlaylistEditor::updateOrderTypes( int index )
{
    int currentOrderType = m_orderTypeCombo->currentItem();
    if( index == m_orderCombo->count()-1 ) {  // random order selected
        m_orderTypeCombo->clear();
        m_orderTypeCombo->insertItem( i18n("Completely Random") );
        m_orderTypeCombo->insertItem( i18n("Score Weighted") );
        m_orderTypeCombo->insertItem( i18n("Rating Weighted") );
    }
    else {  // ordinary order column selected
        m_orderTypeCombo->clear();
        m_orderTypeCombo->insertItem( i18n("Ascending") );
        m_orderTypeCombo->insertItem( i18n("Descending") );
    }
    if( currentOrderType < m_orderTypeCombo->count() )
        m_orderTypeCombo->setCurrentItem( currentOrderType );
    m_orderTypeCombo->setFont(m_orderTypeCombo->font());  // invalidate size hint
    m_orderTypeCombo->updateGeometry();
}

TQDomElement SmartPlaylistEditor::result()
{
    TQDomDocument doc;
    TQDomNode node = doc.namedItem( "smartplaylists" );
    TQDomElement nodeE;
    nodeE = node.toElement();

    TQDomElement smartplaylist = doc.createElement( "smartplaylist" );

    smartplaylist.setAttribute( "name", name() );

    // Limit
    if ( m_limitCheck->isChecked() )
        smartplaylist.setAttribute( "maxresults", m_limitSpin->value() );

    nodeE.appendChild( smartplaylist );
    // Matches
    if( m_matchAnyCheck->isChecked() )
    {
        TQDomElement matches = doc.createElement("matches");
        smartplaylist.appendChild( matches );
        // Iterate through all criteria list
        CriteriaEditor *criteriaeditor = m_criteriaEditorAnyList.first();
        for( int i=0; criteriaeditor; criteriaeditor = m_criteriaEditorAnyList.next(), ++i )
            matches.appendChild( doc.importNode( criteriaeditor->getDomSearchCriteria( doc ), true ) );

        matches.setAttribute( "glue",  "OR" );
        smartplaylist.appendChild( matches );
    }

    if( m_matchAllCheck->isChecked() )
    {
        TQDomElement matches = doc.createElement("matches");
        smartplaylist.appendChild( matches );
        // Iterate through all criteria list
        CriteriaEditor *criteriaeditor = m_criteriaEditorAllList.first();
        for( int i=0; criteriaeditor; criteriaeditor = m_criteriaEditorAllList.next(), ++i )
            matches.appendChild( doc.importNode( criteriaeditor->getDomSearchCriteria( doc ), true ) );

        matches.setAttribute( "glue",  "AND" );
        smartplaylist.appendChild( matches );
    }

    // Order By
    if( m_orderCheck->isChecked() ) {
        TQDomElement orderby = doc.createElement("orderby");
        if (m_orderCombo->currentItem() != m_orderCombo->count()-1) {
            orderby.setAttribute( "field", m_dbFields[ m_orderCombo->currentItem() ] );
            orderby.setAttribute( "order", m_orderTypeCombo->currentItem() == 1 ? "DESC" : "ASC" );
        } else {
            orderby.setAttribute( "field", "random" );
            TQString order;
            if ( m_orderTypeCombo->currentItem() == 0 )
                order = "random";
            else if ( m_orderTypeCombo->currentItem() == 1 )
                order = "weighted";
            else
                order = "ratingweighted";
            orderby.setAttribute( "order", order );
        }

        smartplaylist.appendChild( orderby );
    }

    if( m_expandCheck->isChecked() ) {
        TQDomElement expandBy = doc.createElement("expandby");
        expandBy.setAttribute( "field", m_expandableFields[ m_expandCombo->currentItem() ] );
        smartplaylist.appendChild( expandBy );
    }

    return (smartplaylist);
}

/////////////////////////////////////////////////////////////////////////////
//    CLASS CriteriaEditor
////////////////////////////////////////////////////////////////////////////

CriteriaEditor::CriteriaEditor( SmartPlaylistEditor *editor, TQWidget *parent, int criteriaType, TQDomElement criteria )
    : TQHBox( parent )
    , m_playlistEditor( editor )
    , m_currentValueType( -1 )
{
    setSpacing( 5 );

    m_fieldCombo = new KComboBox( this );
    m_fieldCombo->insertStringList( m_fields );

    m_criteriaCombo = new KComboBox( this );

    m_editBox = new TQHBox( this );
    m_editBox->setSpacing( 5 );
    setStretchFactor( m_editBox, 1 );

    m_addButton = new TQToolButton( this );
    m_addButton->setUsesTextLabel( true );
    m_addButton->setTextLabel("+");
    m_removeButton = new TQToolButton( this );
    m_removeButton->setUsesTextLabel( true );
    m_removeButton->setTextLabel("-");

    connect( m_fieldCombo,    TQ_SIGNAL( activated(int) ), TQ_SLOT( slotFieldSelected(int) ) );
    connect( m_criteriaCombo, TQ_SIGNAL( activated(int) ), TQ_SLOT( loadEditWidgets() ) );
    if (criteriaType == SmartPlaylistEditor::criteriaAny) {
	connect( m_addButton, TQ_SIGNAL( clicked() ), editor, TQ_SLOT( addCriteriaAny() ) );
	connect( m_removeButton, TQ_SIGNAL( clicked() ), TQ_SLOT( slotRemoveCriteriaAny() ) );
    }
    else {
	connect( m_addButton, TQ_SIGNAL( clicked() ), editor, TQ_SLOT( addCriteriaAll() ) );
	connect( m_removeButton, TQ_SIGNAL( clicked() ), TQ_SLOT( slotRemoveCriteriaAll() ) );
    }

    if ( !criteria.isNull() ) {
        int field = m_dbFields.findIndex( criteria.attribute( "field" ) );
        TQString condition = criteria.attribute("condition");


        TQStringList values; //List of the values (only one item, unless condition is "is between")
        TQDomNodeList domvalueList = criteria.elementsByTagName( "value" );
        for (int j = 0, c=domvalueList.count() ; j<c; ++j ) {
                values << domvalueList.item(j).toElement().text();
        }

        //Set the selected field

        m_fieldCombo->setCurrentItem( field );
        slotFieldSelected( field );
        int valueType = getValueType( field );
        //Load the right set of criterias for this type, in the dialog
        loadCriteriaList( valueType, condition );

        loadEditWidgets();

        switch( valueType ) {
            case String: //fall through
            case AutoCompletionString:
            {
                m_lineEdit->setText( values.first() );
                break;
            }
            case Year:    //fall through
            case Number:
            {
                m_intSpinBox1->setValue( values.first().toInt() );
                if( condition == i18n("is between") )
                    m_intSpinBox2->setValue( values.last().toInt() );
                break;
            }
            case Rating:
            {
                m_comboBox->setCurrentItem( ratingToIndex( values.first().toInt() ) );
                if( condition == i18n("is between") )
                    m_comboBox2->setCurrentItem( ratingToIndex( values.last().toInt() ) );
                break;
            }
            case Date:
            {
                if( condition == i18n("is in the last") || condition == i18n("is not in the last") ) {
                    m_intSpinBox1->setValue( values.first().toInt() );
                    TQString period = criteria.attribute("period");
                    if (period=="days" || period.isEmpty() )
                        m_dateCombo->setCurrentItem(0);
                    else if (period=="months")
                        m_dateCombo->setCurrentItem(1);
                    else
                        m_dateCombo->setCurrentItem(2);
                }
                else {
                    TQDateTime dt;
                    dt.setTime_t( values.first().toUInt() );
                    m_dateEdit1->setDate( dt.date() );
                    if( condition == i18n("is between") ) {
                        dt.setTime_t( values.last().toUInt() );
                        m_dateEdit2->setDate( dt.date() );
                    }
                }
                break;
            }
            case Length:
            {
                m_intSpinBox1->setValue( values.first().toInt() );
                if( condition == i18n("is between") )
                    m_intSpinBox2->setValue( values.last().toInt() );
                TQString period = criteria.attribute( "period" );
                if ( period == "seconds" || period.isEmpty() ) //for compatibility
                    m_lengthCombo->setCurrentItem( 0 );
                else if ( period == "minutes" )
                    m_lengthCombo->setCurrentItem( 1 );
                else
                    m_lengthCombo->setCurrentItem( 2 );
                break;
            }
            default: ;
        };
    }
    else
        slotFieldSelected( 0 );
    show();
}


CriteriaEditor::~CriteriaEditor()
{
}

TQDomElement CriteriaEditor::getDomSearchCriteria( TQDomDocument &doc )
{
    TQDomElement criteria = doc.createElement( "criteria" );
    TQString field = m_dbFields[ m_fieldCombo->currentItem() ];
    TQString condition = m_criteriaCombo->currentText();

    criteria.setAttribute( "condition", condition );
    criteria.setAttribute( "field", field );

    TQStringList values;
    // Get the proper value(s)
    switch( getValueType( m_fieldCombo->currentItem() ) ) {
         case String: // fall through
         case AutoCompletionString:
            values << m_lineEdit->text();
            break;
         case Year: // fall through
         case Number:
         {
            values << TQString::number( m_intSpinBox1->value() );
            if( condition == i18n("is between")  )
                values << TQString::number( m_intSpinBox2->value() );
            break;
         }
         case Rating:
         {
            values << TQString::number( indexToRating( m_comboBox->currentItem() ) );
            if( condition == i18n("is between")  )
                    values << TQString::number( indexToRating( m_comboBox2->currentItem() ) );
            break;
         }
         case Date:
         {
            if( condition == i18n("is in the last") || condition == i18n("is not in the last") ) {
                values << TQString::number( m_intSpinBox1->value() );
                // 0 = days; 1=months; 2=years
                criteria.setAttribute( "period", !m_dateCombo->currentItem() ? "days" : (m_dateCombo->currentItem() == 1 ? "months" : "years") );
            }
            else {
                values << TQString::number( TQDateTime( m_dateEdit1->date() ).toTime_t() );
                if( condition == i18n("is between")  ) {
                    values << TQString::number( TQDateTime( m_dateEdit2->date() ).toTime_t() );
               }
            }
            break;
         }
         case Length:
         {
            values << TQString::number( m_intSpinBox1->value() );
            // 0 = seconds, 1=minutes, 2=hours
            criteria.setAttribute( "period", !m_lengthCombo->currentItem() ? "seconds" : (m_lengthCombo->currentItem() == 1 ? "minutes" : "hours") );
            if( condition == i18n( "is between" ) ) {
                values << TQString::number( m_intSpinBox2->value() );
            }
            break;
         }
         default: ;
    }
    foreach( values ) {
        TQDomElement value = doc.createElement( "value" );
        TQDomText t = doc.createTextNode( *it );
        value.appendChild( t );
        criteria.appendChild( value );
    }
    return (criteria);
}


TQString CriteriaEditor::getSearchCriteria()
{
    TQString searchCriteria;
    TQString field = m_dbFields[ m_fieldCombo->currentItem() ];
    TQString criteria = m_criteriaCombo->currentText();

    if( field.isEmpty() )
        return TQString();

    if ( ( field=="statistics.playcounter" || field=="statistics.rating" || field=="statistics.percentage" || field=="statistics.accessdate" || field=="statistics.createdate") )
        searchCriteria += "COALESCE(" + field + ",0)";
    else
        searchCriteria += field;

    TQString value;
    switch( getValueType( m_fieldCombo->currentItem() ) ) {
        case String:
        case AutoCompletionString:
            value = m_lineEdit->text();
            break;
        case Year:    //fall through
        case Number:
            value = TQString::number( m_intSpinBox1->value() );
            if( criteria == i18n("is between")  )
                value += " AND " + TQString::number( m_intSpinBox2->value() );
            break;
        case Rating:
        {
            value = TQString::number( indexToRating( m_comboBox->currentItem() ) );
            if( criteria == i18n("is between")  )
                value += " AND " + TQString::number( indexToRating( m_comboBox2->currentItem() ) );
            break;
        }
        case Date:
        {
            if( criteria == i18n("is in the last") || criteria == i18n("is not in the last") ) {
                int n = m_intSpinBox1->value();
                int time;
                if( m_dateCombo->currentItem() == 0 ) //days
                    time=86400*n;
                else if( m_dateCombo->currentItem() == 1 ) //months
                    time=86400*30*n;
                else time=86400*365*n; //years
                value += "(*CurrentTimeT*)" + TQString(" - %1 AND ").arg(time) + "(*CurrentTimeT*)";
            }
            else {
                TQDateTime datetime1( m_dateEdit1->date() );
                value += TQString::number( datetime1.toTime_t() );
                if( criteria == i18n("is between")  ) {
                    TQDateTime datetime2( m_dateEdit2->date() );
                    value += " AND " + TQString::number( datetime2.toTime_t() );
                }
                else
                    value += " AND " + TQString::number( datetime1.addDays( 1 ).toTime_t() );
            }
            break;
        }
        case Length:
        {
            int n = m_intSpinBox1->value();
            int time;
            if( m_lengthCombo->currentItem() == 0 ) //seconds
                time = n;
            else if( m_lengthCombo->currentItem() == 1 ) //minutes
                time = 60*n;
            else
                time = 3600*n; //hours
            value = TQString::number( time );
            if( criteria == i18n("is between")  ) {
                int n2 = m_intSpinBox2->value();
                int time2;
                if( m_lengthCombo->currentItem() == 0 ) //seconds
                    time2 = n2;
                else if( m_lengthCombo->currentItem() == 1 ) //minutes
                    time2 = 60*n2;
                else
                    time2 = 3600*n2; //hours
                value += " AND " + TQString::number( time2 );
            }
            break;
        }
        default: ;
    };


    if( criteria == i18n("contains") )
        searchCriteria += CollectionDB::likeCondition( value, true, true );
    else if( criteria == i18n("does not contain") )
        searchCriteria += " NOT " + CollectionDB::likeCondition( value, true, true );
    else if( criteria == i18n("is") ) {
        if( m_currentValueType == Date )
            searchCriteria += " BETWEEN ";
        else
            searchCriteria += " = ";
        if( m_currentValueType == String || m_currentValueType == AutoCompletionString )
            value.prepend("'").append("'");
        searchCriteria += value;
    }
    else if( criteria == i18n("is not") ) {
        if( m_currentValueType == Date )
            searchCriteria += " NOT BETWEEN ";
        else
            searchCriteria += " <> ";
        if( m_currentValueType == String || m_currentValueType == AutoCompletionString )
            value.prepend("'").append("'");
        searchCriteria += value;
    }
    else if( criteria == i18n("starts with") )
    {
        if( field == "tags.url" )
        {
            if( value.startsWith( "/" ) )
                value = '.' + value;
            if( !value.startsWith( "./" ) )
                value = "./" + value;
        }
        searchCriteria += CollectionDB::likeCondition( value, false, true );
    }
    else if( criteria == i18n("does not start with") )
    {
        if( field == "tags.url" )
        {
            if( value.startsWith( "/" ) )
                value = '.' + value;
            if( !value.startsWith( "./" ) )
                value = "./" + value;
        }
        searchCriteria += " NOT " + CollectionDB::likeCondition( value, false, true );
    }
    else if( criteria == i18n("ends with") )
        searchCriteria += CollectionDB::likeCondition( value, true, false );
    else if( criteria == i18n("does not end with") )
        searchCriteria += " NOT " + CollectionDB::likeCondition( value, true, false );
    else if( criteria == i18n("is greater than") || criteria == i18n("is after") )
        searchCriteria += " > " + value;
    else if( criteria == i18n("is smaller than") || criteria == i18n("is before" ) )
        searchCriteria += " < " + value;
    else if( criteria == i18n("is between") || criteria == i18n("is in the last") )
        searchCriteria += " BETWEEN " + value;
    else if( criteria == i18n("is not in the last") )
        searchCriteria += " NOT BETWEEN " + value;

    return searchCriteria;
}


void CriteriaEditor::setSearchCriteria( const TQString & )
{
    //TODO
}


void CriteriaEditor::enableRemove( bool enable )
{
    m_removeButton->setEnabled( enable );
}


void CriteriaEditor::slotRemoveCriteriaAny()
{
    m_playlistEditor->removeCriteriaAny( this );
}

void CriteriaEditor::slotRemoveCriteriaAll()
{
    m_playlistEditor->removeCriteriaAll( this );
}

void CriteriaEditor::slotAddCriteriaAny()
{
    m_playlistEditor->addCriteriaAny();
}

void CriteriaEditor::slotAddCriteriaAll()
{
    m_playlistEditor->addCriteriaAll();
}

void CriteriaEditor::slotFieldSelected( int field )
{
    int valueType = getValueType( field );
    loadCriteriaList( valueType );
    loadEditWidgets();
    m_currentValueType = valueType;

    //enable auto-completion for artist, album, composer, label, mountpoint and genre
    if( valueType == AutoCompletionString ) {
        TQStringList items;
        m_comboBox->clear();
        m_comboBox->completionObject()->clear();

        int currentField = m_fieldCombo->currentItem();
        if( currentField == FArtist ) //artist
           items = CollectionDB::instance()->artistList();
        else if( currentField == FComposer ) //composer
           items = CollectionDB::instance()->composerList();
        else if( currentField == FAlbum ) //album
           items = CollectionDB::instance()->albumList();
        else if (currentField == FLabel ) //label
            items = CollectionDB::instance()->labelList();
        else if (currentField == FMountPoint ) //mount point
        {
            KMountPoint::List mountpoints = KMountPoint::currentMountPoints( KMountPoint::NeedRealDeviceName );
            foreachType( KMountPoint::List, mountpoints )
            {
                /* This code is adapted from KDE mediamanager's fstabbackend.cpp
                 * Copyright Kévin Ottens, Bernhard Rosenkraenzer, and from looking
                 * at the commit messages a few other guys who didn't add their name to the header.
                 */
                TQString device = (*it)->realDeviceName();
                TQString fs = (*it)->mountType();
                TQString mountpoint = (*it)->mountPoint();
                if ( fs != "swap"
                     && fs != "tmpfs"
                     && fs != "sysfs"
                     && fs != "fdescfs"
                     && fs != "kernfs"
                     && fs != "usbfs"
                     && !fs.contains( "proc" )
                     && fs != "unknown"
                     && fs != "none"
                     && fs != "sunrpc"
                     && fs != "none"
                     && device != "tmpfs"
                     && device.find("shm") == -1
                     && mountpoint != "/dev/swap"
                     && mountpoint != "/dev/pts"
                     && mountpoint.find("/proc") != 0
                     && mountpoint.find("/sys") != 0
                     || fs.find( "smb" ) != -1
                     || fs.find( "cifs" ) != -1
                     || fs.find( "nfs" ) != -1
                )
                    items << mountpoint;
            }
        }
        else  //genre
           items = CollectionDB::instance()->genreList();

        m_comboBox->insertStringList( items );
        m_comboBox->completionObject()->insertItems( items );
        m_comboBox->completionObject()->setIgnoreCase( true );
        m_comboBox->setCurrentText( "" );
        m_comboBox->setFocus();
    }
}


void CriteriaEditor::loadEditWidgets()
{
    int valueType = getValueType( m_fieldCombo->currentItem() );

    if( m_currentValueType == valueType && !(
        m_criteriaCombo->currentText() == i18n( "is between" ) ||
        m_criteriaCombo->currentText() == i18n( "is in the last" ) ||
        m_criteriaCombo->currentText() == i18n( "is not in the last" ) ||
        m_lastCriteria == i18n( "is between" ) ||
        m_lastCriteria == i18n( "is in the last" ) ||
        m_lastCriteria == i18n( "is not in the last" ) ) )
        return;

    /* Store lastCriteria. This information is used above to decide whether it's necessary to change the Widgets */
    m_lastCriteria = m_criteriaCombo->currentText();

    TQObjectList* list = m_editBox->queryList( "TQWidget" );
    for( TQObject *obj = list->first(); obj; obj = list->next()  )
        obj->deleteLater();

    delete list;

    switch( valueType ) {

        case String:
        {
            m_lineEdit = new KLineEdit( m_editBox );
            m_lineEdit->setFocus();
            m_lineEdit->show();
            break;
        }

        case AutoCompletionString:    //artist, composer, album, genre, label
        {
            m_comboBox = new KComboBox( true, m_editBox );
            m_lineEdit = static_cast<KLineEdit*>( m_comboBox->lineEdit() );
            m_lineEdit->setFocus();
            m_comboBox->setMinimumSize( TQSize( 240, 20 ) );
            m_comboBox->show();
            break;
        }

        case Year:    //fall through
        case Number:
        {
            bool yearField = m_fieldCombo->currentText() == i18n("Year");

            m_intSpinBox1 = new KIntSpinBox( m_editBox );
            int maxValue = 1000;
            if( yearField ) {
                maxValue = TQDate::currentDate().year();
                m_intSpinBox1->setValue( maxValue );
            }
            m_intSpinBox1->setMaxValue( maxValue );
            m_intSpinBox1->setFocus();
            m_intSpinBox1->show();

            if( m_criteriaCombo->currentText() == i18n("is between") ) {
                m_rangeLabel = new TQLabel( i18n("and"), m_editBox );
                m_rangeLabel->setAlignment( AlignCenter );
                m_rangeLabel->show();
                m_intSpinBox2 = new KIntSpinBox( m_editBox );
                if( yearField ) {
                    maxValue = TQDate::currentDate().year();
                    m_intSpinBox2->setValue( maxValue );
                }
                m_intSpinBox2->setMaxValue( maxValue );
                m_intSpinBox2->show();
            }
            break;
        }

        case Rating:
        {
            const TQStringList list = MetaBundle::ratingList();
            m_comboBox = new KComboBox( false, m_editBox );
            m_comboBox->insertStringList( list );
            m_comboBox->show();

            if( m_criteriaCombo->currentText() == i18n("is between") ) {
                m_rangeLabel = new TQLabel( i18n("and"), m_editBox );
                m_rangeLabel->setAlignment( AlignCenter );
                m_rangeLabel->show();
                m_comboBox2 = new KComboBox( false, m_editBox );
                m_comboBox2->insertStringList( list );
                m_comboBox2->show();
            }
            break;
        }

        case Date:
        {
            if( m_criteriaCombo->currentText() == i18n("is in the last") ||
                m_criteriaCombo->currentText() == i18n("is not in the last") ) {
                m_intSpinBox1 = new KIntSpinBox( m_editBox );
                m_intSpinBox1->setMinValue( 1 );
                m_intSpinBox1->show();
                m_dateCombo = new KComboBox( m_editBox );
                m_dateCombo->insertItem( i18n("Days") );
                m_dateCombo->insertItem( i18n("Months") );
                m_dateCombo->insertItem( i18n("Years") );
                m_dateCombo->show();
            }
            else {
                m_dateEdit1 = new TQDateEdit( TQDate::currentDate(), m_editBox);
                m_dateEdit1->setFocus();
                m_dateEdit1->show();
                if( m_criteriaCombo->currentText() == i18n("is between") ) {
                    m_rangeLabel = new TQLabel( i18n("and"), m_editBox );
                    m_rangeLabel->setAlignment( AlignCenter );
                    m_rangeLabel->show();
                    m_dateEdit2 = new TQDateEdit( TQDate::currentDate(), m_editBox);
                    m_dateEdit2->show();
                }
            }

            break;
        }

        case Length:
        {
            m_intSpinBox1 = new KIntSpinBox( m_editBox );
            int maxValue = 1000;
            m_intSpinBox1->setMaxValue( maxValue );
            m_intSpinBox1->setFocus();
            m_intSpinBox1->show();
            if( m_criteriaCombo->currentText() == i18n("is between") ) {
                m_rangeLabel = new TQLabel( i18n("and"), m_editBox );
                m_rangeLabel->setAlignment( AlignCenter );
                m_rangeLabel->show();
                m_intSpinBox2 = new KIntSpinBox( m_editBox );
                m_intSpinBox2->setMaxValue( maxValue );
                m_intSpinBox2->show();
            }
            m_lengthCombo = new KComboBox( m_editBox );
            m_lengthCombo->insertItem( i18n( "Seconds" ) );
            m_lengthCombo->insertItem( i18n( "Minutes" ) );
            m_lengthCombo->insertItem( i18n( "Hours" ) );
            m_lengthCombo->show();
        }

        default: ;
    };

}


void CriteriaEditor::loadCriteriaList( int valueType, TQString condition )
{
    if( m_currentValueType == valueType && condition.isNull() )
        return;

    TQStringList items;

    switch( valueType ) {
        case String:
        case AutoCompletionString:
            items << i18n( "contains" ) << i18n( "does not contain" ) << i18n( "is" ) << i18n( "is not" )
                  << i18n( "starts with" ) << i18n( "does not start with" ) 
                  << i18n( "ends with" ) << i18n( "does not end with" );
            break;

        case Rating:
        case Length:
        case Number:
            items << i18n( "is" ) << i18n( "is not" ) << i18n( "is greater than" ) << i18n( "is smaller than" )
                  << i18n( "is between" );
            break;

        case Year: //fall through
        case Date:
            items << i18n( "is" ) << i18n( "is not" ) << i18n( "is before" ) << i18n( "is after" )
                  << i18n( "is in the last" ) << i18n( "is not in the last" ) << i18n( "is between" );
            break;
        default: ;
    };

    m_criteriaCombo->clear();
    m_criteriaCombo->insertStringList( items );

    if ( !condition.isEmpty() ) {
        int index = items.findIndex( condition );
        if (index!=-1)
            m_criteriaCombo->setCurrentItem( index );
    }
}


int CriteriaEditor::getValueType( int index )
{
    int valueType;

    switch( index ) {
        case FArtist:
        case FComposer:
        case FAlbum:
        case FGenre:
        case FLabel:
        case FMountPoint:
            valueType = AutoCompletionString;
            break;
        case FTitle:
        case FComment:
        case FFilePath:
            valueType = String;
            break;
        case FLength:
            valueType = Length;
            break;
        case FTrack:
        case FScore:
        case FPlayCounter:
        case FBPM:
        case FBitRate:
            valueType = Number;
            break;
        case FRating:
            valueType = Rating;
            break;
        case FYear:
            valueType = Year;
            break;
        default: valueType = Date;
    };

    return valueType;
}


#include "smartplaylisteditor.moc"
