/***************************************************************************
                          notes.cpp  -  description
                             -------------------
    begin                : sob lis 16 2002
    copyright            : (C) 2002-2004 by Micha³ Rudolf
    email                : mrudolf@kdewebdev.org
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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 "notes.h"
#include <tqstringlist.h>
#include <tqfile.h>
#include <tqdatetime.h>
#include <tqtextstream.h>
#include <tqtextcodec.h>
#include <tdelocale.h>
#include <tdelistview.h>
#include <tdeglobal.h>
#include <kiconloader.h>
#include <kmimetype.h>
#include <krun.h>
#include <kurl.h>
#include <tdelistview.h>

#ifdef HAVE_CONFIG_H
#include <config.h>
#else
#define VERSION "?"
#endif


void TNoteLink::save(TQTextStream& ts) const
{
   if (!link.isEmpty()) {
      ts << "\\Link " << link << "\n";
      if (!description.isEmpty())
         ts << "\\Descr " << description << "\n";
      }
}

TQString TNoteLink::text(int fmt) const
{
   if (fmt == LinkOnly || description.isEmpty()) return link;
   else if (fmt == DescriptionOnly) return description;
   else if (fmt == LinkDescription)
      return TQString("%1 (%2)").arg(link).arg(description);
   else return TQString("%1 (%2)").arg(description).arg(link);
}

TQPixmap TNoteLink::icon() const
{
   return TDEGlobal::iconLoader()->loadIcon(KMimeType::iconForURL(KURL(link)),
      TDEIcon::Small);
}

void TNoteLink::open() const
{
   if (!isLocalReference())
      new KRun(KURL(link), 0, false, true);
}

void TNoteLink::openWith() const
{
   if (!isLocalReference())
      KRun::displayOpenWithDialog(KURL(link));
}

bool TNoteLink::isLocalReference() const
{
   KURL url(link);
   return url.protocol() == "knowit";
}





TNotesCollection::TNotesCollection()
{
   Pixmaps[0] = TDEGlobal::iconLoader()->loadIcon("text-vnd.tde.ascii", TDEIcon::Small);
   Pixmaps[1] = TDEGlobal::iconLoader()->loadIcon("text-plain", TDEIcon::Small);
   Pixmaps[2] = TDEGlobal::iconLoader()->loadIcon("folder", TDEIcon::Small);
   Pixmaps[3] = TDEGlobal::iconLoader()->loadIcon("folder_txt", TDEIcon::Small);
   modified = false;
   autoUpdate = true;
}



TNotesCollection::~TNotesCollection()
{}

void TNotesCollection::addNote(TQListViewItem* item)
{
   insert(item, new TNote(item, this));
   TQListViewItem* parent = item->parent();
   if (parent) find(parent)->updateView();
   modified = true;
}

void TNotesCollection::addNote(TQListViewItem* item, const TQString& s)
{
   insert(item, new TNote(item, this, s));
   TQListViewItem* parent = item->parent();
   if (parent) find(parent)->updateView();
   modified = true;
}

void TNotesCollection::removeNote(TQListViewItem* item)
{
   TNote* note = find(item);
   if (!note) tqWarning("Internal error while removing note\n");
   else {
      TQListViewItem* parent = item->parent();
      for (TQListViewItemIterator it(item); it.current() &&
        (it.current() == item || it.current()->depth() > item->depth()); ++it)
         find(it.current())->destroy();
      delete item;
      if (parent) find(parent)->updateView();
      modified = true;
   }
}

bool TNotesCollection::changeNote(TQListViewItem* item, const TQString& s)
{
   TNote* changed = find(item);
   if (!changed) {
      tqWarning("Internal error while changing note\n");
      return false;
   }
   if (changed->change(s))
      return modified = true;
   else
      return false;
}

void TNotesCollection::clearNotes()
{
   setAutoDelete(true);
   clear();
   setAutoDelete(false);
}

const TQString& TNotesCollection::text(TQListViewItem* item)
{
   TNote* note = find(item);
   if (!note)
      tqFatal("Internal error while accessing note text\n");
   return note->text;
}

void TNotesCollection::updateNotesView()
{
   for (TQPtrDictIterator<TNote> it(*this); it.current(); ++it)
      it.current()->updateView();
}





TNote::TNote(TQListViewItem* i, TNotesCollection* coll) : text()
{
   collection = coll;
   item = i;
   updateView();
}

TNote::TNote(TQListViewItem* i, TNotesCollection* coll, const TQString& s) :
   text(s)
{
   collection = coll;
   item = i;
   updateView();
}

bool TNote::isEmpty()
{
   return !text.length();
}

bool TNote::contains(const TQString& sought, bool case_sensitive)
{
   return text.contains(sought, case_sensitive);
}


TNote::~TNote()
{
}

void TNote::destroy()
{
   collection->remove(item);
   delete this;
}

bool TNote::change(const TQString& s)
{
   if (text == s)
      return false;
   text = s;
   updateView();
   return true;
}

TNote::State TNote::state()
{
   if (text.stripWhiteSpace().isEmpty())
      return (item->childCount()) ? EmptyParent : Empty;
   else
      return (item->childCount()) ? TextParent : Text;
}

void TNote::updateView()
{
   if (collection->autoUpdate)
      item->setPixmap(0, collection->Pixmaps[state()]);
}

bool TNote::saveHTML(const KURL& fname, const TQString& origname, const TQString& style,
   int flags)
{
  TQFile file(fname.path());
  if (!file.open( IO_WriteOnly))
     return false;
  TQTextStream ts(&file);
  if (flags & UseUTF8) ts.setEncoding(TQTextStream::UnicodeUTF8);
  else ts.setEncoding(TQTextStream::Locale);
  ts << "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n"
     "<html>\n<head>\n   <title>" << i18n("KnowIt document") <<
     TQString("</title>\n   <meta http-equiv=\"Content-Type\" "
     "content=\"text/html; charset=%1\">\n").arg(ts.codec()->mimeName());
  if (!style.isEmpty())
     ts << "   <style type=\"text/css\">\n   " << style << "\n   </style>\n";
  ts << "</head>\n\n<body>\n";

  TQValueVector<int> Depths;
  Depths.append(0);
  if (flags & TOC) {
     ts << TQString("<h1>%1</h1>\n").arg(i18n("Table of contents"));
     saveHTMLTocEntry(ts, Depths, flags);
     ts << "\n\n";
     }

  Depths.clear();
  Depths.append(0);
  saveHTMLBuf(ts, Depths, flags);
  
  TQString epilog = i18n("Exported from %1 by KnowIt %2, %3.");
  ts << "<br><br><br><small>" << epilog.arg(origname)
     .arg(VERSION)
     .arg(TQDateTime::currentDateTime().toString()) << "</small>\n\n";
  ts << "</body>\n</html>\n\n";
  return true;
}

bool TNote::saveHTMLBuf(TQTextStream& ts, TQValueVector<int>& depths,
  int flags)
{
   int hlevel = depths.count();
   depths.last() = depths.last()+1;
   if (hlevel < 1) hlevel = 1;
   else if (hlevel > 3) hlevel = 3;
   TQString id, number;
   for (uint i=0; i<depths.count(); i++)
         id += TQString("%1.").arg(depths[i]);
   if (flags & Enumerate)
      number = id + " ";
   
   ts << TQString("<h%1 id=\"S%2\">%3%4</h%5>").arg(hlevel).arg(id).arg(number)
      .arg(item->text(0)).arg(hlevel);
   TQString htmltext = text;
   int begin = htmltext.find("<body");
   if (begin >= 0) begin = htmltext.find(">", begin);
   int end = htmltext.find("</body>");
   if (begin < 0) begin = 0;
   if (end <= begin) end = htmltext.length();
   ts << htmltext.mid(begin+1, end-begin-1);

   /* save links */
   for (TQValueList<TNoteLink>::Iterator LinkList = links.begin();
      LinkList != links.end(); ++LinkList)
      ts << TQString("<a href=\"%1\">%2</a><br>\n").arg((*LinkList).link)
         .arg((*LinkList).text(TNoteLink::DescriptionOnly));

   /* save rule */
   if (flags & AddRule) ts << "<hr size=\"1\" noshade>\n\n";
   else ts << "\n\n";

   /* save children */
   if ((SaveSubnotes | SaveAll) & flags && item->childCount()) {
      depths.append(0);
      collection->find(item->firstChild())->saveHTMLBuf(ts, depths, flags);
      }
   if (SaveAll & flags && item->nextSibling())
      collection->find(item->nextSibling())->saveHTMLBuf(ts, depths, flags);

   if (!item->nextSibling()) depths.pop_back();
   return true;
}

bool TNote::saveHTMLTocEntry(TQTextStream& ts, TQValueVector<int>& depths, int flags)
{
   TQString space;
   space.fill(' ', depths.count());
   depths.last() = depths.last()+1;
   if (depths.last() == 1) {
      ts << space;
      if (depths.count() != 1) ts << "<dd>";
      ts << "<dl>\n";
      }

   TQString id, number;
   for (uint i=0; i<depths.count(); i++)
         id += TQString("%1.").arg(depths[i]);
   if (flags & Enumerate) number = id + " ";

   ts << space;
   ts << TQString("<dt><a href=\"#S%1\">%2%3</a></dt>\n").arg(id).arg(number).
      arg(item->text(0));
   if ((SaveSubnotes | SaveAll) & flags && item->childCount()) {
      depths.append(0);
      collection->find(item->firstChild())->saveHTMLTocEntry(ts, depths, flags);
      }
   if (SaveAll & flags && item->nextSibling())
      collection->find(item->nextSibling())->saveHTMLTocEntry(ts, depths, flags);
   if (!item->nextSibling()) {
      depths.pop_back();
      ts << space;
      ts << "</dl>";
      if (depths.count()) ts << "</dd>";
      ts << "\n";
      }
   return true;
}

void TNote::addLink(const TQString& s)
{
   links.append(TNoteLink(s));
   collection->modified = true;
}

void TNote::addLink(const TNoteLink& l)
{
   links.append(l);
   collection->modified = true;
}

void TNote::removeLink(int i)
{
   links.remove(links[i]);
   collection->modified = true;
}

void TNote::modifyLink(int i, const TQString& s)
{
   links[i].link = s;
   collection->modified = true;
}

void TNote::modifyLink(int i, TNoteLink& l)
{
   if (l != links[i])
      collection->modified = true;
   links[i] = l;
}

void TNote::modifyLinkDescription(int i, const TQString& s)
{
   links[i].description = s;
   collection->modified = true;
}

int TNote::linkCount() const
{
   return links.count();
}

const TNoteLink& TNote::link(int i) const
{
   return links[i];
}


void TNote::save(TQTextStream& ts, bool current)
{
   const TQString header = "\\NewEntry %1 %2\n";
   const TQString activeheader = "\\CurrentEntry %1 %2\n";

   if (current)
      ts << activeheader.arg(item->depth()).arg(item->text(0));
   else
      ts << header.arg(item->depth()).arg(item->text(0));
   for (TQValueList<TNoteLink>::Iterator LinkList = links.begin();
        LinkList != links.end(); ++LinkList)
        (*LinkList).save(ts);
   ts << text;
   if (!text.isEmpty())
      ts << '\n';
   ts << '\n';
}

void TNote::open(TQTextStream& ts)
{
   TQString s;
   text = "";
   links.clear();

   while (true) {
      s = ts.readLine();
      if (s.isNull() || s.isEmpty())
         break;
      if (s.left(6) == "\\Link ")
         addLink(s.mid(6));
      else if (s.left(7) == "\\Descr " && links.count()) 
         modifyLinkDescription(links.count()-1, s.mid(7));
      else {
         if (text.length())
            text += '\n';
         text += s;
      }
   }
}
