#include "xwldatabridge.h"
#include "xwlutils.h"
#include "xwlprimary.h"
#include "xwlclipboard.h"
extern "C"
{
#include "log.h"
}

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

#include <unistd.h>

#include <string>
#include <thread>
#include <stdio.h>
#include <string.h>

static xcb_connection_t *s_xcbConn = nullptr;

XwlDataBridge::XwlDataBridge(bool force)
{
    // 创建 XCB 连接
    xcb_connection_t *connection = nullptr;
    connection = xcb_connect(nullptr, 0);
    if (xcb_connection_has_error(connection)) {
        for (size_t i = 0; i < 100; i++) {
            std::string displayStr(":");
            displayStr += std::to_string(i);
            // setenv("DISPLAY",displayStr.c_str(),1);
            connection = xcb_connect(displayStr.c_str(), 0);
            if (xcb_connection_has_error(connection) == 0) {
                break;
            }
        }
    }
    if (xcb_connection_has_error(connection)) {
        log_error("Could not open X display\n");
        return;
    }
    const xcb_query_extension_reply_t *queryExtension;
    queryExtension = xcb_get_extension_data(connection, &xcb_xfixes_id);
    if (!queryExtension) {
        log_error("XFixes extension is not present!");
        xcb_disconnect(connection);
        return;
    }

    s_xcbConn = connection;

    // 获取默认屏幕
    xcb_screen_t *screen = xcb_setup_roots_iterator(xcb_get_setup(connection)).data;
    xcb_window_t rootWindow = screen->root;

    m_clipboard = new XwlClipboard(XwlUtils::getAtom("CLIPBOARD", s_xcbConn), rootWindow, queryExtension, s_xcbConn);

    xcb_discard_reply(s_xcbConn, xcb_xfixes_query_version(s_xcbConn, 1, 0).sequence);

    const uint32_t mask = XCB_XFIXES_SELECTION_EVENT_MASK_SET_SELECTION_OWNER
                          | XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_WINDOW_DESTROY
                          | XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_CLIENT_CLOSE;
    if (m_primary) {
        xcb_xfixes_select_selection_input(s_xcbConn,
                                          m_primary->window(),
                                          XwlUtils::getAtom("PRIMARY", s_xcbConn),
                                          mask);
    }
    if (m_clipboard) {
        xcb_xfixes_select_selection_input(s_xcbConn,
                                          m_clipboard->window(),
                                          XwlUtils::getAtom("CLIPBOARD", s_xcbConn),
                                          mask);
    }
    xcb_flush(s_xcbConn);

    // Forced to open
    if (force) {
        m_enableXwlClipboard = true;
        return;
    }

    // Prevent XWL clipboard from already existing
    xcb_query_tree_cookie_t treeCookie = xcb_query_tree(s_xcbConn, rootWindow);
    xcb_query_tree_reply_t *treeReply = xcb_query_tree_reply(s_xcbConn, treeCookie, NULL);
    if (treeReply == nullptr)
        return;
    xcb_window_t *windows = xcb_query_tree_children(treeReply);
    int numWindows = xcb_query_tree_children_length(treeReply);
    for (int i = 0; i < numWindows; ++i) {
        xcb_get_property_cookie_t propCookie = xcb_get_property(connection, 0, windows[i],
                                                                XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 0, 256);
        xcb_get_property_reply_t *propReply = xcb_get_property_reply(connection, propCookie, NULL);
        if (propReply == nullptr)
            continue;
        const int valueLength = xcb_get_property_value_length(propReply);
        if (valueLength > 0) {
            std::string windowName(static_cast<char *>(xcb_get_property_value(propReply)), valueLength);
            if (windowName.compare("Kwin XWL Clipboard") == 0) {
                log_info("XWL clipboard exists!\n");
                m_xwlClipboardExists = true;
            }
            if (windowName.compare("Kwin XWL DND") == 0) {
                m_enableXwlClipboard = true;
            }
        }
        free(propReply);
        if (m_xwlClipboardExists && m_enableXwlClipboard) {
            break;
        }
    }
    free(treeReply);
}

XwlDataBridge::~XwlDataBridge()
{
    stop();
    if (m_clipboard) {
        delete m_clipboard;
        m_clipboard = nullptr;
    }
    // 断开连接
    xcb_disconnect(s_xcbConn);
    s_xcbConn  = nullptr;
}

void XwlDataBridge::stop()
{
    m_stop = true;
    while (m_active) {}
}

void XwlDataBridge::setClipData(ClipData *clipData)
{
    m_clipData = clipData;
    m_clipDataChange = true;
}

int XwlDataBridge::eventLoop()
{
    if (!s_xcbConn || m_active || m_xwlClipboardExists || !m_enableXwlClipboard) {
        return -1;
    }

    m_active = true;

    // 循环监听事件
    xcb_generic_event_t *event;

    while (!m_stop) {
        if (m_clipDataChange && m_clipboard) {
            m_clipDataChange = false;
            m_clipboard->setClipData(m_clipData);
        }
        if (m_primary) {
            m_primary->timeout();
            m_primary->clearEndPropertys();
        }
        if (m_clipboard) {
            m_clipboard->timeout();
            m_clipboard->clearEndPropertys();
        }
        event = xcb_poll_for_event(s_xcbConn);
        if (event) {
            if (m_primary) {
                m_primary->filterEvent(event);
            }
            if (m_clipboard) {
                m_clipboard->filterEvent(event);
            }
            free(event);
            xcb_flush(s_xcbConn);
        }
        usleep(10000);
    }
    m_active = false;

    return 0;
}
