/*
 * QT Client for X2GoKDrive
 * Copyright (C) 2018-2023 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de>
 * Copyright (C) 2018-2023 phoca-GmbH
 *
 * 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 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#ifndef XCBCLIP_H
#define XCBCLIP_H
#include <QMainWindow>
#include <QClipboard>

#include <xcb/xcb.h>
#include <xcb/xfixes.h>
#include <QThread>

#include "client.h"

class XCBClip : public QObject
{
    Q_OBJECT
public:
    XCBClip(Client* parent);
    ~XCBClip();
    void setInputSelectionData(SelectionType selection, SelectionMime mime, bool firstChunk, bool lastChunk, uint32_t compressed, uint size, char* data, bool notify=false);
    void requestSelectionData(SelectionType selection);

private:

    xcb_atom_t atom(const QString& name);
    QString atom_name(xcb_atom_t xatom);
    QStringList atomsInReply(xcb_get_property_reply_t *reply);
    xcb_atom_t best_atom_from_list(const QStringList& atoms);
    void process_selection_notify(xcb_generic_event_t *e);
    bool process_selection_request(xcb_generic_event_t *e);
    void request_selection_data( xcb_atom_t selection, xcb_atom_t target, xcb_atom_t property, xcb_timestamp_t t);
    void process_property_notify(xcb_generic_event_t *e);
    void read_selection_property(xcb_atom_t selection, xcb_atom_t property);
    bool is_string_atom( xcb_atom_t at);
    bool is_image_atom( xcb_atom_t at);
    xcb_atom_t target_has_atom(const QStringList& atoms, const QString& name);
    void own_selection(SelectionType selection);
    void send_mime_types(xcb_selection_request_event_t* req);
    xcb_atom_t send_data(xcb_selection_request_event_t* req);
    xcb_atom_t set_data_property(xcb_selection_request_event_t* req, QByteArray* data);
    SelectionType selection_from_atom(xcb_atom_t selection);
    xcb_atom_t atom_from_selection(SelectionType selection);
    QString mime_to_QT_img(const QString& mimeType);
    void delay_selection_request( xcb_selection_request_event_t *reqest, xcb_selection_notify_event_t* event);
    bool check_req_sanity(xcb_selection_request_event_t* req);
    void processDelayedRequests();
    void discardDelayedRequest(uint index);
    void updateCurrentTime(xcb_timestamp_t t);
    xcb_timestamp_t currentXTime();
    void startIncrTransaction(xcb_window_t requestor, xcb_atom_t property, xcb_atom_t target, QByteArray data);
    void process_incr_transaction_property(xcb_property_notify_event_t * pn);
    void destroy_incr_transaction(int index);
    void remove_obsolete_incr_transactions(bool checkTS=true);

private slots:
    void checkEvents();
private:
    Client* parent;
    const xcb_query_extension_reply_t *reply;
    xcb_window_t clipWinId;
    xcb_connection_t* con;
//output selection
    xcb_atom_t currentSelection;
    xcb_atom_t incrAtom=0;
    uint incrementalSize=0;
    uint incrementalSizeRead=0;
    xcb_atom_t best_atom[2]; //the best mime type for selection to request on demand sel request from server
    xcb_atom_t ATOM_CLIPBOARD=0; //atom for CLIPBOARD selection

//input selection

    struct InputSelection{
        SelectionMime selMime; //mime of selection (string or image)
        QByteArray selData; //data
        bool owner=false; //if we are owners of this selction
        enum { NOTIFIED, REQUESTED, COMPLETED} state; //state of the selection
        xcb_timestamp_t timestamp; //timestamp when we own selection
    }inputSelection[2];

    //requests which processing should be delayed till we get data from server
    struct DelayedRequest
    {
        xcb_selection_request_event_t *request; // request from client
        xcb_selection_notify_event_t* event; // event which we are going to send to client
    };
    QList <DelayedRequest*> delayedSelectionRequests;

    //save running INCR transactions in this struct
    struct IncrTransaction
    {
        xcb_window_t requestor;
        xcb_atom_t property;
        xcb_atom_t target;
        QByteArray data;
        uint sentBytes;
        qint64 timestamp;
    };
    QList <IncrTransaction*> incrTransactions;


    xcb_timestamp_t lastXTime=XCB_TIME_CURRENT_TIME; //current time of X Server
    int32_t timeDifference=0; //differense between X server time and system time

    uint32_t total_compressed=0;
};
#endif
