/***************************************************************************
    copyright            : (C) 2003-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 "alexandriaimporter.h"
#include "../collections/bookcollection.h"
#include "../entry.h"
#include "../field.h"
#include "../latin1literal.h"
#include "../isbnvalidator.h"
#include "../imagefactory.h"
#include "../progressmanager.h"
#include "../tellico_debug.h"

#include <kcombobox.h>
#include <tdeapplication.h>
#include <kstringhandler.h>

#include <tqlayout.h>
#include <tqlabel.h>
#include <tqgroupbox.h>

using Tellico::Import::AlexandriaImporter;

bool AlexandriaImporter::canImport(int type) const {
  return type == Data::Collection::Book;
}

Tellico::Data::CollPtr AlexandriaImporter::collection() {
  if(!m_widget || m_library->count() == 0) {
    return 0;
  }

  m_coll = new Data::BookCollection(true);

  TQDir dataDir = m_libraryDir;
  dataDir.cd(m_library->currentText());
  dataDir.setFilter(TQDir::Files | TQDir::Readable | TQDir::NoSymLinks);

  const TQString title = TQString::fromLatin1("title");
  const TQString author = TQString::fromLatin1("author");
  const TQString year = TQString::fromLatin1("pub_year");
  const TQString binding = TQString::fromLatin1("binding");
  const TQString isbn = TQString::fromLatin1("isbn");
  const TQString pub = TQString::fromLatin1("publisher");
  const TQString rating = TQString::fromLatin1("rating");
  const TQString cover = TQString::fromLatin1("cover");
  const TQString comments = TQString::fromLatin1("comments");

  // start with yaml files
  dataDir.setNameFilter(TQString::fromLatin1("*.yaml"));
  const TQStringList files = dataDir.entryList();
  const uint numFiles = files.count();
  const uint stepSize = TQMAX(s_stepSize, numFiles/100);
  const bool showProgress = options() & ImportProgress;

  ProgressItem& item = ProgressManager::self()->newProgressItem(this, progressLabel(), true);
  item.setTotalSteps(numFiles);
  connect(&item, TQ_SIGNAL(signalCancelled(ProgressItem*)), TQ_SLOT(slotCancel()));
  ProgressItem::Done done(this);

  TQStringList covers;
  covers << TQString::fromLatin1(".cover")
         << TQString::fromLatin1("_medium.jpg")
         << TQString::fromLatin1("_small.jpg");

  TQTextStream ts;
  ts.setEncoding(TQTextStream::UnicodeUTF8); // YAML is always utf8?
  uint j = 0;
  for(TQStringList::ConstIterator it = files.begin(); !m_cancelled && it != files.end(); ++it, ++j) {
    TQFile file(dataDir.absFilePath(*it));
    if(!file.open(IO_ReadOnly)) {
      continue;
    }

    Data::EntryPtr entry = new Data::Entry(m_coll);

    bool readNextLine = true;
    ts.unsetDevice();
    ts.setDevice(&file);
    TQString line;
    while(!ts.atEnd()) {
      if(readNextLine) {
        line = ts.readLine();
      } else {
        readNextLine = true;
      }
      // skip the line that starts with ---
      if(line.isEmpty() || line.startsWith(TQString::fromLatin1("---"))) {
        continue;
      }
      if(line.endsWith(TQChar('\\'))) {
        line.truncate(line.length()-1); // remove last character
        line += ts.readLine();
      }

      cleanLine(line);
      TQString alexField = line.section(':', 0, 0);
      TQString alexValue = line.section(':', 1).stripWhiteSpace();
      clean(alexValue);

      // Alexandria uses "n/a for empty values, and it is translated
      // only thing we can do is check for english value and continue
      if(alexValue == Latin1Literal("n/a")) {
        continue;
      }

      if(alexField == Latin1Literal("authors")) {
        TQStringList authors;
        line = ts.readLine();
        TQRegExp begin(TQString::fromLatin1("^\\s*-\\s+"));
        while(!line.isNull() && line.find(begin) > -1) {
          line.remove(begin);
          authors += clean(line);
          line = ts.readLine();
        }
        entry->setField(author, authors.join(TQString::fromLatin1("; ")));
        // the next line has already been read
        readNextLine = false;

        // Alexandria calls the edition the binding
      } else if(alexField == Latin1Literal("edition")) {
        // special case if it's "Hardcover"
        if(alexValue.lower() == Latin1Literal("hardcover")) {
          alexValue = i18n("Hardback");
        }
        entry->setField(binding, alexValue);

      } else if(alexField == Latin1Literal("publishing_year")) {
        entry->setField(year, alexValue);

      } else if(alexField == Latin1Literal("isbn")) {
        const ISBNValidator val(0);
        val.fixup(alexValue);
        entry->setField(isbn, alexValue);

        // now find cover image
        KURL u;
        alexValue.remove('-');
        for(TQStringList::Iterator ext = covers.begin(); ext != covers.end(); ++ext) {
          u.setPath(dataDir.absFilePath(alexValue + *ext));
          if(!TQFile::exists(u.path())) {
            continue;
          }
          TQString id = ImageFactory::addImage(u, true);
          if(!id.isEmpty()) {
            entry->setField(cover, id);
            break;
          }
        }
      } else if(alexField == Latin1Literal("notes")) {
        entry->setField(comments, alexValue);

      // now try by name then title
      } else if(m_coll->fieldByName(alexField)) {
        entry->setField(alexField, alexValue);

      } else if(m_coll->fieldByTitle(alexField)) {
        entry->setField(m_coll->fieldByTitle(alexField), alexValue);
      }
    }
    m_coll->addEntries(entry);

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

  return m_coll;
}

TQWidget* AlexandriaImporter::widget(TQWidget* parent_, const char* name_/*=0*/) {
  if(m_widget) {
    return m_widget;
  }

  m_libraryDir = TQDir::home();
  m_libraryDir.setFilter(TQDir::Dirs | TQDir::Readable | TQDir::NoSymLinks);

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

  TQGroupBox* box = new TQGroupBox(2, TQt::Horizontal, i18n("Alexandria Options"), m_widget);
  TQLabel* label = new TQLabel(i18n("&Library:"), box);
  m_library = new KComboBox(box);
  label->setBuddy(m_library);

  // .alexandria might not exist
  if(m_libraryDir.cd(TQString::fromLatin1(".alexandria"))) {
    TQStringList dirs = m_libraryDir.entryList();
    dirs.remove(TQString::fromLatin1(".")); // why can't I tell TQDir not to include these? TQDir::Hidden doesn't work
    dirs.remove(TQString::fromLatin1(".."));
    m_library->insertStringList(dirs);
  }

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

TQString& AlexandriaImporter::cleanLine(TQString& str_) {
  static TQRegExp escRx(TQString::fromLatin1("\\\\x(\\w\\w)"), false);
  str_.remove(TQString::fromLatin1("\\r"));
  str_.replace(TQString::fromLatin1("\\n"), TQString::fromLatin1("\n"));
  str_.replace(TQString::fromLatin1("\\t"), TQString::fromLatin1("\t"));

  // YAML uses escape sequences like \xC3
  int pos = escRx.search(str_);
  int origPos = pos;
  TQCString bytes;
  while(pos > -1) {
    bool ok;
    char c = escRx.cap(1).toInt(&ok, 16);
    if(ok) {
      bytes += c;
    } else {
      bytes = TQCString();
      break;
    }
    pos = escRx.search(str_, pos+1);
  }
  if(!bytes.isEmpty()) {
    str_.replace(origPos, bytes.length()*4, TQString::fromUtf8(bytes.data()));
  }
  return str_;
}

TQString AlexandriaImporter::clean(TQString& str_) {
  const TQRegExp quote(TQString::fromLatin1("\\\\\"")); // equals \"
  if(str_.startsWith(TQChar('\'')) || str_.startsWith(TQChar('"'))) {
    str_.remove(0, 1);
  }
  if(str_.endsWith(TQChar('\'')) || str_.endsWith(TQChar('"'))) {
    str_.truncate(str_.length()-1);
  }
  // we ignore YAML tags, this is not actually a good parser, but will do for now
  str_.remove(TQRegExp(TQString::fromLatin1("^![^\\s]*\\s+")));
  return str_.replace(quote, TQChar('"'));
}

void AlexandriaImporter::slotCancel() {
  m_cancelled = true;
}

#include "alexandriaimporter.moc"
