#pragma once

#include <xcb/xcb.h>

#include <vector>
#include <string>
#include <memory>
#include <atomic>

struct xcb_selection_request_event_t;
struct xcb_xfixes_selection_notify_event_t;

class XwlSelection;
class XwlReadProperty;
class XwlSendProperty;
class ClipData;

class XwlSource
{
public:
    XwlSource(XwlSelection *selection);
    virtual ~XwlSource() {}

    xcb_timestamp_t timestamp() const
    {
        return m_timestamp;
    }

    void setTimestamp(xcb_timestamp_t time)
    {
        m_timestamp = time;
    }

    void sendSelectionNotify(xcb_selection_request_event_t *event, bool success);

    virtual void clearEndPropertys() {}

    virtual void timeout() {}

    virtual void refreshReadProperty();

    virtual void refreshSendProperty();

    void resetReadTimeout()
    {
        m_readTimeout = true;
    }
    void resetSendTimeout()
    {
        m_sendTimeout = true;
    }
    void resetOwnSelectionTimeout()
    {
        m_ownSelectionTimeout = true;
    }

protected:
    XwlSelection *selection() const
    {
        return m_selection;
    }

    void setWindow(xcb_window_t window)
    {
        m_window = window;
    }

    xcb_window_t window() const
    {
        return m_window;
    }

protected:
    std::atomic<bool> m_readTimeout{false};
    std::atomic<bool> m_readRefresh{false};
    std::atomic<bool> m_sendTimeout{false};
    std::atomic<bool> m_sendRefresh{false};
    std::atomic<bool> m_ownSelectionTimeout{false};

private:
    xcb_timestamp_t m_timestamp = XCB_CURRENT_TIME;
    XwlSelection *m_selection = nullptr;
    xcb_window_t m_window = XCB_WINDOW_NONE;
};

using Mimes = std::vector<std::pair<std::string, xcb_atom_t> >;
class Timer;

struct xwlsourcedata {
    xcb_atom_t atom;
    std::string    minetype;
    std::vector<char>  data;
    std::string    path;
};

class X11Source : public XwlSource
{
public:
    X11Source(XwlSelection *selection, xcb_xfixes_selection_notify_event_t *event = nullptr);
    ~X11Source() override;

    void timeout() override;
    void refreshReadProperty() override;
    void refreshSendProperty() override;
    void setClipData(ClipData *clipData);
    void processOwnSelection();
    void getTargets();

    bool handleSelectionNotify(xcb_selection_notify_event_t *event);
    bool handleSelectionRequest(xcb_selection_request_event_t *event);

    bool handleReadSelectionNotify(xcb_selection_notify_event_t *event);
    bool handleReadPropertyNotify(xcb_property_notify_event_t *event);
    bool handleSendPropertyNotify(xcb_property_notify_event_t *event);

    void startReadPropertys();
    void timeoutReadPropertys();

    bool isValidOffer(const std::string& offer) const;

    void setRequestor(xcb_window_t window)
    {
        setWindow(window);
    }

    void sendTargets(xcb_selection_request_event_t *event);
    void sendTimestamp(xcb_selection_request_event_t *event);
    void timeoutSendPropertys();

    void clearEndPropertys() override;

    xcb_window_t readPropertyWindow() const
    {
        return m_readPropertyWindow;
    }

    void timeoutOwnSelection();

private:
    bool startSendProperty(xcb_selection_request_event_t *event);
    bool handleTargets();

    int dataProcessToclip();

    bool isSpecialMimetype(std::string mimetype);

private:
    xcb_window_t m_readPropertyWindow = XCB_WINDOW_NONE;
    Mimes m_offers;
    bool m_endReading{false};
    std::vector<std::shared_ptr<XwlReadProperty> > m_readPropertys;
    std::vector<std::shared_ptr<XwlSendProperty> > m_sendPropertys;

    std::vector<xwlsourcedata>      m_sourceData;

    Timer *m_readTimer{nullptr};
    Timer *m_sendTimer{nullptr};
    Timer *m_ownSelectionTimer{nullptr};
};
