/***************************************************************************

                         i/o part of kvoctrain

    -----------------------------------------------------------------------

    begin          : Thu Mar 11 20:50:53 MET 1999

    copyright      : (C) 1999-2001 Ewald Arnold <kvoctrain@ewald-arnold.de>
                     (C) 2001 The KDE-EDU team
                     (C) 2004-2005 Peter Hedlund <peter.hedlund@kdemail.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.                                   *
 *                                                                         *
 ***************************************************************************/

#include <unistd.h>

#include <tqtimer.h>

#include <tdefiledialog.h>
#include <kcombobox.h>
#include <kprogress.h>
#include <tderecentdocument.h>
#include <kstatusbar.h>
#include <tdelocale.h>
#include <kstandarddirs.h>
#include <kdebug.h>
#include <tdenewstuff/downloaddialog.h>

#include "kvoctrain.h"
#include "UsageManager.h"
#include "common-dialogs/ProgressDlg.h"
#include <prefs.h>

static const char *PATTERN_ALL =  I18N_NOOP("*|All Files (*)\n");
static const char *PATTERN_ML  =  I18N_NOOP("*.kvtml|KVocTrain Markup (*.kvtml)\n");
static const char *PATTERN_LEX =  I18N_NOOP("*.lex|Vocabulary Trainer 5.0 (*.lex)\n");
static const char *PATTERN_VL  =  I18N_NOOP("*.vl|KVoclearn (*.vl)\n");
static const char *PATTERN_TQVO =  I18N_NOOP("*.qvo|TQVocab (*.qvo)\n");
static const char *PATTERN_VOC =  I18N_NOOP("*.voc|Vokabeltrainer (*.voc)\n");
static const char *PATTERN_CSV =  I18N_NOOP("*.csv|Text (*.csv)\n");
static const char *PATTERN_VCB =  I18N_NOOP("*.vocab|Vocabbox (*.vocab)\n");

// we can read these
#define FILTER_RPATTERN  i18n(PATTERN_ML)+i18n(PATTERN_VCB)+i18n(PATTERN_VOC)+i18n(PATTERN_CSV)+i18n(PATTERN_LEX)+i18n(PATTERN_ALL)

// we can write these
#define FILTER_WPATTERN  i18n(PATTERN_ML)+i18n(PATTERN_VCB)+i18n(PATTERN_CSV)+i18n(PATTERN_LEX)+i18n(PATTERN_ALL)


void kvoctrainApp::slotTimeOutBackup()
{
  if (Prefs::autoBackup() && doc && doc->isModified() ) {
    slotStatusMsg(i18n("Autobackup in progress"));
    slotFileSave();
  }
  if (Prefs::backupTime() > 0)
    btimer->start(Prefs::backupTime() * 60 * 1000, true);
  slotStatusMsg(IDS_DEFAULT);
}


bool kvoctrainApp::queryClose()
{
   bool erg = queryExit();
   if (erg)
     doc->setModified(false);  // avoid double query on exit via system menu
   return erg;
}


bool kvoctrainApp::queryExit()
{
  saveOptions();
  if (!doc || !doc->isModified() ) return true;

  bool save = (Prefs::autoSave()); //save without asking

  if (!save)
  {
     int exit = KMessageBox::warningYesNoCancel(this,
               i18n("Vocabulary is modified.\n\nSave file before exit?\n"),
               kapp->makeStdCaption(""),
               KStdGuiItem::save(), KStdGuiItem::discard());
     if (exit==KMessageBox::Yes) {
       save = true;   // save and exit
     }
     else if (exit == KMessageBox::No) {
       save = false;  // dont save but exit
     }
     else {
       return false;  // continue work
     }
  }

  if (save) {
    if (!doc->URL().isEmpty())
      slotFileSave();       // save and exit
    if (doc->isModified())
    {
      // Error while saving or no name
      slotFileSaveAs();
    }
  }
  return true;
}


void kvoctrainApp::slotFileQuit()
{
  ///////////////////////////////////////////////////////////////////
  // exits the Application
  if(queryExit())
    {
      doc->setModified(false);  // Make sure not to bother about saving again.
      kapp->quit();
    }
  else
    slotStatusMsg(IDS_DEFAULT);
}


void kvoctrainApp::slotProgress(kvoctrainDoc *curr_doc, int percent)
{
  if (pdlg != 0)
    pdlg->setValue (curr_doc, percent);
  else if (pbar != 0)
    pbar->setValue (percent);
  kapp->processEvents();
}


void kvoctrainApp::slotFileOpenRecent(const KURL& url)
{
  slotStatusMsg(i18n("Opening file..."));
  if (queryExit() && fileOpenRecent->items().count() > 0)
  {
    fileOpenRecent->setCurrentItem(-1);
    loadfileFromPath(url);
  }
  slotStatusMsg(IDS_DEFAULT);
}


void kvoctrainApp::slotFileNew()
{
  slotStatusMsg(i18n("Creating new file..."));

  if (queryExit() ) {
    view->setView (0, langset);
    delete doc;
    TQString name = "";
    doc = new kvoctrainDoc (this, 0 /*KURL(name)*/);
    loadDocProps(doc);
    if (doc->numLangs() == 0) {
      TQString l = "en";
      doc->appendLang(l);
    }
    view->setView(doc, langset);
    view->getTable()->setFont(Prefs::tableFont());
    view->adjustContent();
    connect (doc, TQ_SIGNAL (docModified(bool)), this, TQ_SLOT(slotModifiedDoc(bool)));
    doc->setModified(false);
  }
  slotStatusMsg(IDS_DEFAULT);
}


void kvoctrainApp::slotFileOpen()
{
  slotStatusMsg(i18n("Opening file..."));

  if (queryExit() ) {
    TQString s;
    KURL url = KFileDialog::getOpenURL(TQString(), FILTER_RPATTERN, parentWidget(), i18n("Open Vocabulary File"));
    loadfileFromPath(url, true);
  }
  slotStatusMsg(IDS_DEFAULT);
}


void kvoctrainApp::loadfileFromPath(const KURL & url, bool addRecent)
{
    if (!url.path().isEmpty())
    {
      view->setView(0, langset);
      delete doc;
      doc = 0;

      TQString format = i18n("Loading %1");
      TQString msg = format.arg(url.path());

      slotStatusMsg(msg);
      prepareProgressBar();
      doc = new kvoctrainDoc (this, url);
      removeProgressBar();
      loadDocProps(doc);
      view->setView(doc, langset);
      view->getTable()->setFont(Prefs::tableFont());
      view->adjustContent();
      if (addRecent)
        fileOpenRecent->addURL(url) /*addRecentFile (url.path())*/;
      connect (doc, TQ_SIGNAL (docModified(bool)), this, TQ_SLOT(slotModifiedDoc(bool)));
      doc->setModified(false);
    }
}

void kvoctrainApp::slotFileOpenExample()
{
  slotStatusMsg(i18n("Opening example file..."));

  if (queryExit() ) {
    TQString s;
    s = locate("data",  "kvoctrain/examples/");
    KURL url = KFileDialog::getOpenURL(s, FILTER_RPATTERN, parentWidget(), i18n("Open Example Vocabulary File"));
    loadfileFromPath(url, false);
    if (doc)
       doc->URL().setFileName(TQString());
  }
  slotStatusMsg(IDS_DEFAULT);
}


void kvoctrainApp::slotGHNS()
{
  if (!m_newStuff)
    m_newStuff = new KVTNewStuff(this);
  m_newStuff->download();
}


void kvoctrainApp::slotFileMerge()
{
  slotStatusMsg(i18n("Merging file..."));

  TQString s;
  KURL url = KFileDialog::getOpenURL(TQString(), FILTER_RPATTERN, parentWidget(), i18n("Merge Vocabulary File"));

  if (!url.isEmpty() ) {

    TQString format = i18n("Loading %1");
    TQString msg = format.arg(url.path());

    slotStatusMsg(msg);
    prepareProgressBar();
    kvoctrainDoc *new_doc = new kvoctrainDoc (this, url);
    connect (new_doc, TQ_SIGNAL (docModified(bool)), this, TQ_SLOT(slotModifiedDoc(bool)));
    doc->setModified(false);
    removeProgressBar();

    vector<TQString> old_names = doc->getLessonDescr();
    vector<TQString> new_names = new_doc->getLessonDescr();

    vector<TQString> old_types = doc->getTypeDescr();
    vector<TQString> new_types = new_doc->getTypeDescr();

    vector<TQString> old_tenses = doc->getTenseDescr();
    vector<TQString> new_tenses = new_doc->getTenseDescr();

    vector<int> old_in_query = doc->getLessonsInQuery();
    vector<int> new_in_query = new_doc->getLessonsInQuery();

    vector<TQString> old_usages = doc->getUsageDescr();
    vector<TQString> new_usages = new_doc->getUsageDescr();

    format = i18n("Merging %1");
    msg = format.arg(url.path());
    slotStatusMsg(msg);

    TQApplication::setOverrideCursor( waitCursor );

    int lesson_offset = lessons->count()-1;
    for (int i = 0; i < (int) new_names.size(); i++) {
      lessons->insertItem (new_names[i]);
      old_names.push_back(new_names[i]);
    }
    doc->setLessonDescr(old_names);

    for (int i = 0; i < (int) new_in_query.size(); i++)
      old_in_query.push_back(new_in_query[i]+lesson_offset);
    doc->setLessonsInQuery(old_in_query);
    querymanager.setLessonItems(old_in_query);

    int types_offset = old_types.size();
    for (int i = 0; i < (int) new_types.size(); i++) {
      old_types.push_back(new_types[i]);
    }
    doc->setTypeDescr(old_types);
    QueryManager::setTypeNames(old_types);


    int tenses_offset = old_tenses.size();
    for (int i = 0; i < (int) new_tenses.size(); i++) {
      old_tenses.push_back(new_tenses[i]);
    }
    doc->setTenseDescr(old_tenses);
    Conjugation::setTenseNames(old_tenses);


    int usages_offset = old_usages.size();
    for (int i = 0; i < (int) new_usages.size(); i++) {
      old_usages.push_back(new_usages[i]);
    }
    doc->setUsageDescr(old_usages);
    UsageManager::setUsageNames(old_usages);


    bool equal = true;
    if (doc->getOriginalIdent() != new_doc->getOriginalIdent())
      equal = false;
    for (int i = 1; i < doc->numLangs(); i++)
      if (doc->getIdent(i) != new_doc->getIdent(i))
        equal = false;

    if (equal) {   // easy way: same language codes, just append

      for (int i = 0; i < new_doc->numEntries(); i++) {
        kvoctrainExpr *expr = new_doc->getEntry(i);

        expr->setLesson(expr->getLesson()+lesson_offset);

        for (int lang = 0; lang <= (int) expr->numTranslations(); lang++) {
          TQString t = expr->getType (lang);
          // adjust type offset
          if (!t.isEmpty() && t.left(1) == TQM_USER_TYPE) {
            TQString t2;
            t.remove (0, 1);
            t2.setNum (t.toInt()+types_offset);
            t2.insert (0, TQM_USER_TYPE);
            expr->setType (lang, t2);
          }

          t = expr->getUsageLabel (lang);
          // adjust usage offset
          TQString tg;
          if (!t.isEmpty() ) {
            TQString t2;
            while (t.left(strlen(UL_USER_USAGE)) == UL_USER_USAGE) {
              TQString n;
              t.remove (0, 1);
              int next;
              if ((next = t.find(UL_USAGE_DIV)) >= 0) {
                n = t.left(next);
                t.remove (0, next+1);
              }
              else {
                n = t;
                t = "";
              }

              t2.setNum (n.toInt()+usages_offset);
              t2.insert (0, UL_USER_USAGE);
              if (tg.length() == 0)
                tg = t2;
              else
                tg += UL_USAGE_DIV + t2;
            }

            if (tg.length() == 0)
              tg = t;
            else if (t.length() != 0)
              tg += UL_USAGE_DIV + t;

            expr->setUsageLabel (lang, tg);
          }

          Conjugation conj = expr->getConjugation(lang);
          bool condirty = false;
          for (int ci = 0; ci < conj.numEntries(); ci++) {
            t = conj.getType(ci);
            if (!t.isEmpty() && t.left(1) == UL_USER_TENSE) {
              t.remove (0, strlen(UL_USER_TENSE));
              TQString t2;
              t2.setNum (t.toInt()+tenses_offset);
              t2.insert (0, UL_USER_TENSE);
              conj.setType(ci, t2);
              condirty = true;
            }
            if (condirty)
              expr->setConjugation(lang, conj);
          }
        }

        doc->appendEntry (expr);
      }
      doc->setModified();
    }
    else { // hard way: move entries around, most attributes get lost
      vector<int> move_matrix;

      vector <bool> cs_equal;
      for (int i = 0; i < TQMAX (doc->numLangs(), new_doc->numLangs()); i++)
        cs_equal.push_back (false);

      move_matrix.push_back(new_doc->findIdent(doc->getOriginalIdent()));
      for (int i = 1; i < doc->numLangs(); i++)
        move_matrix.push_back(new_doc->findIdent(doc->getIdent(i)));

      for (int j = 0; j < new_doc->numEntries(); j++) {
        kvoctrainExpr new_expr;
        kvoctrainExpr *expr = new_doc->getEntry(j);
        new_expr.setLesson(expr->getLesson()+lesson_offset);

        for (int i = 0; i < (int) move_matrix.size(); i++) {
          int lpos = move_matrix[i];
          if (lpos >= 0) {

            if (lpos == 0)
              s = expr->getOriginal();
            else
              s = expr->getTranslation(lpos);

            if (!cs_equal[lpos]) {
              cs_equal[lpos] = true;
              TQString id = lpos == 0 ? doc->getOriginalIdent()
                                     : doc->getIdent(lpos);
            }

            if (i == 0)
              new_expr.setOriginal(s);
            else
              new_expr.setTranslation(i, s);
            TQString r = expr->getRemark(lpos);
            new_expr.setRemark (i, r);

            TQString t = expr->getType(lpos);
            if (!t.isEmpty() && t.left(1) == TQM_USER_TYPE) {
              TQString t2;
              t.remove (0, 1);
              t2.setNum (t.toInt()+types_offset);
              t2.insert (0, TQM_USER_TYPE);
              new_expr.setType (i, t2);
            }

            t = expr->getUsageLabel(lpos);
            if (!t.isEmpty() && t.left(1) == TQM_USER_TYPE) {
              TQString t2;
              t.remove (0, 1);
              t2.setNum (t.toInt()+usages_offset);
              t2.insert (0, TQM_USER_TYPE);
              new_expr.setUsageLabel (i, t2);
            }

            Conjugation conj = expr->getConjugation(lpos);
            for (int ci = 0; ci < conj.numEntries(); ci++) {
              t = conj.getType(ci);
              if (!t.isEmpty() && t.left(1) == TQM_USER_TYPE) {
                t.remove (0, strlen(TQM_USER_TYPE));
                TQString t2;
                t2.setNum (t.toInt()+tenses_offset);
                t2.insert (0, TQM_USER_TYPE);
                conj.setType(ci, t2);
              }
            }

          }
        }
        // only append if entries are used
        bool used = !new_expr.getOriginal().isEmpty();
        for (int i = 1; i < (int) doc->numLangs(); i++)
          if (!new_expr.getTranslation(i).isEmpty())
            used = true;

        if (used) {
          doc->appendEntry(&new_expr);
          doc->setModified();
        }
      }
    }
    delete (new_doc);
    fileOpenRecent->addURL(url); // addRecentFile (url.path());
  }

  view->setView(doc, langset);
  view->getTable()->setFont(Prefs::tableFont());
  view->adjustContent();
  TQApplication::restoreOverrideCursor();
  slotStatusMsg(IDS_DEFAULT);
}


void kvoctrainApp::slotFileSave()
{
  if (entryDlg != 0)
    commitEntryDlg(false);

  if (doc->URL().fileName() == i18n("Untitled") ) {
    slotFileSaveAs();
    return;
  }

  TQString format = i18n("Saving %1");
  TQString msg = format.arg(doc->URL().path());
  slotStatusMsg(msg);

  // remove previous backup
  TQFile::remove(TQFile::encodeName(doc->URL().path()+"~"));
  ::rename (TQFile::encodeName(doc->URL().path()),
            TQFile::encodeName(doc->URL().path()+"~"));

  prepareProgressBar();
  saveDocProps(doc);
  doc->saveAs(this, doc->URL(), doc->getTitle(), kvoctrainDoc::automatic);
  fileOpenRecent->addURL(doc->URL());
  removeProgressBar();

  slotStatusMsg(IDS_DEFAULT);
}


void kvoctrainApp::fillLessonBox(kvoctrainDoc *the_doc)
{
  lessons->clear();
  lessons->insertItem (the_doc->getLessonDescr(0));
  vector<TQString> names = the_doc->getLessonDescr();
  for (int i = 0; i < (int) names.size(); i++)
    lessons->insertItem (names[i]);
  act_lesson = the_doc->getCurrentLesson();
  if (act_lesson > lessons->count() ) {
    act_lesson = 0;
    the_doc->setCurrentLesson(act_lesson);
  }
  lessons->setCurrentItem (act_lesson);
}


void kvoctrainApp::loadDocProps(kvoctrainDoc *the_doc)
{
  fillLessonBox(the_doc);

  random_expr1.clear();
  random_expr2.clear();
  queryList.clear();
  the_doc->getQueryLang (act_query_org, act_query_trans);
  if (!act_query_org.isEmpty() && !act_query_trans.isEmpty() ) {
    for (int i = 0; i < the_doc->numEntries(); i++)
    {
      kvoctrainExpr *entry = the_doc->getEntry(i);
      if (entry->isInQuery())
      {
         int less = entry->getLesson();
         for (int l = (int) queryList.size(); l <= less; l++) {
            vector<QueryEntryRef> ref_vec;
            queryList.push_back(ref_vec);
         }
         QueryEntryRef ref(entry, i);
         queryList[less].push_back(ref);
      }
    }
  }

  QueryManager::setTypeNames (doc->getTypeDescr() );
  UsageManager::setUsageNames (doc->getUsageDescr() );
  Conjugation::setTenseNames (doc->getTenseDescr() );

  querymanager.setLessonItems(doc->getLessonsInQuery() );

  // remove empty elements
  for (int i = (int) queryList.size()-1; i >= 0; i--)
    if (queryList[i].size() == 0) {
      queryList.erase(queryList.begin() + i);
    }

  query_cycle = 1;
  query_startnum = 0;
  for (int i = 0; i < (int) queryList.size(); i++)
    query_startnum += queryList[i].size();
  query_num = query_startnum;
}


void kvoctrainApp::saveDocProps(kvoctrainDoc *the_doc)
{
  the_doc->setQueryLang (act_query_org, act_query_trans);
}


void kvoctrainApp::slotFileSaveAs()
{
  slotStatusMsg(i18n("Saving file under new filename..."));

  if (entryDlg != 0)
    commitEntryDlg(false);

  KURL url = KFileDialog::getSaveURL(TQString(), FILTER_WPATTERN, parentWidget(), i18n("Save Vocabulary As"));

  if (!url.isEmpty() ) {
    TQFileInfo fileinfo(url.path());
    if (fileinfo.exists() && KMessageBox::warningContinueCancel(0,
       i18n("<qt>The file<br><b>%1</b><br>already exists. Do you want to overwrite it?</qt>")
       .arg(url.path()),TQString(),i18n("Overwrite")) == KMessageBox::Cancel)
    {
    // do nothing
    }
    else

    if (doc) {
      TQString format = i18n("Saving %1");
      TQString msg = format.arg(url.path());
      slotStatusMsg(msg);

      TQFile::remove(TQFile::encodeName(url.path()+"~"));         // remove previous backup
      ::rename (TQFile::encodeName(url.path()), TQFile::encodeName(TQString(url.path()+"~")));
      saveDocProps(doc);

      prepareProgressBar();
      doc->saveAs(this, url, doc->getTitle(), kvoctrainDoc::automatic);
      fileOpenRecent->addURL(doc->URL());
      removeProgressBar();
    }
  }
  slotStatusMsg(IDS_DEFAULT);
}


void kvoctrainApp::slotSaveSelection ()
{
  if (entryDlg != 0)
    commitEntryDlg(false);

  slotStatusMsg(i18n("Saving selected area under new filename..."));
  TQString save_separator = Prefs::separator();
  Prefs::setSeparator("\t");
  kvoctrainDoc seldoc(this, "");
  // transfer most important parts
  seldoc.appendLang(doc->getOriginalIdent());
  for (int i = 1; i < doc->numLangs(); i++)
    seldoc.appendLang(doc->getIdent(i));
  seldoc.setAuthor(doc->getAuthor());
  seldoc.setLessonDescr(doc->getLessonDescr());
  seldoc.setTypeDescr(doc->getTypeDescr());

  for (int i = doc->numEntries()-1; i >= 0; i--)
    if (doc->getEntry(i)->isInQuery() )
      seldoc.appendEntry(doc->getEntry(i));

  KURL url = KFileDialog::getSaveURL(TQString(), FILTER_WPATTERN, parentWidget(), i18n("Save Vocabulary As"));

  if (!url.isEmpty() )
  {
    TQFileInfo fileinfo(url.path());
    if (fileinfo.exists() && KMessageBox::warningContinueCancel(0,
       i18n("<qt>The file<br><b>%1</b><br>already exists. Do you want to overwrite it?</qt>")
       .arg(url.path()),TQString(),i18n("Overwrite")) == KMessageBox::Cancel)
    {
    // do nothing
    }
    else
    {
      TQString format = i18n("Saving %1");
      TQString msg = format.arg(url.path());
      slotStatusMsg(msg);

      TQFile::remove(url.path()+"~");         // remove previous backup
      // FIXME: check error
      ::rename (TQFile::encodeName(url.path()), TQFile::encodeName(url.path()+"~"));
      saveDocProps(&seldoc);

      prepareProgressBar();
      seldoc.saveAs(this, url, i18n ("Part of: ") + doc->getTitle(), kvoctrainDoc::automatic);
      removeProgressBar();
    }
  }
  Prefs::setSeparator(save_separator);
  slotStatusMsg(IDS_DEFAULT);
}


void kvoctrainApp::prepareProgressBar ()
{
    statusBar()->clear ();
    pbar = new KProgress (statusBar());
    pbar->setFrameStyle( TQFrame::Panel | TQFrame::Sunken );
    statusBar()->addWidget(pbar, 150, true);
    pbar->show();
}


void kvoctrainApp::removeProgressBar ()
{
    statusBar()->clear ();
    statusBar()->removeWidget(pbar);
    delete pbar;
    pbar = 0;
    delete pdlg;
    pdlg = 0;
}
