/***************************************************************************
 *   Copyright (C) 2017 by Julian Wolff <wolff@julianwolff.de>             *
 *                                                                         *
 *   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 "converter.h"

#include "generator_md.h"

#include <KLocalizedString>

#include <QDir>
#include <QTextCursor>
#include <QTextDocument>
#include <QTextFrame>
#include <QTextStream>

#include <core/action.h>

#include "debug_md.h"

extern "C" {
#include <mkdio.h>
}

// older versions of discount might not have these flags.
// defining them to 0 allows us to convert without them
#ifndef MKD_FENCEDCODE
#define MKD_FENCEDCODE 0
#endif

#ifndef MKD_GITHUBTAGS
#define MKD_GITHUBTAGS 0
#endif

#ifndef MKD_AUTOLINK
#define MKD_AUTOLINK 0
#endif

using namespace Markdown;

Converter::Converter()
    : m_markdownFile(nullptr)
{
}

Converter::~Converter()
{
    if (m_markdownFile) {
        fclose(m_markdownFile);
    }
}

QTextDocument *Converter::convert(const QString &fileName)
{
    m_markdownFile = fopen(fileName.toLocal8Bit(), "rb");
    if (!m_markdownFile) {
        emit error(i18n("Failed to open the document"), -1);
        return nullptr;
    }

    m_fileDir = QDir(fileName.left(fileName.lastIndexOf('/')));

    QTextDocument *doc = convertOpenFile();
    QHash<QString, QTextFragment> internalLinks;
    QHash<QString, QTextBlock> documentAnchors;
    extractLinks(doc->rootFrame(), internalLinks, documentAnchors);

    for (auto linkIt = internalLinks.constBegin(); linkIt != internalLinks.constEnd(); ++linkIt) {
        auto anchorIt = documentAnchors.constFind(linkIt.key());
        if (anchorIt != documentAnchors.constEnd()) {
            const Okular::DocumentViewport viewport = calculateViewport(doc, anchorIt.value());
            Okular::GotoAction *action = new Okular::GotoAction(QString(), viewport);
            emit addAction(action, linkIt.value().position(), linkIt.value().position() + linkIt.value().length());
        } else {
            qDebug() << "Could not find destination for" << linkIt.key();
        }
    }

    return doc;
}

void Converter::convertAgain()
{
    setDocument(convertOpenFile());
}

QTextDocument *Converter::convertOpenFile()
{
    rewind(m_markdownFile);

    MMIOT *markdownHandle = mkd_in(m_markdownFile, 0);

    int flags = MKD_FENCEDCODE | MKD_GITHUBTAGS | MKD_AUTOLINK | MKD_TOC | MKD_IDANCHOR;
    if (!MarkdownGenerator::isFancyPantsEnabled())
        flags |= MKD_NOPANTS;
    if (!mkd_compile(markdownHandle, flags)) {
        emit error(i18n("Failed to compile the Markdown document."), -1);
        return nullptr;
    }

    char *htmlDocument;
    const int size = mkd_document(markdownHandle, &htmlDocument);

    const QString html = QString::fromUtf8(htmlDocument, size);

    QTextDocument *textDocument = new QTextDocument;
    textDocument->setPageSize(QSizeF(980, 1307));
    textDocument->setHtml(html);
    textDocument->setDefaultFont(generator()->generalSettings()->font());

    mkd_cleanup(markdownHandle);

    QTextFrameFormat frameFormat;
    frameFormat.setMargin(45);

    QTextFrame *rootFrame = textDocument->rootFrame();
    rootFrame->setFrameFormat(frameFormat);

    convertImages(rootFrame, m_fileDir, textDocument);

    return textDocument;
}

void Converter::extractLinks(QTextFrame *parent, QHash<QString, QTextFragment> &internalLinks, QHash<QString, QTextBlock> &documentAnchors)
{
    for (QTextFrame::iterator it = parent->begin(); !it.atEnd(); ++it) {
        QTextFrame *textFrame = it.currentFrame();
        const QTextBlock textBlock = it.currentBlock();

        if (textFrame) {
            extractLinks(textFrame, internalLinks, documentAnchors);
        } else if (textBlock.isValid()) {
            extractLinks(textBlock, internalLinks, documentAnchors);
        }
    }
}

void Converter::extractLinks(const QTextBlock &parent, QHash<QString, QTextFragment> &internalLinks, QHash<QString, QTextBlock> &documentAnchors)
{
    for (QTextBlock::iterator it = parent.begin(); !it.atEnd(); ++it) {
        const QTextFragment textFragment = it.fragment();
        if (textFragment.isValid()) {
            const QTextCharFormat textCharFormat = textFragment.charFormat();
            if (textCharFormat.isAnchor()) {
                const QString href = textCharFormat.anchorHref();
                if (href.startsWith('#')) { // It's an internal link, store it and we'll resolve it at the end
                    internalLinks.insert(href.mid(1), textFragment);
                } else {
                    Okular::BrowseAction *action = new Okular::BrowseAction(QUrl(textCharFormat.anchorHref()));
                    emit addAction(action, textFragment.position(), textFragment.position() + textFragment.length());
                }

                const QStringList anchorNames = textCharFormat.anchorNames();
                for (const QString &anchorName : anchorNames) {
                    documentAnchors.insert(anchorName, parent);
                }
            }
        }
    }
}

void Converter::convertImages(QTextFrame *parent, const QDir &dir, QTextDocument *textDocument)
{
    for (QTextFrame::iterator it = parent->begin(); !it.atEnd(); ++it) {
        QTextFrame *textFrame = it.currentFrame();
        const QTextBlock textBlock = it.currentBlock();

        if (textFrame) {
            convertImages(textFrame, dir, textDocument);
        } else if (textBlock.isValid()) {
            convertImages(textBlock, dir, textDocument);
        }
    }
}

void Converter::convertImages(const QTextBlock &parent, const QDir &dir, QTextDocument *textDocument)
{
    for (QTextBlock::iterator it = parent.begin(); !it.atEnd(); ++it) {
        const QTextFragment textFragment = it.fragment();
        if (textFragment.isValid()) {
            const QTextCharFormat textCharFormat = textFragment.charFormat();
            if (textCharFormat.isImageFormat()) {
                // TODO: Show images from http URIs

                QTextImageFormat format;

                format.setName(QDir::cleanPath(dir.absoluteFilePath(textCharFormat.toImageFormat().name())));
                const QImage img = QImage(format.name());

                if (img.width() > 890) {
                    format.setWidth(890);
                    format.setHeight(img.height() * 890. / img.width());
                } else {
                    format.setWidth(img.width());
                    format.setHeight(img.height());
                }

                QTextCursor cursor(textDocument);
                cursor.setPosition(textFragment.position(), QTextCursor::MoveAnchor);
                cursor.setPosition(textFragment.position() + textFragment.length(), QTextCursor::KeepAnchor);
                cursor.removeSelectedText();
                cursor.insertImage(format);
            }
        }
    }
}
