/***************************************************************************
 *   Copyright (C) 2004-5 by Enrico Ros <eros.kde@email.it>                *
 *   Copyright (C) 2005   by Piotr Szymanski <niedakh@gmail.com>           *
 *   Copyright (C) 2008   by Albert Astals Cid <aacid@kde.org>             *
 *   Copyright (C) 2017   Klarälvdalens Datakonsult AB, a KDAB Group       *
 *                        company, info@kdab.com. Work sponsored by the    *
 *                        LiMux project of the city of Munich              *
 *                                                                         *
 *   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.                                   *
 ***************************************************************************/

#ifndef _OKULAR_GENERATOR_H_
#define _OKULAR_GENERATOR_H_

#include "okularcore_export.h"
#include "document.h"
#include "fontinfo.h"
#include "global.h"
#include "pagesize.h"

#include <QList>
#include <QObject>
#include <QSharedDataPointer>
#include <QSizeF>
#include <QString>
#include <QVariant>
#include <QVector>

#include <QMimeType>
#include <kpluginfactory.h>

#define OKULAR_EXPORT_PLUGIN(classname, json ) \
    static_assert(json[0] != '\0', "arg2 must be a string literal"); \
    K_PLUGIN_CLASS_WITH_JSON(classname, json)

class QByteArray;
class QMutex;
class QPrinter;
class QIcon;

namespace Okular {

class BackendOpaqueAction;
class DocumentFonts;
class DocumentInfo;
class DocumentObserver;
class DocumentSynopsis;
class EmbeddedFile;
class ExportFormatPrivate;
class FontInfo;
class GeneratorPrivate;
class Page;
class PixmapRequest;
class PixmapRequestPrivate;
class TextPage;
class TextRequest;
class TextRequestPrivate;
class NormalizedRect;

/* Note: on contents generation and asynchronous queries.
 * Many observers may want to request data synchronously or asynchronously.
 * - Sync requests. These should be done in-place.
 * - Async request must be done in real background. That usually means a
 *   thread, such as QThread derived classes.
 * Once contents are available, they must be immediately stored in the
 * Page they refer to, and a signal is emitted as soon as storing
 * (even for sync or async queries) has been done.
 */

/**
 * @short Defines an entry for the export menu
 *
 * This class encapsulates information about an export format.
 * Every Generator can support 0 or more export formats which can be
 * queried with @ref Generator::exportFormats().
 */
class OKULARCORE_EXPORT ExportFormat
{
    public:
        typedef QList<ExportFormat> List;

        /**
         * Creates an empty export format.
         *
         * @see isNull()
         */
        ExportFormat();

        /**
         * Creates a new export format.
         *
         * @param description The i18n'ed description of the format.
         * @param mimeType The supported mime type of the format.
         */
        ExportFormat( const QString &description, const QMimeType &mimeType );

        /**
         * Creates a new export format.
         *
         * @param icon The icon used in the GUI for this format.
         * @param description The i18n'ed description of the format.
         * @param mimeType The supported mime type of the format.
         */
        ExportFormat( const QIcon &icon, const QString &description, const QMimeType &mimeType );

        /**
         * Destroys the export format.
         */
        ~ExportFormat();

        /**
         * @internal
         */
        ExportFormat( const ExportFormat &other );

        /**
         * @internal
         */
        ExportFormat& operator=( const ExportFormat &other );

        /**
         * Returns the description of the format.
         */
        QString description() const;

        /**
         * Returns the mime type of the format.
         */
        QMimeType mimeType() const;

        /**
         * Returns the icon for GUI representations of the format.
         */
        QIcon icon() const;

        /**
         * Returns whether the export format is null/valid.
         *
         * An ExportFormat is null if the mimetype is not valid or the
         * description is empty, or both.
         */
        bool isNull() const;

        /**
         * Type of standard export format.
         */
        enum StandardExportFormat
        {
            PlainText,         ///< Plain text
            PDF,               ///< PDF, aka Portable Document Format
            OpenDocumentText,  ///< OpenDocument Text format @since 0.8 (KDE 4.2)
            HTML   ///< OpenDocument Text format @since 0.8 (KDE 4.2)
        };

        /**
         * Builds a standard format for the specified @p type .
         */
        static ExportFormat standardFormat( StandardExportFormat type );

        bool operator==( const ExportFormat &other ) const;

        bool operator!=( const ExportFormat &other ) const;

    private:
        /// @cond PRIVATE
        friend class ExportFormatPrivate;
        /// @endcond
        QSharedDataPointer<ExportFormatPrivate> d;
};

/**
 * @short [Abstract Class] The information generator.
 *
 * Most of class members are virtuals and some of them pure virtual. The pure
 * virtuals provide the minimal functionalities for a Generator, that is being
 * able to generate QPixmap for the Page 's of the Document.
 *
 * Implementing the other functions will make the Generator able to provide
 * more contents and/or functionalities (like text extraction).
 *
 * Generation/query is requested by the Document class only, and that
 * class stores the resulting data into Page s. The data will then be
 * displayed by the GUI components (PageView, ThumbnailList, etc..).
 *
 * @see PrintInterface, ConfigInterface, GuiInterface
 */
class OKULARCORE_EXPORT Generator : public QObject
{
    /// @cond PRIVATE
    friend class PixmapGenerationThread;
    friend class TextPageGenerationThread;
    /// @endcond

    Q_OBJECT

    public:
        /**
         * Describe the possible optional features that a Generator can
         * provide.
         */
        enum GeneratorFeature
        {
            Threaded,          ///< Whether the Generator supports asynchronous generation of pictures or text pages
            TextExtraction,    ///< Whether the Generator can extract text from the document in the form of TextPage's
            ReadRawData,       ///< Whether the Generator can read a document directly from its raw data.
            FontInfo,          ///< Whether the Generator can provide information about the fonts used in the document
            PageSizes,         ///< Whether the Generator can change the size of the document pages.
            PrintNative,       ///< Whether the Generator supports native cross-platform printing (QPainter-based).
            PrintPostscript,   ///< Whether the Generator supports postscript-based file printing.
            PrintToFile,       ///< Whether the Generator supports export to PDF & PS through the Print Dialog
            TiledRendering,    ///< Whether the Generator can render tiles @since 0.16 (KDE 4.10)
            SwapBackingFile,   ///< Whether the Generator can hot-swap the file it's reading from @since 1.3
            SupportsCancelling ///< Whether the Generator can cancel requests @since 1.4
        };

        /**
         * Creates a new generator.
         */
        explicit Generator(QObject* parent = nullptr, const QVariantList& args = QVariantList());

        /**
         * Destroys the generator.
         */
        ~Generator() override;

        /**
         * Loads the document with the given @p fileName and fills the
         * @p pagesVector with the parsed pages.
         *
         * @note If you implement the WithPassword variants you don't need to implement this one
         *
         * @returns true on success, false otherwise.
         */
        virtual bool loadDocument( const QString & fileName, QVector< Page * > & pagesVector );

        /**
         * Loads the document from the raw data @p fileData and fills the
         * @p pagesVector with the parsed pages.
         *
         * @note If you implement the WithPassword variants you don't need to implement this one
         *
         * @note the Generator has to have the feature @ref ReadRawData enabled
         *
         * @returns true on success, false otherwise.
         */
        virtual bool loadDocumentFromData( const QByteArray & fileData, QVector< Page * > & pagesVector );

        /**
         * Loads the document with the given @p fileName and @p password and fills the
         * @p pagesVector with the parsed pages.
         *
         * @note Do not implement this if your format doesn't support passwords, it'll cleanly call loadDocument()
         *
         * @since 0.20 (KDE 4.14)
         *
         * @returns a LoadResult defining the result of the operation
         */
        virtual Document::OpenResult loadDocumentWithPassword( const QString & fileName, QVector< Page * > & pagesVector, const QString &password );

        /**
         * Loads the document from the raw data @p fileData and @p password and fills the
         * @p pagesVector with the parsed pages.
         *
         * @note Do not implement this if your format doesn't support passwords, it'll cleanly call loadDocumentFromData()
         *
         * @note the Generator has to have the feature @ref ReadRawData enabled
         *
         * @since 0.20 (KDE 4.14)
         *
         * @returns a LoadResult defining the result of the operation
         */
        virtual Document::OpenResult loadDocumentFromDataWithPassword( const QByteArray & fileData, QVector< Page * > & pagesVector, const QString &password );

        /**
         * Describes the result of an swap file operation.
         *
         * @since 1.3
         */
        enum SwapBackingFileResult
        {
            SwapBackingFileError,               //< The document could not be swapped
            SwapBackingFileNoOp,                //< The document was swapped and nothing needs to be done
            SwapBackingFileReloadInternalData   //< The document was swapped and internal data (forms, annotations, etc) needs to be reloaded
        };

        /**
         * Changes the path of the file we are reading from. The new path must
         * point to a copy of the same document.
         *
         * @note the Generator has to have the feature @ref SwapBackingFile enabled
         *
         * @since 1.3
         */
        virtual SwapBackingFileResult swapBackingFile( const QString & newFileName, QVector<Okular::Page*> & newPagesVector );

        /**
         * This method is called when the document is closed and not used
         * any longer.
         *
         * @returns true on success, false otherwise.
         */
        bool closeDocument();

        /**
         * This method returns whether the generator is ready to
         * handle a new pixmap request.
         */
        virtual bool canGeneratePixmap() const;

        /**
         * This method can be called to trigger the generation of
         * a new pixmap as described by @p request.
         */
        virtual void generatePixmap( PixmapRequest * request );

        /**
         * This method returns whether the generator is ready to
         * handle a new text page request.
         */
        virtual bool canGenerateTextPage() const;

        /**
         * This method can be called to trigger the generation of
         * a text page for the given @p page.
         *
         * The generation is done in the calling thread.
         *
         * @see TextPage
         */
        void generateTextPage( Page * page );

        /**
         * Returns the general information object of the document.
         *
         * Changed signature in okular version 0.21
         */
        virtual DocumentInfo generateDocumentInfo( const QSet<DocumentInfo::Key> &keys ) const;

        /**
         * Returns the 'table of content' object of the document or 0 if
         * no table of content is available.
         */
        virtual const DocumentSynopsis * generateDocumentSynopsis();

        /**
         * Returns the 'list of embedded fonts' object of the specified \p page
         * of the document.
         *
         * \param page a page of the document, starting from 0 - -1 indicates all
         * the other fonts
         */
        virtual FontInfo::List fontsForPage( int page );

        /**
         * Returns the 'list of embedded files' object of the document or 0 if
         * no list of embedded files is available.
         */
        virtual const QList<EmbeddedFile*> * embeddedFiles() const;

        /**
         * This enum identifies the metric of the page size.
         */
        enum PageSizeMetric
        {
          None,   ///< The page size is not defined in a physical metric.
          Points, ///< The page size is given in 1/72 inches.
          Pixels  ///< The page size is given in screen pixels @since 0.19 (KDE 4.13)
        };

        /**
         * This method returns the metric of the page size. Default is @ref None.
         */
        virtual PageSizeMetric pagesSizeMetric() const;

        /**
         * Returns whether the given @p action is allowed in the document.
         * @see @ref Okular::Permission
         */
        virtual bool isAllowed( Permission action ) const;

        /**
         * This method is called when the orientation has been changed by the user.
         */
        virtual void rotationChanged( Rotation orientation, Rotation oldOrientation );

        /**
         * Returns the list of supported page sizes.
         */
        virtual PageSize::List pageSizes() const;

        /**
         * This method is called when the page size has been changed by the user.
         */
        virtual void pageSizeChanged( const PageSize &pageSize, const PageSize &oldPageSize );

        /**
         * This method is called to print the document to the given @p printer.
         */
        virtual bool print( QPrinter &printer );

        /**
         * Possible print errors
         * @since 0.11 (KDE 4.5)
         */
        enum PrintError
        {
            NoPrintError,                       ///< There was no print error
            UnknownPrintError,
            TemporaryFileOpenPrintError,
            FileConversionPrintError,
            PrintingProcessCrashPrintError,
            PrintingProcessStartPrintError,
            PrintToFilePrintError,
            InvalidPrinterStatePrintError,
            UnableToFindFilePrintError,
            NoFileToPrintError,
            NoBinaryToPrintError,
            InvalidPageSizePrintError ///< @since 0.18.2 (KDE 4.12.2)
        };
        
        /**
         * This method returns the meta data of the given @p key with the given @p option
         * of the document.
         */
        virtual QVariant metaData( const QString &key, const QVariant &option ) const;

        /**
         * Returns the list of additional supported export formats.
         */
        virtual ExportFormat::List exportFormats() const;

        /**
         * This method is called to export the document in the given @p format and save it
         * under the given @p fileName. The format must be one of the supported export formats.
         */
        virtual bool exportTo( const QString &fileName, const ExportFormat &format );

        /**
         * This method is called to know which wallet data should be used for the given file name.
         * Unless you have very special requirements to where wallet data should be stored you
         * don't need to reimplement this method.
         */
        virtual void walletDataForFile( const QString &fileName, QString *walletName, QString *walletFolder, QString *walletKey ) const;

        /**
         * Query for the specified @p feature.
         */
        bool hasFeature( GeneratorFeature feature ) const;

        /**
         * Update DPI of the generator
         *
         * @since 0.19 (KDE 4.13)
         */
        void setDPI(const QSizeF &dpi); // TODO remove the & when we do a BIC change elsewhere

        /**
         * Returns the 'layers model' object of the document or NULL if
         * layers model is not available.
         *
         * @since 0.24
         */
        virtual QAbstractItemModel * layersModel() const;

        /**
         * Calls the backend to execute an BackendOpaqueAction
         */
        virtual void opaqueAction( const BackendOpaqueAction *action );

    Q_SIGNALS:
        /**
         * This signal should be emitted whenever an error occurred in the generator.
         *
         * @param message The message which should be shown to the user.
         * @param duration The time that the message should be shown to the user.
         */
        void error( const QString &message, int duration );

        /**
         * This signal should be emitted whenever the user should be warned.
         *
         * @param message The message which should be shown to the user.
         * @param duration The time that the message should be shown to the user.
         */
        void warning( const QString &message, int duration );

        /**
         * This signal should be emitted whenever the user should be noticed.
         *
         * @param message The message which should be shown to the user.
         * @param duration The time that the message should be shown to the user.
         */
        void notice( const QString &message, int duration );

    protected:
        /**
         * This method must be called when the pixmap request triggered by generatePixmap()
         * has been finished.
         */
        void signalPixmapRequestDone( PixmapRequest * request );

        /**
         * This method must be called when a text generation has been finished.
         */
        void signalTextGenerationDone( Page *page, TextPage *textPage );

        /**
         * This method is called when the document is closed and not used
         * any longer.
         *
         * @returns true on success, false otherwise.
         */
        virtual bool doCloseDocument() = 0;

        /**
         * Returns the image of the page as specified in
         * the passed pixmap @p request.
         *
         * Must return a null image if the request was cancelled and the generator supports cancelling
         *
         * @warning this method may be executed in its own separated thread if the
         * @ref Threaded is enabled!
         */
        virtual QImage image( PixmapRequest *request );

        /**
         * Returns the text page for the given @p request.
         *
         * Must return a null pointer if the request was cancelled and the generator supports cancelling
         *
         * @warning this method may be executed in its own separated thread if the
         * @ref Threaded is enabled!
         *
         * @since 1.4
         */
        virtual TextPage* textPage( TextRequest *request );

        /**
         * Returns a pointer to the document.
         */
        const Document * document() const;

        /**
         * Toggle the @p feature .
         */
        void setFeature( GeneratorFeature feature, bool on = true );

        /**
         * Internal document setting
         */
        enum DocumentMetaDataKey {
            PaperColorMetaData,         ///< Returns (QColor) the paper color if set in Settings or the default color (white) if option is true (otherwise returns a non initialized QColor)
            TextAntialiasMetaData,      ///< Returns (bool) text antialias from Settings (option is not used)
            GraphicsAntialiasMetaData,  ///< Returns (bool)graphic antialias from Settings (option is not used)
            TextHintingMetaData         ///< Returns (bool)text hinting from Settings (option is not used)
        };

        /**
         * Request a meta data of the Document, if available, like an internal
         * setting.
         *
         * @since 1.1
         */
        QVariant documentMetaData( const DocumentMetaDataKey key, const QVariant &option = QVariant() ) const;

        /**
         * Request a meta data of the Document, if available, like an internal
         * setting.
         */
        OKULARCORE_DEPRECATED QVariant documentMetaData( const QString &key, const QVariant &option = QVariant() ) const;

        /**
         * Return the pointer to a mutex the generator can use freely.
         */
        QMutex* userMutex() const;

        /**
         * Set the bounding box of a page after the page has already been handed
         * to the Document. Call this instead of Page::setBoundingBox() to ensure
         * that all observers are notified.
         *
         * @since 0.7 (KDE 4.1)
         */
        void updatePageBoundingBox( int page, const NormalizedRect & boundingBox );

        /**
         * Returns DPI, previously set via setDPI()
         * @since 0.19 (KDE 4.13)
         */
        QSizeF dpi() const;

    protected Q_SLOTS:
        /**
         * Gets the font data for the given font
         *
         * @since 0.8 (KDE 4.1)
         */
        void requestFontData(const Okular::FontInfo &font, QByteArray *data);

        /**
         * This method can be called to trigger a partial pixmap update for the given request
         * Make sure you call it in a way it's executed in the main thread.
         * @since 1.3
         */
        void signalPartialPixmapRequest( Okular::PixmapRequest *request, const QImage &image );

    protected:
        /**
         * Returns the last print error in case print() failed
         * @since 0.11 (KDE 4.5)
         */
        // TODO Make print() return a PrintError instead of bool and remove this function when a BIC change happens somewhere else
        Q_INVOKABLE Okular::Generator::PrintError printError() const;

        /// @cond PRIVATE
        Generator(GeneratorPrivate &dd, QObject *parent, const QVariantList &args);
        Q_DECLARE_PRIVATE( Generator )
        GeneratorPrivate *d_ptr;

        friend class Document;
        friend class DocumentPrivate;
        /// @endcond PRIVATE

    private:
        Q_DISABLE_COPY( Generator )
};

/**
 * @short Describes a pixmap type request.
 */
class OKULARCORE_EXPORT PixmapRequest
{
    friend class Document;
    friend class DocumentPrivate;

    public:
        enum PixmapRequestFeature
        {
            NoFeature = 0,
            Asynchronous = 1,
            Preload = 2
        };
        Q_DECLARE_FLAGS( PixmapRequestFeatures, PixmapRequestFeature )

        /**
         * Creates a new pixmap request.
         *
         * @param observer The observer.
         * @param pageNumber The page number.
         * @param width The width of the page.
         * @param height The height of the page.
         * @param priority The priority of the request.
         * @param features The features of generation.
         */
        PixmapRequest( DocumentObserver *observer, int pageNumber, int width, int height, int priority, PixmapRequestFeatures features );

        /**
         * Destroys the pixmap request.
         */
        ~PixmapRequest();

        /**
         * Returns the observer of the request.
         */
        DocumentObserver *observer() const;

        /**
         * Returns the page number of the request.
         */
        int pageNumber() const;

        /**
         * Returns the page width of the requested pixmap.
         */
        int width() const;

        /**
         * Returns the page height of the requested pixmap.
         */
        int height() const;

        /**
         * Returns the priority (less it better, 0 is maximum) of the
         * request.
         */
        int priority() const;

        /**
         * Returns whether the generation should be done synchronous or
         * asynchronous.
         *
         * If asynchronous, the pixmap is created in a thread and the observer
         * is notified when the job is done.
         */
        bool asynchronous() const;

        /**
         * Returns whether the generation request is for a page that is not important
         * i.e. it's just for speeding up future rendering
         */
        bool preload() const;

        /**
         * Returns a pointer to the page where the pixmap shall be generated for.
         */
        Page *page() const;

        /**
         * Sets whether the generator should render only the given normalized
         * rect or the entire page
         *
         * @since 0.16 (KDE 4.10)
         */
        void setTile( bool tile );

        /**
         * Returns whether the generator should render just the region given by
         * normalizedRect() or the entire page.
         *
         * @since 0.16 (KDE 4.10)
         */
        bool isTile() const;

        /**
         * Sets the region of the page to request.
         *
         * @since 0.16 (KDE 4.10)
         */
        void setNormalizedRect( const NormalizedRect &rect );

        /**
         * Returns the normalized region of the page to request.
         *
         * @since 0.16 (KDE 4.10)
         */
        const NormalizedRect& normalizedRect() const;

        /**
         * Sets whether the request should report back updates if possible
         *
         * @since 1.3
         */
        void setPartialUpdatesWanted(bool partialUpdatesWanted);

        /**
         * Should the request report back updates if possible?
         *
         * @since 1.3
         */
        bool partialUpdatesWanted() const;

        /**
         * Should the request be aborted if possible?
         *
         * @since 1.4
         */
        bool shouldAbortRender() const;

    private:
        Q_DISABLE_COPY( PixmapRequest )

        friend class PixmapRequestPrivate;
        PixmapRequestPrivate* const d;
};

/**
 * @short Describes a text request.
 *
 * @since 1.4
 */
class OKULARCORE_EXPORT TextRequest
{
    public:
        /**
         * Creates a new text request.
         */
        explicit TextRequest( Page *page );

        TextRequest();

        /**
         * Destroys the pixmap request.
         */
        ~TextRequest();

        /**
         * Returns a pointer to the page where the pixmap shall be generated for.
         */
        Page *page() const;

        /**
         * Should the request be aborted if possible?
         */
        bool shouldAbortExtraction() const;

    private:
        Q_DISABLE_COPY( TextRequest )

        friend TextRequestPrivate;
        TextRequestPrivate* const d;
};

}

Q_DECLARE_METATYPE(Okular::Generator::PrintError)
Q_DECLARE_METATYPE(Okular::PixmapRequest*)

#define OkularGeneratorInterface_iid "org.kde.okular.Generator"
Q_DECLARE_INTERFACE(Okular::Generator, OkularGeneratorInterface_iid)

#ifndef QT_NO_DEBUG_STREAM
OKULARCORE_EXPORT QDebug operator<<( QDebug str, const Okular::PixmapRequest &req );
#endif

#endif

/* kate: replace-tabs on; indent-width 4; */
