/*
 * This file is part of the KFTPGrabber project
 *
 * Copyright (C) 2003-2004 by the KFTPGrabber developers
 * Copyright (C) 2003-2004 Jernej Kos <kostko@jweb-network.net>
 *
 * 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
 * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
 * NON-INFRINGEMENT.  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., 51 Franklin Steet, Fifth Floor, Boston,
 * MA 02110-1301, USA.
 *
 * In addition, as a special exception, the copyright holders give
 * permission to link the code of portions of this program with the
 * OpenSSL library under certain conditions as described in each
 * individual source file, and distribute linked combinations
 * including the two.
 * You must obey the GNU General Public License in all respects
 * for all of the code used other than OpenSSL.  If you modify
 * file(s) with this exception, you may extend this exception to your
 * version of the file(s), but you are not obligated to do so.  If you
 * do not wish to do so, delete this exception statement from your
 * version.  If you delete this exception statement from all source
 * files in the program, then also delete it here.
 */

#include "browser/treeview.h"
#include "browser/view.h"

#include "engine/thread.h"

#include "misc.h"

#include <qheader.h>
#include <qtimer.h>

#include <kio/job.h>
#include <kurl.h>
#include <kurldrag.h>

using namespace KFTPGrabberBase;

namespace KFTPWidgets {

namespace Browser {

/*
 *                         TreeViewItem class
 */

TreeViewItem::TreeViewItem(TreeView *parent, const KURL &url)
  : QListViewItem(parent),
    m_tree(parent),
    m_url(url),
    m_dirty(false)
{
  // Set the name
  setText(0, path2Name(url.path(-1)));
}

TreeViewItem::TreeViewItem(TreeView *tree, QListViewItem *parent, const KURL &url)
  : QListViewItem(parent),
    m_tree(tree),
    m_url(url),
    m_dirty(false)
{
  // Set the name
  setText(0, path2Name(url.path(-1)));
}

TreeViewItem::~TreeViewItem()
{
  // The item should be removed from the list as well
  m_tree->m_treeItems.remove(m_url.path());
}

int TreeViewItem::compare(QListViewItem *i, int col, bool) const
{
  // Hidden files must be on top
  if (m_url.fileName().at(0) == '.')
    return -1;

  return QListViewItem::compare(i, col, false);
}

/*
 *                          TreeView class
 */

TreeView::TreeView(QWidget *parent)
 : KListView(parent)
{
  // Create the columns
  addColumn(i18n("Directory"));

  // General tree settings
  setDragEnabled(true);
  setAcceptDrops(true);
  setMinimumWidth(150);
  setRootIsDecorated(false);
  header()->hide();

  // Reset the view
  resetView(KURL("/"));
  m_noItemOpen = false;

  // Connect signals
  connect(this, SIGNAL(clicked(QListViewItem*)), this, SLOT(slotClicked(QListViewItem*)));
  connect(this, SIGNAL(doubleClicked(QListViewItem*)), this, SLOT(slotDoubleClicked(QListViewItem*)));
  
  // Drag and drop
  m_dropItem = 0L;
}


TreeView::~TreeView()
{
  // Free the item index
  m_treeItems.clear();
}

void TreeView::resetView(const KURL &url)
{
  // Free the item index
  m_treeItems.clear();
  
  // Clear the view
  clear();
  header()->resizeSection(0, 0);

  // Create the root item
  TreeViewItem *rootItem = new TreeViewItem(this, remoteUrl("/", url));
  rootItem->setText(0, url.isLocalFile() ? i18n("Root directory") : url.host());
  rootItem->setPixmap(0, loadSmallPixmap("folder_red"));
  setOpen(rootItem, true);
}

int TreeView::openUrl(const KURL &url, QListViewItem *parent)
{
  // Root item should always be open
  setOpen(firstChild(), true);

  // Go trough all items in the list and try to find the url
  QListViewItem *item;
  if (!parent)
    item = firstChild();
  else
    item = parent;

  while (item) {
    // Check if the item is correct
    if (static_cast<TreeViewItem*>(item)->m_url.path(-1) == url.path(-1)) {
      // We have found it
      ensureItemVisible(item);
      setCurrentItem(item);
      setSelected(item, true);

      // Change viewport
      QRect r = itemRect(item);
      if (r.isValid()) {
        int x, y;
        viewportToContents(contentsX(), r.y(), x, y);
        setContentsPos(x, y);
      }

      return 1;
    }

    if (item->firstChild()) {
      // If item has children, go after them
      if (openUrl(url, item->firstChild()) == 1)
        return 1;
    }

    item = item->nextSibling();
  }

  return 0;
}

QListViewItem *TreeView::findItem(QListViewItem *parent, const QString &name)
{
  QListViewItem *item = parent->firstChild();

  while (item) {
    if (item->text(0) == name)
      return item;

    item = item->nextSibling();
  }

  // If nothing is found, parent should be returned
  return parent;
}

void TreeView::createFolder(const KURL &url, QPixmap icon)
{
  int numDirs = url.path(1).contains('/', false);
  QListViewItem *item = firstChild();
  
  for (int i = 1; i < numDirs; i++) {
    QString itemUrl = url.path().section('/', 0, i);
    
    if (m_treeItems[itemUrl]) {
      // Item exists for this URL
      item = m_treeItems[itemUrl];
    } else {
      // Item not yet created
      KURL tmp = url;
      tmp.setPath(itemUrl);
      
      item = new TreeViewItem(this, item, tmp);
      if (i == numDirs - 1)
        item->setPixmap(0, icon);
      else
        item->setPixmap(0, loadSmallPixmap("folder"));
      
      m_treeItems.insert(itemUrl, static_cast<TreeViewItem*>(item));
    }
    
    // Mark it as dirty
    static_cast<TreeViewItem*>(item)->m_dirty = true;

    // Open it
    if (!m_noItemOpen)
      setOpen(item, true);
  }

  // Root item should always be open
  setOpen(firstChild(), true);
}

void TreeView::endUpdate(const KURL &url)
{
  // Remove all items in the selected dir, not marked as dirty
  TreeViewItem *top = static_cast<TreeViewItem*>(firstChild());
  TreeViewItem *tmp = 0L;
  int numDirs = url.path(1).contains('/', false);
  
  if (url.path() == "/") {
    TreeViewItem *i = static_cast<TreeViewItem*>(top->firstChild());
    
    while (i) {
      tmp = static_cast<TreeViewItem*>(i->nextSibling());
      
      if (!i->m_dirty) {
        // Remove item from the index
        delete i;
      } else {
        i->m_dirty = false;
      }
      
      i = tmp;
    }
    
    return;
  }

  // URL for items
  KURL itemURL = url;
  itemURL.setPath("/");
  
  for (int i = 1; i < numDirs; i++) {
    QString sect = url.path().section('/', i, i);
    itemURL.setPath(url.path().section('/', 0, i));
    
    if (!m_treeItems[itemURL.path()]) {
      // Item not yet created
      return;
    } else {
      // Item is already present
      top = m_treeItems[itemURL.path()];

      // Check for URL match
      if (itemURL.path(-1) == url.path(-1)) {
        // URL match, delete the item's children
        TreeViewItem *i = static_cast<TreeViewItem*>(top->firstChild());
        
        while (i) {
          tmp = static_cast<TreeViewItem*>(i->nextSibling());
          
          if (!i->m_dirty) {
            // Remove item from the index
            delete i;
          } else {
            i->m_dirty = false;
          }
            
          i = tmp;
        }
        return;
      }
    }
  }
}

void TreeView::clearFolder(const KURL &url)
{
  // Remove url's children
  QListViewItem *top = firstChild();
  int numDirs = url.path(1).contains('/', false);
  
  if (url.path() == "/") {
    QListViewItem *i = top->firstChild();
    
    while (i) {
      // Remove item from the index
      delete i;
      
      i = top->firstChild();
    }
    
    return;
  }

  // URL for items
  KURL itemURL = url;
  itemURL.setPath("/");

  for (int i = 1; i < numDirs; i++) {
    QString sect = url.path().section('/', i, i);
    itemURL.setPath(url.path().section('/', 0, i));

    if (!m_treeItems[itemURL.path()]) {
      // Item not yet created
      return;
    } else {
      // Item is already present
      top = m_treeItems[itemURL.path()];

      // Check for URL match
      if (itemURL.path(-1) == url.path(-1)) {
        // URL match, delete the item's children
        QListViewItem *i = top->firstChild();
        
        while (i) {
          // Remove item from the index
          delete i;
          
          i = top->firstChild();
        }
        return;
      }
    }
  }
}

void TreeView::removeFolder(const KURL &url)
{
  // Removes a folder at url
  QListViewItem *top = firstChild();
  int numDirs = url.path(1).contains('/', false);

  // URL for items
  KURL itemURL = url;
  itemURL.setPath("/");

  for (int i = 1; i < numDirs; i++) {
    QString sect = url.path().section('/', i, i);
    itemURL.setPath(url.path().section('/', 0, i));

    if (!m_treeItems[itemURL.path()]) {
      // Item not yet created
      return;
    } else {
      // Item is already present
      top = m_treeItems[itemURL.path()];

      // Check for URL match
      if (itemURL.path(-1) == url.path(-1)) {
        // Remove item from the index
        delete top;
        
        return;
      }
    }
  }
}

void TreeView::slotClicked(QListViewItem *item)
{
  if (!item)
    return;
    
  m_noItemOpen = true;
  emit pathChanged(static_cast<TreeViewItem*>(item)->m_url);
  m_noItemOpen = false;
}

void TreeView::slotDoubleClicked(QListViewItem *item)
{
  if (!item)
    return;
    
  setOpen(item, !item->isOpen());
}

void TreeView::startDrag()
{
  dragObject()->drag();
}

bool TreeView::acceptDrag(QDropEvent *e)
{
  return KURLDrag::canDecode(e) &&
         ( e->action() == QDropEvent::Copy
           || e->action() == QDropEvent::Move
           || e->action() == QDropEvent::Link )
         && acceptDrops()
         && dragEnabled();
}

QDragObject *TreeView::dragObject()
{
  TreeViewItem *item = static_cast<TreeViewItem*>(selectedItem());

  // Set the correct pixmap
  QPixmap pix = *item->pixmap(0);
  
  KURL::List urls;
  urls.append(item->m_url);
  
  KIO::MetaData meta;
  meta.insert(item->m_url.htmlURL().local8Bit(), "D:0");
  
  m_dragObject = new KURLDrag(urls, meta, this, name());
  m_dragObject->setPixmap(pix, QPoint(pix.width() / 2, pix.height() / 2));
  
  return m_dragObject;
}

void TreeView::contentsDragEnterEvent(QDragEnterEvent *e)
{
  if (!acceptDrag(e)) {
    e->accept(false);
    return;
  }

  e->acceptAction();
  QListViewItem *i = itemAt(contentsToViewport(e->pos()));
  if (i)
    m_dropItem = i;
}

void TreeView::contentsDragMoveEvent(QDragMoveEvent *e)
{
  if (!acceptDrag(e)) {
    e->accept(false);
    return;
  }

  e->acceptAction();
  QListViewItem *i = itemAt(contentsToViewport(e->pos()));
  
  if (i && i != m_dropItem)
    m_dropItem = i;
}

void TreeView::contentsDragLeaveEvent(QDragLeaveEvent*)
{
  m_dropItem = 0L;
}

void TreeView::contentsDropEvent(QDropEvent *e)
{
  if (!m_dropItem)
    return;
    
  if (!acceptDrag(e)) {
    e->acceptAction(false);
    return;
  }
  e->acceptAction();

  // Decode the data and try to init transfer
  KIO::MetaData meta;
  KURL::List urls;
  KURLDrag::decode(e, urls, meta);

  // Move the specified file(s)
  for (KURL::List::iterator i = urls.begin(); i != urls.end(); ++i) {
    KURL destUrl = static_cast<TreeViewItem*>(m_dropItem)->m_url;
    
    if (destUrl != (*i)) {
      if (destUrl.isLocalFile() != (*i).isLocalFile()) {
        // TODO Queue up a transfer
      } else {
        // Rename/move the file
        destUrl.addPath((*i).fileName());
        
        if ((*i).isLocalFile()) {
          KIO::move((*i), destUrl, false);
          
          // Reload the listing
          static_cast<View*>(parent()->parent())->reload();
        } else {
          static_cast<View*>(parent()->parent())->m_ftpClient->rename((*i), destUrl);
        }
      }
    }
  }
  
  // Invalidate the drop item
  m_dropItem = 0L;
}

}

}

#include "treeview.moc"
