/*
 *    SPDX-License-Identifier: GPL-2.0-or-later
 *    SPDX-FileCopyrightText: 2011 Alexander Rieder <alexanderrieder@gmail.com>
 */

#include "latexrenderer.h"
using namespace Cantor;

#include <QProcess>
#include <QDebug>
#include <QDir>
#include <QFileInfo>
#include <QEventLoop>
#include <QTemporaryFile>
#include <KColorScheme>
#include <QUuid>
#include <QApplication>

#include <config-cantorlib.h>
#include "cantor_libs_settings.h"

class Cantor::LatexRendererPrivate
{
public:
    QString latexCode;
    QString header;
    LatexRenderer::Method method;
    bool isEquationOnly;
    LatexRenderer::EquationType equationType;
    QString errorMessage;
    bool success;
    QString latexFilename;
    QString pdfFilename;
    QString uuid;
    QTemporaryFile* texFile;
    QColor bgColor{Qt::white};
    QColor textColor{Qt::black};
};

static const QLatin1String tex("\\documentclass%9{minimal}"\
                         "\\usepackage{latexsym,amsfonts,amssymb,ulem}"\
                         "\\usepackage{amsmath}"\
                         "\\usepackage[utf8]{inputenc}"\
                         "\\usepackage{color}"\
                         "\\usepackage[active,displaymath,tightpage]{preview}"\
                         "\\setlength\\PreviewBorder{2pt}"\
                         "\\setlength{\\fboxsep}{0pt}"\
                         "\\begin{document}"\
                         "\\pagecolor[rgb]{%1,%2,%3}"\
                         "\\begin{preview}\n"\
                         "\\color[rgb]{%4,%5,%6}"\
                         "\\fontsize{%7}{%7}\\selectfont\n"\
                         "%8\n"\
                         "\\end{preview}"\
                         "\\end{document}");

static const QLatin1String eqnHeader("$\\displaystyle %1$");
static const QLatin1String inlineEqnHeader("$%1$");

LatexRenderer::LatexRenderer(QObject* parent) : QObject(parent),
d(new LatexRendererPrivate)
{
    d->method=LatexMethod;
    d->isEquationOnly=false;
    d->equationType=InlineEquation;
    d->success=false;
    d->texFile=nullptr;
}

LatexRenderer::~LatexRenderer()
{
    delete d;
}

QString LatexRenderer::latexCode() const
{
    return d->latexCode;
}

void LatexRenderer::setLatexCode(const QString& src)
{
    d->latexCode=src;
}

QString LatexRenderer::header() const
{
    return d->header;
}

void LatexRenderer::addHeader(const QString& header)
{
    d->header.append(header);
}

void LatexRenderer::setHeader(const QString& header)
{
    d->header=header;
}

LatexRenderer::Method LatexRenderer::method() const
{
    return d->method;
}

void LatexRenderer::setMethod(LatexRenderer::Method method)
{
    d->method=method;
}

void LatexRenderer::setEquationType(LatexRenderer::EquationType type)
{
    d->equationType=type;
}

LatexRenderer::EquationType LatexRenderer::equationType() const
{
    return d->equationType;
}

void LatexRenderer::setErrorMessage(const QString& msg)
{
    d->errorMessage=msg;
}

QString LatexRenderer::errorMessage() const
{
    return d->errorMessage;
}

bool LatexRenderer::renderingSuccessful() const
{
    return d->success;
}

void LatexRenderer::setEquationOnly(bool isEquationOnly)
{
    d->isEquationOnly=isEquationOnly;
}

bool LatexRenderer::isEquationOnly() const
{
    return d->isEquationOnly;
}

QString LatexRenderer::imagePath() const
{
    return d->pdfFilename;
}

QString Cantor::LatexRenderer::uuid() const
{
    return d->uuid;
}

bool LatexRenderer::render()
{
    switch(d->method)
    {
        case LatexRenderer::LatexMethod:
            return renderWithLatex();

        case LatexRenderer::MmlMethod:
            return renderWithMml();

        default:
            return false;
    };
}

void LatexRenderer::renderBlocking()
{
    QEventLoop event;
    connect(this, &LatexRenderer::done, &event, &QEventLoop::quit);
    connect(this, &LatexRenderer::error, &event, &QEventLoop::quit);

    bool success = render();
    // We can't Q_EMIT error before running event loop, so exit by passing false as an error indicator
    if (success)
        event.exec();
    else
        return;
}

bool LatexRenderer::renderWithLatex()
{
    qDebug()<<"rendering using latex method";
    QString dir=QStandardPaths::writableLocation(QStandardPaths::TempLocation);

    if (d->texFile)
        delete d->texFile;

    d->texFile=new QTemporaryFile(dir + QDir::separator() + QLatin1String("cantor_tex-XXXXXX.tex"));
    d->texFile->open();

    KColorScheme scheme(QPalette::Active);
    const QColor backgroundColor=scheme.background().color();
    const QColor foregroundColor=scheme.foreground().color();

    QString expressionTex=tex;

    // Wrap the latex code with equation formatting
    QString wrappedLatex;
    if(isEquationOnly())
    {
        switch(equationType())
        {
            case FullEquation: wrappedLatex = eqnHeader; break;
            case InlineEquation: wrappedLatex = inlineEqnHeader; break;
            case CustomEquation: wrappedLatex = QLatin1String("%1"); break;
        }
        wrappedLatex = wrappedLatex.arg(d->latexCode);
    }
    else
    {
        wrappedLatex = d->latexCode;
    }

    int fontPointSize = QApplication::font().pointSize();

    expressionTex = expressionTex
    .arg(d->bgColor.redF()).arg(d->bgColor.greenF()).arg(d->bgColor.blueF())
    .arg(d->textColor.redF()).arg(d->textColor.greenF()).arg(d->textColor.blueF())
    .arg(fontPointSize)
    .arg(wrappedLatex)
    .arg(QString()); // %9 - empty for documentclass option

    // qDebug()<<"full tex:\n"<<expressionTex;

    d->texFile->write(expressionTex.toUtf8());
    d->texFile->flush();

    QString fileName = d->texFile->fileName();
    qDebug()<<"fileName: "<<fileName;
    d->latexFilename=fileName;
    QProcess *p=new QProcess( this );
    p->setWorkingDirectory(dir);

    d->uuid = genUuid();
    d->pdfFilename = dir + QDir::separator() + QStringLiteral("cantor_") + d->uuid + QStringLiteral(".pdf");

    qDebug() << CantorLibsSettings::self()->latexCommand();
    const QString& pdflatex = QStandardPaths::findExecutable(QLatin1String("pdflatex"));
    if (!pdflatex.isEmpty())
    {
        p->setProgram(pdflatex);
        p->setArguments({QStringLiteral("-jobname=cantor_") + d->uuid, QStringLiteral("-halt-on-error"), fileName});

        connect(p, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(convertingDone()) );
        p->start();
        return true;
    }
    else
    {
        setErrorMessage(QStringLiteral("failed to find pdflatex executable"));
        return false;
    }
}

void LatexRenderer::convertingDone()
{
    QFileInfo info(d->pdfFilename);
    qDebug() <<"remove temporary files for " << d->latexFilename;

    QString pathWithoutExtension = info.path() + QDir::separator() + info.completeBaseName();
    QFile::remove(pathWithoutExtension + QLatin1String(".log"));
    QFile::remove(pathWithoutExtension + QLatin1String(".aux"));

    if(info.exists())
    {
        delete d->texFile;
        d->texFile = nullptr;

        d->success=true;
        Q_EMIT done();
    }
    else
    {
        d->success=false;
        setErrorMessage(QStringLiteral("failed to create the latex preview pdf"));
        Q_EMIT error();
    }
}

bool LatexRenderer::renderWithMml()
{
    qWarning()<<"WARNING: MML rendering not implemented yet!";
    Q_EMIT error();
    return false;
}

QString LatexRenderer::genUuid()
{
    QString uuid = QUuid::createUuid().toString();
    uuid.remove(0, 1);
    uuid.chop(1);
    uuid.replace(QLatin1Char('-'), QLatin1Char('_'));
    return uuid;
}

bool Cantor::LatexRenderer::isLatexAvailable()
{
    QFileInfo infoPdf(QStandardPaths::findExecutable(QLatin1String("pdflatex")));
    return infoPdf.exists() && infoPdf.isExecutable();
}

void LatexRenderer::setBackgroundColor(const QColor& bg) {
    d->bgColor = bg;
}

void LatexRenderer::setTextColor(const QColor& fg) {
    d->textColor = fg;
}
