/***************************************************************************
    copyright            : (C) 2005-2006 by Robby Stephenson
    email                : robby@periapsis.org
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of version 2 of the GNU General Public License as  *
 *   published by the Free Software Foundation;                            *
 *                                                                         *
 ***************************************************************************/

#include "filelistingimporter.h"
#include "../collections/filecatalog.h"
#include "../entry.h"
#include "../field.h"
#include "../latin1literal.h"
#include "../imagefactory.h"
#include "../tellico_utils.h"
#include "../tellico_kernel.h"
#include "../progressmanager.h"
#include "../core/netaccess.h"
#include "../tellico_debug.h"

#include <kapplication.h>
#include <kmountpoint.h>
#include <kio/job.h>
#include <kio/netaccess.h>

#include <tqcheckbox.h>
#include <tqvgroupbox.h>
#include <tqlayout.h>
#include <tqwhatsthis.h>
#include <tqfile.h>
#include <tqfileinfo.h>

#include <stdio.h>

namespace {
  static const int FILE_PREVIEW_SIZE = 128;
  // volume name starts at 16*2048+40 bytes into the header
  static const int VOLUME_NAME_POS = 32808;
  static const int VOLUME_NAME_SIZE = 32;
}

using Tellico::Import::FileListingImporter;

FileListingImporter::FileListingImporter(const KURL& url_) : Importer(url_), m_coll(0), m_widget(0),
    m_job(0), m_cancelled(false) {
  m_files.setAutoDelete(true);
}

bool FileListingImporter::canImport(int type) const {
  return type == Data::Collection::File;
}

Tellico::Data::CollPtr FileListingImporter::collection() {
  if(m_coll) {
    return m_coll;
  }

  ProgressItem& item = ProgressManager::self()->newProgressItem(this, i18n("Scanning files..."), true);
  item.setTotalSteps(100);
  connect(&item, TQT_SIGNAL(signalCancelled(ProgressItem*)), TQT_SLOT(slotCancel()));
  ProgressItem::Done done(this);

  // going to assume only one volume will ever be imported
  TQString volume = volumeName();

  m_job = m_recursive->isChecked()
          ? KIO::listRecursive(url(), true, false)
          : KIO::listDir(url(), true, false);
  connect(m_job, TQT_SIGNAL(entries(KIO::Job*, const KIO::UDSEntryList&)),
          TQT_SLOT(slotEntries(KIO::Job*, const KIO::UDSEntryList&)));

  if(!KIO::NetAccess::synchronousRun(m_job, Kernel::self()->widget()) || m_cancelled) {
    return 0;
  }

  const bool usePreview = m_filePreview->isChecked();

  const TQString title    = TQString::fromLatin1("title");
  const TQString url      = TQString::fromLatin1("url");
  const TQString desc     = TQString::fromLatin1("description");
  const TQString vol      = TQString::fromLatin1("volume");
  const TQString folder   = TQString::fromLatin1("folder");
  const TQString type     = TQString::fromLatin1("mimetype");
  const TQString size     = TQString::fromLatin1("size");
  const TQString perm     = TQString::fromLatin1("permissions");
  const TQString owner    = TQString::fromLatin1("owner");
  const TQString group    = TQString::fromLatin1("group");
  const TQString created  = TQString::fromLatin1("created");
  const TQString modified = TQString::fromLatin1("modified");
  const TQString metainfo = TQString::fromLatin1("metainfo");
  const TQString icon     = TQString::fromLatin1("icon");

  m_coll = new Data::FileCatalog(true);
  TQString tmp;
  const uint stepSize = TQMAX(1, m_files.count()/100);
  const bool showProgress = options() & ImportProgress;

  item.setTotalSteps(m_files.count());
  uint j = 0;
  for(KFileItemListIterator it(m_files); !m_cancelled && it.current(); ++it, ++j) {
    Data::EntryPtr entry = new Data::Entry(m_coll);

    const KURL u = it.current()->url();
    entry->setField(title,  u.fileName());
    entry->setField(url,    u.url());
    entry->setField(desc,   it.current()->mimeComment());
    entry->setField(vol,    volume);
    tmp = KURL::relativePath(this->url().path(), u.directory());
    // remove "./" from the string
    entry->setField(folder, tmp.right(tmp.length()-2));
    entry->setField(type,   it.current()->mimetype());
    entry->setField(size,   KIO::convertSize(it.current()->size()));
    entry->setField(perm,   it.current()->permissionsString());
    entry->setField(owner,  it.current()->user());
    entry->setField(group,  it.current()->group());

    time_t t = it.current()->time(KIO::UDS_CREATION_TIME);
    if(t > 0) {
      TQDateTime dt;
      dt.setTime_t(t);
      entry->setField(created, dt.toString(Qt::ISODate));
    }
    t = it.current()->time(KIO::UDS_MODIFICATION_TIME);
    if(t > 0) {
      TQDateTime dt;
      dt.setTime_t(t);
      entry->setField(modified, dt.toString(Qt::ISODate));
    }
    const KFileMetaInfo& meta = it.current()->metaInfo();
    if(meta.isValid() && !meta.isEmpty()) {
      const TQStringList keys = meta.supportedKeys();
      TQStringList strings;
      for(TQStringList::ConstIterator it2 = keys.begin(); it2 != keys.end(); ++it2) {
        KFileMetaInfoItem item = meta.item(*it2);
        if(item.isValid()) {
          TQString s = item.string();
          if(!s.isEmpty()) {
            strings << item.key() + "::" + s;
          }
        }
      }
      entry->setField(metainfo, strings.join(TQString::fromLatin1("; ")));
    }

    if(!m_cancelled && usePreview) {
      m_pixmap = NetAccess::filePreview(it.current(), FILE_PREVIEW_SIZE);
      if(m_pixmap.isNull()) {
        m_pixmap = it.current()->pixmap(0);
      }
    } else {
      m_pixmap = it.current()->pixmap(0);
    }

    if(!m_pixmap.isNull()) {
      // is png best option?
      TQString id = ImageFactory::addImage(m_pixmap, TQString::fromLatin1("PNG"));
      if(!id.isEmpty()) {
        entry->setField(icon, id);
      }
    }

    m_coll->addEntries(entry);

    if(showProgress && j%stepSize == 0) {
      ProgressManager::self()->setProgress(this, j);
      kapp->processEvents();
    }
  }

  if(m_cancelled) {
    m_coll = 0;
    return 0;
  }

  return m_coll;
}

TQWidget* FileListingImporter::widget(TQWidget* parent_, const char* name_) {
  if(m_widget) {
    return m_widget;
  }

  m_widget = new TQWidget(parent_, name_);
  TQVBoxLayout* l = new TQVBoxLayout(m_widget);

  TQVGroupBox* box = new TQVGroupBox(i18n("File Listing Options"), m_widget);

  m_recursive = new TQCheckBox(i18n("Recursive folder search"), box);
  TQWhatsThis::add(m_recursive, i18n("If checked, folders are recursively searched for all files."));
  // by default, make it checked
  m_recursive->setChecked(true);

  m_filePreview = new TQCheckBox(i18n("Generate file previews"), box);
  TQWhatsThis::add(m_filePreview, i18n("If checked, previews of the file contents are generated, which can slow down "
                                      "the folder listing."));
  // by default, make it no previews
  m_filePreview->setChecked(false);

  l->addWidget(box);
  l->addStretch(1);
  return m_widget;
}

void FileListingImporter::slotEntries(KIO::Job* job_, const KIO::UDSEntryList& list_) {
  if(m_cancelled) {
    job_->kill();
    m_job = 0;
    return;
  }

  for(KIO::UDSEntryList::ConstIterator it = list_.begin(); it != list_.end(); ++it) {
    KFileItem* item = new KFileItem(*it, url(), false, true);
    if(item->isFile()) {
      m_files.append(item);
    } else {
      delete item;
    }
  }
}

TQString FileListingImporter::volumeName() const {
  // this functions turns /media/cdrom into /dev/hdc, then reads 32 bytes after the 16 x 2048 header
  TQString volume;
  const KMountPoint::List mountPoints = KMountPoint::currentMountPoints(KMountPoint::NeedRealDeviceName);
  for(KMountPoint::List::ConstIterator it = mountPoints.begin(), end = mountPoints.end(); it != end; ++it) {
    // path() could be /media/cdrom
    // which could be the mount point of the device
    // I know it works for iso9660 (cdrom) and udf (dvd)
    if(url().path() == (*it)->mountPoint()
       && ((*it)->mountType() == Latin1Literal("iso9660")
           || (*it)->mountType() == Latin1Literal("udf"))) {
      volume = (*it)->mountPoint();
      if(!(*it)->realDeviceName().isEmpty()) {
        TQString devName = (*it)->realDeviceName();
        if(devName.endsWith(TQChar('/'))) {
          devName.truncate(devName.length()-1);
        }
        // TQFile can't do a sequential seek, and I don't want to do a 32808x loop on getch()
        FILE* dev = 0;
        if((dev = fopen(devName.latin1(), "rb")) != 0) {
          // returns 0 on success
          if(fseek(dev, VOLUME_NAME_POS, SEEK_SET) == 0) {
            char buf[VOLUME_NAME_SIZE];
            int ret = fread(buf, 1, VOLUME_NAME_SIZE, dev);
            if(ret == VOLUME_NAME_SIZE) {
              volume = TQString::fromLatin1(buf, VOLUME_NAME_SIZE).stripWhiteSpace();
            }
          } else {
            myDebug() << "FileListingImporter::volumeName() - can't seek " << devName << endl;
          }
          fclose(dev);
        } else {
          myDebug() << "FileListingImporter::volumeName() - can't read " << devName << endl;
        }
      }
      break;
    }
  }
  return volume;
}

void FileListingImporter::slotCancel() {
  m_cancelled = true;
  if(m_job) {
    m_job->kill();
  }
}

#include "filelistingimporter.moc"