/*******************************************************************************
**
** SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru>
** SPDX-License-Identifier: BSD-3-Clause
** This file is part of the OfficeViewer project.
**
** Redistribution and use in source and binary forms,
** with or without modification, are permitted provided
** that the following conditions are met:
**
** * Redistributions of source code must retain the above copyright notice,
**   this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright notice,
**   this list of conditions and the following disclaimer
**   in the documentation and/or other materials provided with the distribution.
** * Neither the name of the copyright holder nor the names of its contributors
**   may be used to endorse or promote products derived from this software
**   without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
** AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
** THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
** FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
** OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
** LOSS OF USE, DATA, OR PROFITS;
** OR BUSINESS INTERRUPTION)
** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE)
** ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
** EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
*******************************************************************************/


#pragma once

#include <QRunnable>
#include <QObject>
#include <QQueue>
#include <QStack>

#define LOK_USE_UNSTABLE_API
#include <loevent.h>
#include <LibreOfficeKit/LibreOfficeKit.hxx>
#include <LibreOfficeKit/LibreOfficeKitEnums.h>

namespace lok {
    class Office;
    class Document;
} // namespace lok
namespace LoEvent {
    struct Message;
}
class QLocalServer;
class QLocalSocket;
class QThreadPool;
class QSharedMemory;
class QTimer;
class WorkerThread;

struct RenderCommand: public LoEvent::PayloadMakePage
{
    RenderCommand() { pageIndex = -1; }
    RenderCommand(const LoEvent::PayloadMakePage &data, bool renderPart, const QString &keySharedMem)
        : LoEvent::PayloadMakePage(data)
        , renderPart(renderPart)
        , keySharedMem(keySharedMem)
    {}
    bool renderPart;
    QString keySharedMem;
    QSharedMemory *mem;
};
Q_DECLARE_METATYPE(RenderCommand)

class LoadingWorkerLok : public QObject, public QRunnable
{
    Q_OBJECT

public:
    LoadingWorkerLok(lok::Office *office, const QByteArray &url);
    ~LoadingWorkerLok();

    void run() override final;

signals:
    void progress(int percent);
    void documentReady(void *pointer);

private:
    lok::Office *m_office;
    QByteArray m_url;

    static void progressCallback(int nType, const char *pPayload, void *pData);
};

class OfficeWorker : public QObject
{
    Q_OBJECT

public:
    struct Task
    {
        int pageNumber;
        QRect rect;
        qreal zoom;

        bool operator==(const Task &other) const
        {
            return pageNumber == other.pageNumber && rect == other.rect && qFuzzyCompare(zoom, other.zoom);
        }
    };

    explicit OfficeWorker(const QString &socketName, QObject *parent = nullptr);
    ~OfficeWorker();

    void loadDocument(char *url);
    void processing();
    static void globalCallback(int nType, const char *pPayload, void *pData);

public slots:
    void callbackImpl(int type, const QByteArray &payload);
    void sendDebug(const QString &errorString);

private slots:
    void progressReceived(int progress);
    void documentLoaded(void *pointer);
    void fullRendered(const RenderCommand &cmd);
    void partRendered(const RenderCommand &cmd);
    void renderedNotVisible(const RenderCommand &cmd);
    void setProcessingStatus(bool processing);
    void sendMessage(const LoEvent::Message &message);

private:
    void initServer(const QString &socketName);
    void newConnection();
    void newCommand();
    void runCommand(const LoEvent::Message &message);
    void sendError(int error);
    QString makeKey();
    void addRenderCmd(const RenderCommand &cmd, bool high = false);
    void removeRenderCmd(const Task &task, bool high = false);
    void clearRenderCmd();
    void processingRenderCmd(const RenderCommand &cmd);
    void addLoCmd(const LoEvent::Message &cmd);
    void processingLoCmd(const LoEvent::Message &cmd);

private:
    lok::Office *m_office;
    lok::Document *m_document;
    QLocalServer *m_server;
    QLocalSocket *m_socket;
    /// The queue for departure to the docviewer
    QQueue<LoEvent::Message> m_sendQueue;
    QThreadPool *m_innnerThreadPool;
    QSet<Task> m_pendingTasks;
    QMap<QString, QSharedMemory *> m_sharedMemoryList;
    QString m_urlDocument;
    bool m_processing;
    QQueue<QString> m_availableKeyList;
    QQueue<LoEvent::Message> m_queueCommands;
    QStack<RenderCommand> m_lowRenderCommands;
    QQueue<RenderCommand> m_highRenderCommands;
    WorkerThread *m_workerThread;
};
