/*******************************************************************************
**
** 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 <cstring>
#include <QByteArray>
#include <QDataStream>
#include <QDebug>
#include <QIODevice>
#include <QRect>
#include <QSize>
#include <QString>

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

namespace LoEvent {

    static const int EVENT_ID_LENGTH = 6;
    static const char EVENT_ID[EVENT_ID_LENGTH] = {'O', 'f', 'f', 'i', 'c', 'e'};

    enum CommandType : char
    {
        UnknownCommand,
        // Worker messages
        OfficeReady,           // arguments: bool initialized (1 - true, 0 - false)
        LoadStatus,            // arguments: bool isLoaded (1 - true, 0 - false)
        ProcessingStatus,      // arguments: bool isProcessing (1 - true, 0 - false)
        LoadProgress,          // arguments: int percent
        DocumentType,          // arguments: int documentType
        PageRectangles,        // arguments: QList<QRect>
        PageCount,             // arguments: int pageCount
        PageSize,              // arguments: LoEvent::PayloadPageSize
        PageName,              // arguments: LoEvent::PayloadPageName
        Error,                 // arguments: int errorType
        NewPage,               // arguments: LoEvent::PayloadNewPage
        NewTile,               // arguments: LoEvent::PayloadNewTile
        Debug,                 // arguments: QString
        CallbakEvent,          // arguments: LoEvent::PayloadCallbackEvent
        CallbakUnoCommand,     // arguments: LoEvent::PayloadUnoEvent
        CallbackTextSelection, // arguments: QString
        SaveAsStatus,          // arguments: LoEvent::PayloadSaveAsStatus
        // Provider messages
        MakePage,              // arguments: LoEvent::PayloadMakePage
        MakeTile,              // arguments: LoEvent::PayloadMakeTail
        ClearTasks,            // no arguments
        StopRender,            // arguments: LoEvent::PayloadStopRender
        RemoveSharedMem,       // arguments: QString
        PostUnoCommand,        // arguments: LoEvent::PayloadUnoEvent
        PostKeyEvent,          // arguments: LoEvent::PayloadKeyEvent
        PostMouseEvent,        // arguments: LoEvent::PayloadMouseEvent
        PostSelectEvent,       // arguments: LoEvent::PayloadSelectEvent
        GetTextSelection,      // arguments: QString
        SetPart,               // arguments: int partIndex
        SaveAs,                // arguments: LoEvent::PayloadSaveAs
        GetUnoCommandValues    // arguments: LoEvent::PayloadUnoEvent
    };

    enum class RenderRequestFlags {
        NoFlags = 0,
        Visible = 1,
        Cached = (1 << 1)
    };

    RenderRequestFlags operator|(RenderRequestFlags lhs, RenderRequestFlags rhs);

    RenderRequestFlags operator&(RenderRequestFlags lhs, RenderRequestFlags rhs);

    QDataStream &operator<<(QDataStream &os, const LoEvent::RenderRequestFlags &flags);
    QDataStream &operator>>(QDataStream &is, LoEvent::RenderRequestFlags &flags);

#pragma pack(1)
    struct Header
    {
        Header() { strncpy(signature, EVENT_ID, EVENT_ID_LENGTH); }

        char signature[EVENT_ID_LENGTH];
        CommandType commandType = UnknownCommand;
        int sizePayload = 0;

        friend QDataStream &operator<<(QDataStream &os, const Header &msg);
        friend QDataStream &operator>>(QDataStream &is, Header &msg);
    };
#pragma pack()
    struct Message
    {
        Message() = default;
        template<typename T>
        Message(CommandType commandType, T data)
        {
            header.commandType = commandType;
            QDataStream out(&payload, QIODevice::WriteOnly);
            out << data;
            header.sizePayload = payload.size();
        }

        Header header;
        QByteArray payload;

        friend QDataStream &operator<<(QDataStream &os, const LoEvent::Message &msg);
        friend QDataStream &operator>>(QDataStream &is, LoEvent::Message &msg);
    };

    struct PayloadPageSize
    {
        PayloadPageSize() = default;
        PayloadPageSize(int pageIndex, const QSize &size)
            : pageIndex(pageIndex)
            , size(size)
        {}

        int pageIndex = -1;
        QSize size;

        friend QDataStream &operator<<(QDataStream &os, const LoEvent::PayloadPageSize &msg);
        friend QDataStream &operator>>(QDataStream &is, LoEvent::PayloadPageSize &msg);
    };

    struct PayloadPageName
    {
        PayloadPageName() = default;
        PayloadPageName(int pageIndex, const QString &name)
            : pageIndex(pageIndex)
            , name(name)
        {}

        int pageIndex = -1;
        QString name;

        friend QDataStream &operator<<(QDataStream &os, const LoEvent::PayloadPageName &msg);
        friend QDataStream &operator>>(QDataStream &is, LoEvent::PayloadPageName &msg);
    };

    struct PayloadNewPage
    {
        int pageIndex;
        QSize size;
        qreal zoom;
        QString sharedMemKey;
        RenderRequestFlags renderRequest;

        friend QDataStream &operator<<(QDataStream &os, const LoEvent::PayloadNewPage &msg);
        friend QDataStream &operator>>(QDataStream &is, LoEvent::PayloadNewPage &msg);
    };

    struct PayloadNewTile
    {
        int pageIndex;
        QRect rect;
        QSize canvasSize;
        qreal zoom;
        QString sharedMemKey;
        RenderRequestFlags renderRequest;

        friend QDataStream &operator<<(QDataStream &os, const LoEvent::PayloadNewTile &msg);
        friend QDataStream &operator>>(QDataStream &is, LoEvent::PayloadNewTile &msg);
    };

    struct PayloadMakePage
    {
        int pageIndex;
        QRect rect;
        QSize canvasSize;
        qreal zoom;
        RenderRequestFlags renderRequest = RenderRequestFlags::Visible;

        friend QDataStream &operator<<(QDataStream &os, const LoEvent::PayloadMakePage &msg);
        friend QDataStream &operator>>(QDataStream &is, LoEvent::PayloadMakePage &msg);
    };

    QDebug operator<<(QDebug debug, const CommandType &cmdType);
    QDebug operator<<(QDebug debug, const Message &msg);

    using PayloadMakeTile = PayloadMakePage;
    using PayloadStopRender = PayloadMakePage;

    struct PayloadKeyEvent
    {
        int type;     // Event type, like press or release
        int charCode; // contains the Unicode character generated by this event or 0
        int keyCode;  // contains the integer code representing the key of the event (non-zero for control keys)

        friend QDataStream &operator<<(QDataStream &os, const LoEvent::PayloadKeyEvent &msg);
        friend QDataStream &operator>>(QDataStream &is, LoEvent::PayloadKeyEvent &msg);
    };

    struct PayloadMouseEvent
    {
        LibreOfficeKitMouseEventType type;     // Event type, like down, move or up.
        int x;        // horizontal position in document coordinates
        int y;        // vertical position in document coordinates
        int count;    // number of clicks: 1 for single click, 2 for double click
        int buttons;  // which mouse buttons: 1 for left, 2 for middle, 4 right
        int modifier; // which keyboard modifier: (see include/vcl/vclenum.hxx for possible values)
        int pageIndex;// only Calc

        friend QDataStream &operator<<(QDataStream &os, const LoEvent::PayloadMouseEvent &msg);
        friend QDataStream &operator>>(QDataStream &is, LoEvent::PayloadMouseEvent &msg);
    };

    struct PayloadSelectEvent
    {
        LibreOfficeKitSetTextSelectionType type;
        int x;    // horizontal position in document coordinates
        int y;    // vertical position in document coordinates

        friend QDataStream &operator<<(QDataStream &os, const LoEvent::PayloadSelectEvent &msg);
        friend QDataStream &operator>>(QDataStream &is, LoEvent::PayloadSelectEvent &msg);
    };

    struct PayloadCallbackEvent
    {
        LibreOfficeKitCallbackType type;
        QByteArray payload;

        friend QDataStream &operator<<(QDataStream &os, const LoEvent::PayloadCallbackEvent &msg);
        friend QDataStream &operator>>(QDataStream &is, LoEvent::PayloadCallbackEvent &msg);
    };

    struct PayloadUnoEvent
    {
        QByteArray command;
        QByteArray arguments;
        bool notifyWhenFinished = true;

        friend QDataStream &operator<<(QDataStream &os, const LoEvent::PayloadUnoEvent &msg);
        friend QDataStream &operator>>(QDataStream &is, LoEvent::PayloadUnoEvent &msg);
    };

    struct PayloadSaveAs
    {
        QString path;
        QString format;
        QString filterOptions;

        friend QDataStream &operator<<(QDataStream &os, const LoEvent::PayloadSaveAs &msg);
        friend QDataStream &operator>>(QDataStream &is, LoEvent::PayloadSaveAs &msg);
    };

    struct PayloadSaveAsStatus
    {
        QString path;
        QString format;
        bool status;

        friend QDataStream &operator<<(QDataStream &os, const LoEvent::PayloadSaveAsStatus &msg);
        friend QDataStream &operator>>(QDataStream &is, LoEvent::PayloadSaveAsStatus &msg);
    };

} // namespace LoEvent
