//
//  kjots
//
//  Copyright (C) 1997 Christoph Neerfeld
//  Copyright (C) 2002, 2003 Aaron J. Seigo
//  Copyright (C) 2003 Stanislav Kljuhhin
//
//  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 WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
//

#include <qdir.h>
#include <qpainter.h>
#include <qpaintdevicemetrics.h>

#include <kapplication.h>
#include <kiconloader.h>
#include <kinputdialog.h>
#include <klocale.h>
#include <kmessagebox.h>
#include <kprogress.h>
#include <kstandarddirs.h>

#include <kio/job.h>
#include <assert.h>

#include "kjotsedit.h"
#include "KJotsMain.h"
#include "kjotsentry.h"

// helper functions for HTML output
QString prepForHTML(QString text)
{
    text.replace("<", "&lt;");
    text.replace(">", "&gt;");
    text.replace("\n", "<br>");
    return text;
}

QString htmlHeader(const QString& title)
{
    return "<html><title>" + title + "</title><body>";
}

QString htmlFooter()
{
    return "</body></html>";
}


//
// KJotsEntryBase
//

KJotsEntryBase::KJotsEntryBase(KListView* parent, QListViewItem* after=0)
    :KListViewItem(parent,after)
{

}

KJotsEntryBase::KJotsEntryBase(KListViewItem* parent, QListViewItem* after=0)
	:KListViewItem(parent,after)
{

}

void KJotsEntryBase::setSubject(const QString& subj)
{
    setText(0, subj);
}

void KJotsEntryBase::setText(int column, const QString& text)
{
    if (column == 0 && text.isEmpty())
        KListViewItem::setText(0, defaultSubject());
    else
        KListViewItem::setText(column, text);
}

//
// KJotsBook
//

KJotsBook::KJotsBook(KListView* parent, QString subj, QListViewItem* after)
    : KJotsEntryBase(parent, after),
      m_dirty(false),
      m_open(false),
      m_deleteFlag(false),
      m_pageID(0),
      m_saveInProgress(0),
      m_saveProgressDialog(0)
{
    KJotsEntryBase::setSubject(subj);
    m_beforeRename = subject();
    setExpandable(true);
    setPixmap(0, kapp->iconLoader()->loadIcon(QString("contents"),KIcon::Small));
}

KJotsBook::~KJotsBook()
{
    if (m_deleteFlag)
        annihilateBook();
}

bool KJotsBook::isBookFile(const QString& filename)
{
    QFile folder(filename);

    if ( !folder.exists() )
    {
        return false;
    }
    else if ( !folder.open(IO_ReadWrite) )
    {
        return false;
    }

    QTextStream st(static_cast<QIODevice*>(&folder));
    st.setEncoding( QTextStream::Locale );
    QString buf = st.readLine();

    if (buf.left(9) != "\\NewEntry")
    {
        return false;
    }

    return true;
}

bool KJotsBook::openBook()
{
    if (m_open)
    {
        return true;
    }

    listView()->setUpdatesEnabled(false);

    QDir dir = QDir(locateLocal("appdata",""));
    QString file_name = dir.absPath();
    file_name += '/';
    file_name += QFile::encodeName(subject());

    QFile folder(file_name);

    if ( !folder.exists() )
    {
        addPage();
        listView()->setUpdatesEnabled(true);
        m_open = true;
        return true;
    }
    else if ( !folder.open(IO_ReadWrite) )
    {
        // TODO: should check for IO_READ access and open read only if so
        addPage();
        listView()->setUpdatesEnabled(true);
        m_open = true;
        return false;
    }

    QTextStream st(static_cast<QIODevice*>(&folder));
    st.setEncoding( QTextStream::Locale );
    QString buf = st.readLine();

    if (buf.left(9) != "\\NewEntry")
    {
        addPage();
        listView()->setUpdatesEnabled(true);
        m_open = true;
        return false;
    }

    KJotsPage *entry = 0;
    QString body;

    bool quit=false;

    while (1)
    {
        if (buf.left(9) == "\\NewEntry")
        {
            if (entry)
            {
                entry->setBody(body);
            }

            body = QString::null;
            QString title = buf.mid(10, buf.length());

            // now catch the page id
            buf = st.readLine();
            int pageID = -1;
            if (buf.left(3) == "\\ID")
            {
                pageID = buf.mid(3, buf.length()).toInt();
                if (pageID > m_pageID)
                {
                    m_pageID = pageID;
                }
                buf = st.readLine();
            }

            if (pageID < 0)
            {
                pageID = nextID();
            }
            entry = addPage(title, entry, pageID);
        }

        int pos = 0;

        while ((pos = buf.find('\\',pos)) != -1)
            if (buf[++pos] == '\\')
                buf.remove(pos, 1 );

        body.append( buf + "\n");

        if (quit)
            break;

        buf = st.readLine();

        quit = st.eof();
    }

    entry->setBody(body);

    folder.close();

    listView()->setUpdatesEnabled(true);

    m_open = true;
    return true;
}

void KJotsBook::closeBook(bool saveOnly)
{
    if (!m_open)
        return;

    if (!saveOnly)
    {
        listView()->setUpdatesEnabled(false);
    }

    QDir dir = QDir(locateLocal("appdata",""));
    QString file_name = dir.absPath();
    file_name +=  '/' + subject();

    QFile folder(file_name);
    if (!firstChild() || !folder.open(IO_WriteOnly | IO_Truncate))
    {
        if (!saveOnly)
        {
            listView()->setUpdatesEnabled(true);
            m_open = false;
        }

        return;
    }

    KJotsPage* entry = dynamic_cast<KJotsPage*>(firstChild());
    QTextStream st((QIODevice *) &folder);
    st.setEncoding( QTextStream::Locale );

    while (entry)
    {
        st << "\\NewEntry " << entry->subject() << "\n";
        st << "\\ID " << entry->id() << "\n";
        QString buf = entry->body();
        st << buf.replace('\\', "\\\\");
        if( buf.right(1) != "\n" )
        {
            st << '\n';
        }

        if (!saveOnly)
        {
            KJotsPage* newEntry = dynamic_cast<KJotsPage*>(entry->nextSibling());
            delete entry;
            entry = newEntry;
        }
        else
            entry = dynamic_cast<KJotsPage*>(entry->nextSibling());
    }

    folder.close();
    if (!saveOnly)
    {
        listView()->setUpdatesEnabled(true);
        m_open = false;
    }

    if ( m_dirty ) m_dirty = false;
}

void KJotsBook::annihilateBook()
{
    QDir dir = QDir(locateLocal("appdata",""));
    dir.remove(subject());
}

void KJotsBook::rename()
{
    bool ok;
    QString name = KInputDialog::getText(i18n( "Rename Book" ),
                                         i18n( "Book name:" ),
                                         subject(), &ok, listView());
    if (ok)
    {
        setSubject(name);

        KJotsMain* m = dynamic_cast<KJotsMain*>(kapp->mainWidget());

        if (m)
            m->updateCaption();
    }
}

void KJotsBook::setSubject(const QString& newSubject)
{
    //Sanity check. Did they just press enter?
    if ( newSubject == m_beforeRename ) return;

    QDir dir = QDir(locateLocal("appdata",""));
    QString fileName = dir.absPath();
    fileName += '/' + QFile::encodeName(newSubject);

    if (QFile::exists(fileName))
    {
        KJotsEntryBase::setSubject(m_beforeRename);
        KMessageBox::sorry(listView(), i18n("A book named %1 already exists.").arg(newSubject), i18n("Rename Book"));
        return;
    }

    bool wasOpen = m_open;
    if (wasOpen)
    {
        closeBook();
    }

    if (!dir.rename(m_beforeRename, newSubject))
    {
        KJotsEntryBase::setSubject(m_beforeRename);
        KMessageBox::sorry(listView(), i18n("The name change failed. Please check the file permissions.\n\nFrom: %1\nTo: %2").arg(m_beforeRename, newSubject), i18n("Rename Book"));
        return;
    }

    KJotsEntryBase::setSubject(newSubject);

    if (wasOpen)
    {
        openBook();
    }

    m_beforeRename = text(0);
}

void KJotsBook::saveToFile(KURL url, bool plainText)
{
    if (url.isEmpty() || m_saveInProgress != 0)
        return;

    m_saveToPlainText = plainText;
    KIO::TransferJob* job = KIO::put(url, -1, true, false, false);

    if (!job)
    {
        return;
    }

    m_saveProgressDialog = new KProgressDialog(listView(), "bookSaveInProgress",
                                               i18n("Saving %1").arg(subject()),
                                               i18n("Saving the contents of %1 to %2")
                                                    .arg(subject(), url.prettyURL()),
                                               true);
    m_saveProgressDialog->progressBar()->setTotalSteps(childCount());
    m_saveProgressDialog->showCancelButton(false);
    m_saveProgressDialog->setAutoClose(true);

    connect(job, SIGNAL(dataReq(KIO::Job*, QByteArray&)), SLOT(saveDataReq(KIO::Job*, QByteArray&)));
    connect(job, SIGNAL(result( KIO::Job *)), SLOT(slotSaveResult( KIO::Job *)));
    m_saveInProgress = firstChild();
}

void KJotsBook::saveDataReq(KIO::Job* /* job */, QByteArray& data)
{
    static int pageNo = 1;
    if (!m_saveInProgress)
    {
        return;
    }

    QTextStream stream(data, IO_WriteOnly);
    if (m_saveInProgress == firstChild())
    {
        QString subjectText = prepForHTML(subject());
        stream << htmlHeader(subjectText);
        // first time in
        // FIXME: what if they save, it takes a long time, and they change the order of pages?
        //        RACY!
        QString TOC;
        QListViewItem* child = m_saveInProgress;
        int i = 1;
        while (child)
        {
            KJotsPage* page = dynamic_cast<KJotsPage*>(child);
            if (page)
            {
                QString text = prepForHTML(page->subject());
                TOC += "<li><a href=\"#p" + QString::number(i) + "\">" + text + "</a></li>";
                ++i;
            }
            child = child->nextSibling();
        }

        stream << "<h1>" + subjectText + "</h1>";
        stream << "<h3>" + i18n("Table of Contents") + "</h3><ul>" + TOC + "</ul><hr>";
    }

    KJotsPage* entry = 0;
    while (m_saveInProgress)
    {
        entry = dynamic_cast<KJotsPage*>(m_saveInProgress);

        if (entry)
        {
            break;
        }

        m_saveInProgress = m_saveInProgress->nextSibling();
    }

    if (entry)
    {
        stream << "<h3><a name=\"p" << QString::number(pageNo) << "\">"
               << prepForHTML(entry->subject()) << "</a></h3>";
        entry->write(stream, m_saveToPlainText);
        stream << "<hr>";
        m_saveInProgress = m_saveInProgress->nextSibling();
        ++pageNo;
    }

    if (!m_saveInProgress) // we are truly finished
    {
        stream << htmlFooter();
        pageNo = 1;
    }
}

void KJotsBook::slotSaveResult(KIO::Job *)
{
    m_saveInProgress = 0;
    delete m_saveProgressDialog;
    m_saveProgressDialog = 0;
    /* if (job->error() != 0) {} */
}

KJotsPage* KJotsBook::addPage(QString subject, QListViewItem* after, int id)
{
    if (id < 0)
    {
        id = nextID();
    }

    // append new pages
    if (after == 0)
    {
        QListViewItem* tmp = firstChild();
        while (tmp)
        {
            after = tmp;
            tmp = tmp->nextSibling();
        }
    }

    return new KJotsPage(this, subject, id, after);
}

void KJotsBook::print(QFont& defaultFont)
{
    KPrinter printer;
    printer.setDocName(subject());
    printer.setFullPage(false);
    printer.setCreator("KJots");

    if (!printer.setup(listView(), i18n("Print: %1").arg(subject())))
    {
        return;
    }

    QPainter painter(&printer);
    painter.setFont(defaultFont);
    QPaintDeviceMetrics metrics( &printer );
    int maxWidth = metrics.width();
    int maxHeight = metrics.height();
    int y = 0;
    int pageNo = 0;

    for (QListViewItem* page = firstChild(); page; page = page->nextSibling())
    {
        KJotsPage* subject = dynamic_cast<KJotsPage*>(page);

        if (!subject)
        {
            continue;
        }

        QString pageTitle = subject->subject().isEmpty() ?
                                     i18n("Page %1").arg(pageNo) :
                                     subject->subject();
        QRect r = painter.boundingRect(0, y,
                                       maxWidth, maxHeight,
                                       QPainter::ExpandTabs | QPainter::WordBreak | QPainter::AlignHCenter,
                                       pageTitle);
        y += 10;

        if ((y + r.height()) > maxHeight)
        {
            printer.newPage();
            y = 0;
        }
        else
        {
            r.moveBy(0, 10);
        }

        r.setLeft(0);
        r.setRight(maxWidth);
        painter.drawRect(r);
        painter.drawText(0, y, maxWidth, maxHeight - y,
                QPainter::ExpandTabs | QPainter::WordBreak | QPainter::AlignHCenter,
                pageTitle);
        y += r.height() + 15;
        y = subject->print(printer, painter, y);
        ++pageNo;
    }

    painter.end();
}

QString KJotsBook::defaultSubject()
{
    return i18n("Untitled Book");
}

bool KJotsBook::isDirty()
{
    //has an edit already happened?
    if ( m_dirty ) return true;

    //is an edit currently happening?
    if ( m_active )
    {
        KJotsPage* page = dynamic_cast<KJotsPage*>(firstChild());
        while (page)
        {
            if ( page->isDirty() )
            {
                return true;
            } else {
                page = dynamic_cast<KJotsPage*>(page->nextSibling());
            }
        }
    }

    return false;
}

//
// KJotsPage
//

KJotsPage::KJotsPage(KJotsBook* parent, QString subj, int id, QListViewItem* after)
    : KJotsEntryBase(parent,after),
      m_id(id),
      m_saveInProgress(false),
      m_editor(0)
{
    setSubject(subj);
    setPixmap(0, kapp->iconLoader()->loadIcon(QString("edit"), KIcon::Small));
}

QString KJotsPage::body()
{
    //if we're being edited we want the current text, not whatever we saved before.
    if ( m_editor && m_editor->edited() )
    {
        m_text = m_editor->text();
        m_editor->setEdited(false);
        KJotsBook* book = dynamic_cast<KJotsBook*>(KListViewItem::parent());
        if ( book ) book->setDirty();
    }

    return m_text;
}

void KJotsPage::setBody(const QString& text)
{
    assert ( !m_editor ); //m_editor should *never* be set.
    m_text = text;
}

void KJotsPage::rename()
{
    bool ok;

    QString name = KInputDialog::getText(i18n( "Rename Page" ),
                                         i18n( "Page title:" ),
                                         subject(), &ok, listView() );

    if (ok)
    {
        setSubject(name);

        KJotsMain* m = dynamic_cast<KJotsMain*>(kapp->mainWidget());

        if (m)
            m->updateCaption();
    }
}

void KJotsPage::write(QTextStream &stream, bool plainText)
{
    if (plainText)
    {
        QString line, buf;
        line.fill('#', subject().length() + 2);
        buf = body();
        if (buf.right(1) != "\n")
        {
            buf.append('\n');
        }

        stream << line << '\n'
            << "# " << subject() << '\n'
            << line << '\n'
            << buf
            << '\n';
    }
    else // html output
    {
        if (m_saveInProgress)
        {
             // we started the save ourselves, so write the html header
             QString subjectText = prepForHTML(subject());
             stream << htmlHeader(subjectText);
             stream << "<h1>" << subjectText << "</h1>";
        }

        stream << prepForHTML(body());

        if (m_saveInProgress)
        {
             // we started the save ourselves, so write the html header
             stream << htmlFooter();
        }

    }
}

// FIXME: break up into chunks in case of REAAAAALLY long pages?
void KJotsPage::saveToFile(KURL url, bool plainText)
{
    if (url.isEmpty() || m_saveInProgress)
        return;

    m_saveToPlainText = plainText;
    KIO::TransferJob* job = KIO::put(url, -1, true, false, false);
    if (!job)
    {
        return;
    }

    connect(job, SIGNAL(dataReq(KIO::Job*, QByteArray&)), SLOT(saveDataReq(KIO::Job*, QByteArray&)));
    connect(job, SIGNAL(result( KIO::Job *)), SLOT(slotSaveResult( KIO::Job *)));
    m_saveInProgress = true;
}

void KJotsPage::saveDataReq(KIO::Job* /* job */, QByteArray& data)
{
    if (!m_saveInProgress)
    {
        return;
    }

    QTextStream stream(data, IO_WriteOnly);
    write(stream, m_saveToPlainText);

    m_saveInProgress = false;
}

void KJotsPage::slotSaveResult(KIO::Job *)
{
    m_saveInProgress = false;
    /* if (job->error() != 0) {} */
}

void KJotsPage::print(QFont& defaultFont)
{
    KJotsEntryBase* book = dynamic_cast<KJotsEntryBase*>(KListViewItem::parent());

    QString docName = book->subject();
    if (!subject().isNull())
    {
        docName += ": " + subject();
    }

    KPrinter printer;
    printer.setDocName(docName);
    printer.setFullPage(false);
    printer.setCreator("KJots");
    int y = 0;

    if (printer.setup(listView(), i18n("Print: %1").arg(docName)))
    {
        QPainter painter( &printer );
        painter.setFont(defaultFont);
        y = print(printer, painter, y);
        painter.end();
    }
}

int KJotsPage::print(KPrinter& printer, QPainter& painter, int y)
{
    QPaintDeviceMetrics metrics( &printer );
    int maxWidth = metrics.width();
    int maxHeight = metrics.height();
    QStringList paragraphs = QStringList::split(QChar('\n'), body(), true);

    for (QStringList::iterator para = paragraphs.begin();
         para != paragraphs.end();
         ++para)
    {
        //Watch for blank lines inserted as spacers.
        if ( (*para).isNull() ) *para = " ";

        QRect r = painter.boundingRect(0, y,
                                       maxWidth, maxHeight,
                                       QPainter::ExpandTabs | QPainter::WordBreak,
                                       *para);

        if ((y + r.height()) > maxHeight)
        {
            printer.newPage();
            y = 0;
        }

        painter.drawText(0, y, maxWidth, maxHeight - y,
                QPainter::ExpandTabs | QPainter::WordBreak,
                *para);
        y += r.height();
    }

    return y;
}

QString KJotsPage::defaultSubject()
{
    return i18n("Page %1").arg(m_id);
}

void KJotsPage::setEditor( KJotsEdit *editor )
{
    KJotsBook* book = dynamic_cast<KJotsBook*>(KListViewItem::parent());

    //out with the old...
    if ( m_editor )
    {
        if ( m_editor->edited() )
        {
            m_text = m_editor->text();
            if (book) book->setDirty();
        }
    }

    if (book) book->setActive(false);
    m_editor = editor;

    //and in with the new
    if ( m_editor )
    {
        if ( book ) book->setActive(true);
        m_editor->setEdited(false);
    }

    return;
}

bool KJotsPage::isDirty()
{
    if ( m_editor )
    {
        if ( m_editor->edited() )
        {
            return true;
        }
    }

    return false;
}

/* ex: set tabstop=4 softtabstop=4 shiftwidth=4 expandtab: */

#include "kjotsentry.moc"
