/***************************************************************************
                         directorylist.cpp
                            -------------------
   begin                : Tue Feb 4 2003
   copyright            : (C) 2003 Scott Wheeler <wheeler@kde.org>
                        : (C) 2004 Max Howell <max.howell@methylblue.com>
                        : (C) 2004 Mark Kretschmann <markey@web.de>
***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include <tqlabel.h>
#include <tqtooltip.h>
#include <tqdir.h>

#include <tdeconfig.h>
#include <tdeglobal.h>
#include <tdemessagebox.h>
#include <tdefileitem.h>
#include <tdelocale.h>
#include <kdebug.h>
#include <tdeversion.h>
#include <tqregexp.h>
#include <kiconloader.h>
#include <tdeapplication.h>
#include <dcopref.h>
#include <dcopclient.h>

#include "directorylist.h"

using Collection::Item;
using Collection::DeviceItem;

CollectionSetup::CollectionSetup( TQWidget *parent, bool recursive, bool fullMode, TQStringList dirs )
    : TQVBox( parent )
{
    m_dirs = dirs;

//     (new TQLabel( i18n(
//         "Select the folder(s) to scan. "), this ))->setAlignment( TQt::WordBreak );

    m_view = new TQListView( this );
/*     m_recursive = new TQCheckBox( i18n("&Scan folders recursively"), this );*/
    m_recursive = recursive;
//     m_monitor   = new TQCheckBox( i18n("&Watch folders for changes"), this );
//     m_playlists = new TQCheckBox( i18n("&Import playlists"), this );
//
//     TQToolTip::add( m_recursive, i18n( "If selected, amaroK reads all folders recursively." ) );
//     TQToolTip::add( m_monitor,   i18n( "If selected, folders will automatically get rescanned when the content is modified, e.g. when a new file was added." ) );
//     TQToolTip::add( m_playlists, i18n( "If selected, playlist files (.m3u) will automatically be added to the Playlist-Browser." ) );

    // Read config values
//     m_dirs = AmarokConfig::collectionFolders();
//     m_recursive->setChecked( false );
//     m_monitor->setChecked( AmarokConfig::monitorChanges() );
//     m_playlists->setChecked( AmarokConfig::importPlaylists() );

    m_view->addColumn( TQString::null );
    m_view->setRootIsDecorated( true );
    reinterpret_cast<TQWidget*>(m_view->header())->hide();
    new Item(  m_view, this, i18n( "System Folder" ), "/", "folder_red" );
    new Item(  m_view, this, i18n( "Home Folder" ), TQDir::homeDirPath(), "folder_home" );
    if( fullMode ) new DeviceItem( m_view, this);

    setSpacing( 6 );
}

void CollectionSetup::slotRecursiveToggled(bool on)
{
	m_recursive = on;
}


//////////////////////////////////////////////////////////////////////////////////////////
// CLASS Item
//////////////////////////////////////////////////////////////////////////////////////////
Item::Item( TQListView *parent, CollectionSetup *collection, const TQString &name, const TQString &path, const TQString &icon )
    : TQCheckListItem(  parent, name, TQCheckListItem::CheckBox  )
    , m_lister( true )
    , m_url( "file:" + path )
    , m_listed( false )
{
    collectionSetup = collection;
    m_lister.setDirOnlyMode( true );
    m_lister.setShowingDotFiles( true );
    connect( &m_lister, SIGNAL(newItems( const KFileItemList& )), SLOT(newItems( const KFileItemList& )) );
    setText( 1, path );
    setOpen( true );
    if (  !icon.isNull() )
       setPixmap(  0, SmallIcon( icon ) );
    else
       setPixmap(  0, SmallIcon( "folder" ) );
    setVisible( true );
    connect( collectionSetup, SIGNAL(resetDirs()), this, SLOT(reset()) );
}


Item::Item( TQListViewItem *parent, CollectionSetup *collection, const KURL &url )
    : TQCheckListItem( parent, url.fileName(), TQCheckListItem::CheckBox  )
    , m_lister( true )
    , m_url( url )
    , m_listed( false )
{
    collectionSetup = collection;
    m_lister.setDirOnlyMode( true );
    m_lister.setShowingDotFiles( true );
    setText( 1, url.fileName() );
    setExpandable( true );
    connect( &m_lister, SIGNAL(newItems( const KFileItemList& )), SLOT(newItems( const KFileItemList& )) );
    connect( &m_lister, SIGNAL(completed()), SLOT(completed()) );
    connect( &m_lister, SIGNAL(canceled()), SLOT(completed()) );
    connect( collectionSetup, SIGNAL(resetDirs()), this, SLOT(reset()) );
}


TQString
Item::fullPath() const
{
   TQString path;

   for (  const TQListViewItem *item = this; dynamic_cast<const TQListViewItem*>( item ); item = item->parent() )
   {
      path.prepend( '/' );
      path.prepend(  item->text(  1 ) );
   }
   return path;
}


void
Item::setOpen( bool b )
{
    if ( !m_listed )
    {
        m_lister.openURL( m_url, true );
        m_listed = true;
    }

    TQListViewItem::setOpen( b );
}


void
Item::stateChange( bool b )
{
    if( collectionSetup->recursive() )
        for( TQListViewItem *item = firstChild(); item; item = item->nextSibling() )
            static_cast<TQCheckListItem*>(item)->TQCheckListItem::setOn( b );

    // Update folder list
    TQStringList::Iterator it = collectionSetup->m_dirs.find( m_url.path() );
    if ( isOn() ) {
        if ( it == collectionSetup->m_dirs.end() )
        {
            collectionSetup->m_dirs << m_url.path();
            collectionSetup->m_refcount[ m_url.path() ] = 1;
        }
        else
            collectionSetup->m_refcount[ m_url.path() ]++;
    }
    else if ( collectionSetup->m_refcount.find( m_url.path() ) != collectionSetup->m_refcount.end() )
    {
        if ( --collectionSetup->m_refcount[ m_url.path() ] == 0 )
        {
            collectionSetup->m_dirs.erase(  it );
            collectionSetup->m_refcount.remove( m_url.path() );
        }
    }

    // Redraw parent items
    listView()->triggerUpdate();
}


void
Item::activate()
{
    if( !isDisabled() )
        TQCheckListItem::activate();
}


void
Item::newItems( const KFileItemList &list ) //SLOT
{
    for( KFileItemListIterator it( list ); *it; ++it )
    {
        Item *item = new Item( this, collectionSetup, (*it)->url() );

        item->setOn( collectionSetup->recursive() && isOn() ||
                     collectionSetup->m_dirs.contains( item->fullPath() ) );

        item->setPixmap( 0, (*it)->pixmap( TDEIcon::SizeSmall ) );
    }
}


void
Item::paintCell( TQPainter * p, const TQColorGroup & cg, int column, int width, int align )
{
    bool dirty = false;

    // Figure out if a child folder is activated
    for ( uint i = 0; i < collectionSetup->m_dirs.count(); i++ )
    {
       if ( collectionSetup->m_dirs[ i ] == m_url.path() )
       {
          dirty = true;
       }
       else if (  collectionSetup->m_dirs[ i ].startsWith(  m_url.path() ) )
          dirty = true;
    }

    // Use a different color if this folder has an activated child folder
    TQColorGroup _cg = cg;
    if ( dirty ) _cg.setColor( TQColorGroup::Text, TQt::blue );

    TQCheckListItem::paintCell( p, isDisabled() ? listView()->palette().disabled() : _cg, column, width, align );
    if (!dirty)
        setOn(false);
}

void Item::reset() { this->setOn(false); }

//////////////////////////////////////////////////////////////////////////////////////////
// CLASS DeviceItem
//////////////////////////////////////////////////////////////////////////////////////////

DeviceItem::DeviceItem( TQListView *parent, CollectionSetup *collection )
    : TQCheckListItem( parent, i18n("Devices"), TQCheckListItem::CheckBox  )
    , m_lister( true )
    , m_listed( false )
{
    collectionSetup = collection;
    m_lister.setDirOnlyMode( true );
    connect( &m_lister, SIGNAL(newItems( const KFileItemList& )), SLOT(newItems( const KFileItemList& )) );
    connect( collectionSetup, SIGNAL(resetDirs()), this, SLOT(reset()) );

    if ( KDE::versionMajor() == 3 && KDE::versionMinor() < 4 )
    {
       m_url = "devices:/";
    }
    else
       m_url = "media:/";

    setText(1, "devices");
    setOpen( true );
    setPixmap(0, SmallIcon("kfm") );
    setVisible( true );
}


DeviceItem::DeviceItem( TQListViewItem *parent, CollectionSetup *collection, const TQString &name, const KURL &url )
    : TQCheckListItem( parent, name, TQCheckListItem::CheckBox  )
    , m_lister( true )
    , m_url( url )
    , m_listed( false )
{
    collectionSetup = collection;

    if (!kapp->dcopClient()->isAttached())
        kapp->dcopClient()->attach();
    else
		////kdDebug() << "attached" << endl;

    TQByteArray data;
    TQByteArray param;
    TQCString retType;
    TQStringList retVal;
    TQDataStream streamout(param,IO_WriteOnly);
    streamout<< url.fileName();
    TQCString mediacall="mediamanager";
    TQCString devicecall="properties";
    if ( KDE::versionMajor() == 3 && KDE::versionMinor() < 4 )
    {
       mediacall="mountwatcher";
       devicecall="basicDeviceInfo";
    }


	DCOPRef mediamanager("kded", mediacall);
	DCOPReply reply = mediamanager.call( devicecall, url.fileName() );

    if ( !reply.isValid() )
    {
            ////kdDebug() << "not valid" << endl;
    }
    retVal = reply;
    //KAutoMount* am = new KAutoMount( true, "", retVal[5], "","", false );
    ////kdDebug() << retVal[6] << endl;

    setText(1, KURL(retVal[6]).path());
	setText(2, url.fileName());
    kdDebug() << "Device Item: " << name << " " << url.fileName() << " " << text(1) << endl;
    m_lister.setDirOnlyMode( true );
    setExpandable( false );
    connect( &m_lister, SIGNAL(newItems( const KFileItemList& )), SLOT(newItems( const KFileItemList& )) );
    connect( &m_lister, SIGNAL(completed()), SLOT(completed()) );
    connect( &m_lister, SIGNAL(canceled()), SLOT(completed()) );
    connect( collectionSetup, SIGNAL(resetDirs()), this, SLOT(reset()) );
}


TQString
DeviceItem::fullPath() const
{
    TQString path = text(1);
    if (path != "devices")
        return path;
    return "";
}


void
DeviceItem::setOpen( bool b )
{
    if ( !m_listed  && text(1) == "devices")
    {
        m_lister.openURL( m_url, true );
    }
    m_listed = true;

    TQListViewItem::setOpen( b );
}


void
DeviceItem::stateChange( bool b )
{
    if( collectionSetup->recursive() )
        for( TQListViewItem *item = firstChild(); item; item = item->nextSibling() )
            static_cast<TQCheckListItem*>(item)->TQCheckListItem::setOn( b );

    if (text(1) != "devices")
    {
        // Update folder list
        TQStringList::Iterator it = collectionSetup->m_dirs.find( text(1) );
        if ( isOn() ) {
            if ( it == collectionSetup->m_dirs.end() )
            {
                collectionSetup->m_dirs << text(1);
				mountDevice(text(2));
                collectionSetup->m_refcount[text(1)] = 1;
            }
            else
                collectionSetup->m_refcount[text(1)]++;

        }
        else if ( collectionSetup->m_refcount.find(text(1)) != collectionSetup->m_refcount.end() )
        {
            if ( --collectionSetup->m_refcount[text(1)] == 0 )
            {
                collectionSetup->m_dirs.erase( it );
                collectionSetup->m_refcount.remove(text(1));
            }
        }
    }

    // Redraw parent items
    listView()->triggerUpdate();
}


void
DeviceItem::activate()
{
    if( !isDisabled() )
        TQCheckListItem::activate();
}


void
DeviceItem::newItems( const KFileItemList &list ) //SLOT
{
    for( KFileItemListIterator it( list ); *it; ++it )
    {
        kdDebug() << (*it)->name() << " " << (*it)->url() << " " << (*it)->text() << endl;
        if (this->listView()->findItem((*it)->name(),0) == 0){
            DeviceItem *item = new DeviceItem( this, collectionSetup, (*it)->name(), (*it)->url() );

            item->setOn( collectionSetup->recursive() && isOn() ||
                    collectionSetup->m_dirs.contains( item->fullPath() ) );

            item->setPixmap( 0, (*it)->pixmap( TDEIcon::SizeSmall ) );
        }
    }
}


void
DeviceItem::paintCell( TQPainter * p, const TQColorGroup & cg, int column, int width, int align )
{
   bool dirty = false;

   TQColorGroup _cg = cg;

	////kdDebug() << text(1) << endl;
   if (text(1) != "devices")
   {
      // Figure out if a child folder is activated
      for ( uint i = 0; i < collectionSetup->m_dirs.count(); i++ )
      {
         if ( collectionSetup->m_dirs[i] == text(1) )
         {
            dirty = true;
         }
         else if ( collectionSetup->m_dirs[i].startsWith( text(1) + "/" ) )
            dirty = true;
      }

   }
   else
   {
      for( TQListViewItem *item = firstChild(); item; item = item->nextSibling() )
      {
         DeviceItem *itm = dynamic_cast<DeviceItem*>(item);
         for ( uint i = 0; i < collectionSetup->m_dirs.count(); i++ )
         {
            if ( collectionSetup->m_dirs[i] == itm->fullPath() )
            {
               dirty = true;
               break;
            }
            else if ( collectionSetup->m_dirs[i].startsWith( itm->fullPath() ) )
            {
               dirty = true;
               break;
            }
         }
      }
   }
   // Use a different color if this folder has an activated child folder
   if ( dirty ) _cg.setColor( TQColorGroup::Text, TQt::blue );
   TQCheckListItem::paintCell( p, isDisabled() ? listView()->palette().disabled() : _cg, column, width, align );
}

TQString DeviceItem::getMountPoint( const TQString & device )
{
	DCOPRef mediamanager("kded", "mediamanager");
	DCOPReply reply = mediamanager.call( "properties", device );

	TQStringList properties;
	reply.get( properties, "TQStringList" );

	TQString mountpoint = * (properties.at(7) );

	return mountpoint;

}

void
DeviceItem::mountDevice( const TQString & device)
{

    if (!kapp->dcopClient()->isAttached())
        kapp->dcopClient()->attach();

	TQString mountpoint;
	mountpoint = getMountPoint(device);

	if( mountpoint != TQString::null ) // already mounted
		return;

	DCOPRef mediamanager("kded", "mediamanager");
	DCOPReply reply = mediamanager.call( "mountByNode", device );

	bool success;
	reply.get( success, "bool" );

	mountpoint = getMountPoint(device);

	if(! success || mountpoint == TQString::null ) {
		KMessageBox::sorry( 0, i18n("Cannot mount device '%1'. Please check that you have the permissions needed to mount the device, as well as the needed kernel modules loaded.").arg(device) );
	}
}

void DeviceItem::reset() { this->setOn(false); }

TQStringList CollectionSetup::pruneSelectedDirs( TQStringList listOfUrls ){
    // This gets rid of redundant sub-directories
    // from the list of dirs to be scanned.

    TQStringList filepattern;

    listOfUrls.sort();

    TQString prev;
    TQStringList prevdirs;
    struct stat sb;

    for (TQStringList::Iterator it = listOfUrls.begin(); it != listOfUrls.end(); it++ ){
        //kdDebug() << "dir: " << (*it) << endl;
        (*it) = (*it).stripWhiteSpace();

        // replace block devices with mountpoints
        lstat( (*it).ascii(), &sb );
        if ( (sb.st_mode & S_IFMT) == S_IFBLK ) {
            // This is actually from directorylist.cpp
            DCOPRef mediamanager("kded", "mediamanager");
            DCOPReply reply = mediamanager.call( "properties", (*it) );

            TQStringList properties;
            reply.get( properties, "TQStringList" );

            (*it) = * (properties.at(7) );
        } else {
                (*it) = (*it) + "/";
        }

        if (prevdirs.isEmpty()){
            //kdDebug() << (*it) << endl;
            filepattern.append(*it);
            prevdirs.append((*it));
        }else{
            filepattern.append(*it);
            bool shouldappend = true;
            for (TQStringList::Iterator it2 = prevdirs.begin(); it2 != prevdirs.end(); it2++ ){
                if ((*it).contains(*it2)){
                    //kdDebug() << (*it) << endl;
                    filepattern.remove((*it));
                    shouldappend = false;
                    break;
                }
            }

            if (shouldappend)
                prevdirs.append((*it));
        }
    }

    return filepattern;
}

void CollectionSetup::writeConfig( const char* optGroup, const char* optName )
{
    TDEConfig *config = TDEGlobal::config();
    config->setGroup( optGroup );
    config->writeEntry( optName, dirs() );
    config->sync();
}

void CollectionSetup::reset()
{
    emit resetDirs();
}

#include "directorylist.moc"
