/* This file RenderEngine.h is part of the EqualX project https://launchpad.net/equalx
 * Copyright (C) 2013  Mihai Niculescu
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef RENDERENGINE_H
#define RENDERENGINE_H

#include <QColor>
#include <QFlag>
#include <QMap>
#include <QObject>
#include <QProcess>
#include <QString>
#include <QList>

#include "defines.h"
#include "File.h"

namespace EqualX{

/*
 * Converter:
 * opts -  must include the input and output file name (as QString arg() identifieries: %1 - as input file %2 - as output file)
 */

struct Converter{
    QString path; // path to the executable
    QString opts; // options to pass to the executable - must include %1 - input file, %2 - output file
};

typedef QMap<QString,EqualX::Converter> ConvertersList; // maps the export filetype of the converter to a converter settings
typedef QMapIterator<QString,EqualX::Converter> ConvertersListIterator;

typedef QMap<int,QString> ErrorsList; // maps an error line number with an error string
typedef QMapIterator<int,QString> ErrorsListIterator;


// this class does... see  RenderEngine::run()
class RenderEngine : public QObject
{
    Q_OBJECT
public:
    explicit RenderEngine(QObject *parent = 0);
    ~RenderEngine();

    // returns full path to the build directory, including the last directory separator
    // i.e: on linux it returns: /tmp/equalx/
    QString buildDir() const;
    EqualX::ConvertersList converters() const;

    static bool isRunning();

    QString latexLog() const;
    const EqualX::ErrorsList* latexErrors() const; // return parsed latex errors mapped by line number and error string

    // returns full path to a generated file based on its file extension (without the prefixed dot .)
    QString file(const QString& type) const; //! the string is empty if the file wasnt generated or if there's no converter for this type
    QStringList fileTypes() const; // a list of file extensions (without .) that this Engine can convert to

    /* build directory is the place to save all generated files
     * NOTE: the directory must exist and must have write permissions
     */
    void setBuildDir(const QString& dir);
    void setFileInfo(const EqualX::FileInfo& info);

    // only registers the converters, do not run them
    void addConverter(const QString& type, const EqualX::Converter& converter); // do not add pdflatex or gs
    void registerConverters(const EqualX::ConvertersList& converters);//! pdflatex and gs must not be in this list
    void setPDFLatex(const QString& path);
    void setGS(const QString& path);

    /* Description of RenderEngine::run()
     * - generates a .xmpi file to be used by XMP (EqualX metadata)
     * - generates a latex file
     * - generate another latex file (latex-crop) for cropping the PDF (created previously by pdfLatex) and include the generated XMP file
     * - launches a list of applications (converters) on the output of pdfLatex (PDF file)
     *
     * NB: The order of starting the converters:
     *  1. runs pdfLatex on the generated latex file
     *  2. launch gs to compute the bounding box of PDF. Use the bounding box parameters in the latex-with-crop
     *  3. run again pdfLatex on the latex-with-crop => obtains a PDF-crop
     *  4. the other converters can now be launched in async on the PDF-crop
     *
     *  RenderEngine starts the converters only after going through steps 1,2,3 - see above
     *
     */

    // filetypes - run only (registered)converters for these file types.
    // freshRun  - true to start a fresh run from beginning (latex->pdf->gs->pdfcrop), false - use existing PDF-crop file
    void run(const QStringList& filetypes=QStringList(), bool freshRun=true); //! this is asynchronos

    //overloaded convenience function
    void run(const QString& filetype); //! blocks main loop

    QProcess* currentProcess() const{ return mChildProcess; }
signals:
    void started();
    void finished();
    void finished(int exitCode,QProcess::ExitStatus exitStatus);
    void error(QProcess::ProcessError err);

private slots:
    void finishedPdfLatex(int exitCode,QProcess::ExitStatus exitStatus);
    void finishedGS(int, QProcess::ExitStatus);
    void finishedPdfCrop(int, QProcess::ExitStatus);
    void finishedConverter(int exitCode,QProcess::ExitStatus exitStatus);


protected:
    bool parseLatexLog(); // Parse the latex log and return true if found latex errors in the log, false otherwise

private:
    void stop();
    void finish(int exitCode=0,QProcess::ExitStatus exitStatus=QProcess::NormalExit);
    void runConvertersOnFileTypes();

    QString mPathPDFLatex;
    QString mPathGS;
    EqualX::ConvertersList mConverters;
    EqualX::File mFile;
    QStringList mConvertToFileTypes; // convert to these file types when run()
    QString mBuildDir;
    QString mAppDirSave; // save app current dir; the working dir is restored to this after the engine finishes

    QString mLatexLog; // contains whole output from pdflatex

    /* mLatexErrors - contains parsed errors from latex log
       mapped by line number and error string  */
    EqualX::ErrorsList mLatexErrors;
    QProcess* mChildProcess;

    int mRunningProcesses; // number of started processes
    static bool mIsRunning; // if Engine is Running

};

} // end namespace EqualX
#endif // RENDERENGINE_H
