/*
    Copyright (C) 2006  Nicolas Le Guen <goldenear@free.fr>

    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.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

// QT includes.
#include <qdir.h>
#include <qfile.h>
#include <qfileinfo.h>
#include <qpixmap.h>
#include <qevent.h>
#include <qpoint.h>
#include <qmessagebox.h>
#include <qdragobject.h>
#include <qmime.h>
#include <qstrlist.h>
#include <qstringlist.h>
#include <qapplication.h>
#include <qheader.h>

// KDE includes.
#include <klistview.h>
#include <kiconloader.h>

// Local includes.
#include "filesview.h"

QPixmap *folderLocked = 0;
QPixmap *folderClosed = 0;
QPixmap *folderOpen = 0;
QPixmap *fileNormal = 0;

/*****************************************************************************
 *
 * Class Directory
 *
 *****************************************************************************/

Directory::Directory( Directory * parent, const QString& filename )
    : QListViewItem( parent ), f(filename),
      showDirsOnly( parent->showDirsOnly ),
      pix( 0 )
{
    p = parent;
    readable = QDir( fullName() ).isReadable();

    if ( !readable )
        setPixmap( folderLocked );
    else
        setPixmap( folderClosed );
}


Directory::Directory( QListView * parent, const QString& filename )
    : QListViewItem( parent ), f(filename),
      showDirsOnly( ( (DirectoryView*)parent )->showDirsOnly() ),
      pix( 0 )
{
    p = 0;
    readable = QDir( fullName() ).isReadable();
}


void Directory::setPixmap( QPixmap *px )
{
    pix = px;
    setup();
    widthChanged( 0 );
    invalidateHeight();
    repaint();
}


const QPixmap *Directory::pixmap( int i ) const
{
    if ( i )
        return 0;
    return pix;
}

void Directory::setOpen( bool o )
{
    if ( o )
        setPixmap( folderOpen );
    else
        setPixmap( folderClosed );

    if ( o && !childCount() ) {
        QString s( fullName() );
        QDir thisDir( s );
        if ( !thisDir.isReadable() ) {
            readable = FALSE;
            setExpandable( FALSE );
            return;
        }

        listView()->setUpdatesEnabled( FALSE );
        const QFileInfoList * files = thisDir.entryInfoList();
        if ( files ) {
            QFileInfoListIterator it( *files );
            QFileInfo * fi;
            while( (fi=it.current()) != 0 ) {
                ++it;
                if ( fi->fileName() == "." || fi->fileName() == ".." )
                    ; // nothing
                else if ( fi->isSymLink() && !showDirsOnly ) {
                    FileItem *item = new FileItem( this, fi->fileName(),
                                                     "Symbolic Link" );
                    item->setPixmap( fileNormal );
                }
                else if ( fi->isDir() )
                    (void)new Directory( this, fi->fileName() );
                else if ( !showDirsOnly ) {
                    FileItem *item
                        = new FileItem( this, fi->fileName(),
                                             fi->isFile()?"File":"Special" );
                    item->setPixmap( fileNormal );
                }
            }
        }
        listView()->setUpdatesEnabled( TRUE );
    }
    QListViewItem::setOpen( o );
}


void Directory::setup()
{
    setExpandable( TRUE );
    QListViewItem::setup();
}


QString Directory::fullName()
{
    QString s;
    if ( p ) {
        s = p->fullName();
        s.append( f.name() );
        s.append( "/" );
    } else {
        s = f.name();
    }
    return s;
}


QString Directory::text( int column ) const
{
    if ( column == 0 )
        return f.name();
    else if ( readable )
        return "Directory";
    else
        return "Unreadable Directory";
}

/*****************************************************************************
 *
 * Class DirectoryView
 *
 *****************************************************************************/

DirectoryView::DirectoryView( QWidget *parent, const char *name, bool sdo )
    : KListView( parent, name ), dirsOnly( sdo ), oldCurrent( 0 ),
      dropItem( 0 ), mousePressed( FALSE )
{
    autoopen_timer = new QTimer( this );
    if ( !folderLocked ) {
        folderLocked = new QPixmap( SmallIcon( "folder_locked" ) );
        folderClosed = new QPixmap( SmallIcon( "folder" ) );
        folderOpen = new QPixmap( SmallIcon( "folder_open" ) );
        fileNormal = new QPixmap( SmallIcon( "unknown" ) );
    }

    connect( this, SIGNAL( doubleClicked( QListViewItem * ) ),
             this, SLOT( slotFolderSelected( QListViewItem * ) ) );
    connect( this, SIGNAL( returnPressed( QListViewItem * ) ),
             this, SLOT( slotFolderSelected( QListViewItem * ) ) );

    setAcceptDrops( TRUE );
    viewport()->setAcceptDrops( TRUE );

    connect( autoopen_timer, SIGNAL( timeout() ),
             this, SLOT( openFolder() ) );


}

void DirectoryView::slotFolderSelected( QListViewItem *i )
{
    if ( !i || !showDirsOnly() )
        return;

    Directory *dir = (Directory*)i;
    emit folderSelected( dir->fullName() );
}

void DirectoryView::openFolder()
{
    autoopen_timer->stop();
    if ( dropItem && !dropItem->isOpen() ) {
        dropItem->setOpen( TRUE );
        dropItem->repaint();
    }
}

static const int autoopenTime = 750;


void DirectoryView::contentsDragEnterEvent( QDragEnterEvent *e )
{
    if ( !QUriDrag::canDecode(e) ) {
        e->ignore();
        return;
    }

    oldCurrent = currentItem();

    QListViewItem *i = itemAt( contentsToViewport(e->pos()) );
    if ( i ) {
        dropItem = i;
        autoopen_timer->start( autoopenTime );
    }
}


void DirectoryView::contentsDragMoveEvent( QDragMoveEvent *e )
{
    if ( !QUriDrag::canDecode(e) ) {
        e->ignore();
        return;
    }

    QPoint vp = contentsToViewport( ( (QDragMoveEvent*)e )->pos() );
    QListViewItem *i = itemAt( vp );
    if ( i ) {
        setSelected( i, TRUE );
        e->accept();
        if ( i != dropItem ) {
            autoopen_timer->stop();
            dropItem = i;
            autoopen_timer->start( autoopenTime );
        }
        switch ( e->action() ) {
        case QDropEvent::Copy:
            break;
        case QDropEvent::Move:
            e->acceptAction();
            break;
        case QDropEvent::Link:
            e->acceptAction();
            break;
        default:
            ;
        }
    } else {
        e->ignore();
        autoopen_timer->stop();
        dropItem = 0;
    }
}

void DirectoryView::contentsDragLeaveEvent( QDragLeaveEvent * )
{
    autoopen_timer->stop();
    dropItem = 0;

    setCurrentItem( oldCurrent );
    setSelected( oldCurrent, TRUE );
}

void DirectoryView::contentsDropEvent( QDropEvent *e )
{
    autoopen_timer->stop();

    if ( !QUriDrag::canDecode(e) ) {
        e->ignore();
        return;
    }

    QListViewItem *item = itemAt( contentsToViewport(e->pos()) );
    if ( item ) {

        QStrList lst;

        QUriDrag::decode( e, lst );

        QString str;

        switch ( e->action() ) {
            case QDropEvent::Copy:
            str = "Copy";
            break;
            case QDropEvent::Move:
            str = "Move";
            e->acceptAction();
            break;
            case QDropEvent::Link:
            str = "Link";
            e->acceptAction();
            break;
            default:
            str = "Unknown";
        }

        str += "\n\n";

        e->accept();

        for ( uint i = 0; i < lst.count(); ++i ) {
            QString filename = QDir::convertSeparators(QUriDrag::uriToLocalFile(lst.at(i)));
            str += filename + "\n";
        }
        str += QString( "\nTo\n\n   %1" )
            .arg( QDir::convertSeparators(fullPath(item)) );

        QMessageBox::information( this, "Drop target", str, "Not implemented" );
    } else
        e->ignore();

}


QString DirectoryView::fullPath(QListViewItem* item)
{
    QString fullpath = item->text(0);
    while ( (item=item->parent()) ) {
        if ( item->parent() )
            fullpath = item->text(0) + "/" + fullpath;
        else
            fullpath = item->text(0) + fullpath;
    }
#ifdef Q_WS_WIN
        if (fullpath.length() > 2 && fullpath[1] != ':') {
                QDir dir(fullpath);
                fullpath = dir.currentDirPath().left(2) + fullpath;
        }
#endif

    return fullpath;
}

void DirectoryView::contentsMousePressEvent( QMouseEvent* e )
{
    QListView::contentsMousePressEvent(e);
    QPoint p( contentsToViewport( e->pos() ) );
    QListViewItem *i = itemAt( p );
    if ( i ) {
        // if the user clicked into the root decoration of the item, don't try to start a drag!
        if ( p.x() > header()->cellPos( header()->mapToActual( 0 ) ) +
             treeStepSize() * ( i->depth() + ( rootIsDecorated() ? 1 : 0) ) + itemMargin() ||
             p.x() < header()->cellPos( header()->mapToActual( 0 ) ) ) {
            presspos = e->pos();
            mousePressed = TRUE;
        }
    }
}

void DirectoryView::contentsMouseMoveEvent( QMouseEvent* e )
{
    if ( mousePressed && ( presspos - e->pos() ).manhattanLength() > QApplication::startDragDistance() ) {
        mousePressed = FALSE;
        QListViewItem *item = itemAt( contentsToViewport(presspos) );
        if ( item ) {
            QString source = fullPath(item);
            if ( QFile::exists(source) ) {
                QUriDrag* ud = new QUriDrag(viewport());
                ud->setFileNames( source );
                if ( ud->drag() )
                    QMessageBox::information( this, "Drag source",
                    QString("Delete ") + QDir::convertSeparators(source), "Not implemented" );
            }
        }
    }
}

void DirectoryView::contentsMouseReleaseEvent( QMouseEvent * )
{
    mousePressed = FALSE;
}

void DirectoryView::setDir( const QString &s )
{
    QListViewItemIterator it( this );
    ++it;
    for ( ; it.current(); ++it ) {
        it.current()->setOpen( FALSE );
    }

    QStringList lst( QStringList::split( "/", s ) );
    QListViewItem *item = firstChild();
    QStringList::Iterator it2 = lst.begin();
    for ( ; it2 != lst.end(); ++it2 ) {
        while ( item ) {
            if ( item->text( 0 ) == *it2 ) {
                item->setOpen( TRUE );
                break;
            }
            item = item->itemBelow();
        }
    }

    if ( item )
        setCurrentItem( item );
}

void FileItem::setPixmap( QPixmap *p )
{
    pix = p;
    setup();
    widthChanged( 0 );
    invalidateHeight();
    repaint();
}


const QPixmap *FileItem::pixmap( int i ) const
{
    if ( i )
        return 0;
    return pix;
}

FilesView::FilesView( QWidget *parent, const char *name, bool )
 : DirectoryView( parent, "files", FALSE)
{

    this->addColumn( "Files" );
    this->setFullWidth ( true );
//    this->addColumn( "Type" );
    this->setTreeStepSize( 20 );

    const QFileInfoList* roots = QDir::drives();
    QPtrListIterator<QFileInfo> i(*roots);
    QFileInfo* fi;
    while ( (fi = *i) ) {
        ++i;
        Directory * root = new Directory( this, fi->filePath() );
        if ( roots->count() <= 1 )
            root->setOpen( TRUE ); // be interesting
    }

//    this->resize( 400, 400 );
//    this->setCaption( "Qt Example - Directory Browser" );
    this->setAllColumnsShowFocus( TRUE );
}

FilesView::~FilesView()
{
    
}
