#include "xwlsource.h"
#include "xwlselection.h"
#include "xwlutils.h"
#include "xwlproperty.h"
#include "timer.h"
#include "../datamanage/clipdata.h"
#include "../datamanage/clipdatarecord.h"
#include "../datamanage/clipdataproperty.h"
#include "../systemclipboard.h"
#include "../clipboarddataprocess.h"
#include "../imageprocess.h"
extern "C"
{
#include "log.h"
}

#include <xcb/xfixes.h>

#include <stdio.h>
#include <stdlib.h>
#include <algorithm>

#define XWL_MAX_DATA_SIZE 52428800

using namespace std;

XwlSource::XwlSource(XwlSelection *selection)
    : m_selection(selection)
    , m_window(selection->window())
{
}

void XwlSource::sendSelectionNotify(xcb_selection_request_event_t *event, bool success)
{
    selection()->sendSelectionNotify(event, success);
}

void XwlSource::refreshReadProperty()
{
    m_readRefresh = true;
}

void XwlSource::refreshSendProperty()
{
    m_sendRefresh = true;
}

X11Source::X11Source(XwlSelection *selection, xcb_xfixes_selection_notify_event_t *event)
    : XwlSource(selection)
{
    if (event) {
        setTimestamp(event->timestamp);
        m_readPropertyWindow = event->owner;
    }
    m_readTimer = new Timer(100, false, this);
    m_sendTimer = new Timer(100, false, this);
    m_ownSelectionTimer  = new Timer(100, true, this);
}

X11Source::~X11Source()
{
    if (m_readTimer) {
        delete m_readTimer;
        m_readTimer = nullptr;
    }
    if (m_sendTimer) {
        delete m_sendTimer;
        m_sendTimer = nullptr;
    }
    if (m_ownSelectionTimer) {
        delete m_ownSelectionTimer;
        m_ownSelectionTimer = nullptr;
    }
    for (int i = m_readPropertys.size() - 1; i >= 0; i--) {
        m_readPropertys.erase(m_readPropertys.begin() + i);
    }
    for (int i = m_sendPropertys.size() - 1; i >= 0; i--) {
        m_sendPropertys.erase(m_sendPropertys.begin() + i);
    }
}

void X11Source::timeout()
{
    if (m_readTimeout) {
        m_readTimeout = false;
        if (!m_readRefresh) {
            timeoutReadPropertys();
        } else {
            m_readRefresh = false;
        }
    }
    if (m_sendTimeout) {
        m_sendTimeout = false;
        if (!m_sendRefresh) {
            timeoutReadPropertys();
        } else {
            m_sendRefresh = false;
        }
    }
    if (m_ownSelectionTimeout) {
        timeoutOwnSelection();
        m_ownSelectionTimeout = false;
    }
}

void X11Source::refreshReadProperty()
{
    XwlSource::refreshReadProperty();
    for (auto readProperty : m_readPropertys) {
        if (readProperty && readProperty->isFinished()) {
            readProperty->resetTimeout();
        }
    }
}

void X11Source::refreshSendProperty()
{
    XwlSource::refreshSendProperty();
    for (auto property : m_sendPropertys) {
        if (property && property->isFinished()) {
            property->resetTimeout();
        }
    }
}

void X11Source::setClipData(ClipData *clipData)
{
    vector<string> vMimeData = clipData->getMimeTypes();

    int vRecorderCount = clipData->getRecordCount();

    for (int i = 0; i < vRecorderCount; i++) {
        ClipDataRecord *vDataR = clipData->getRecordAt(i);
        if (!vDataR)
            continue;
        xwlsourcedata t_data;
        t_data.minetype = vDataR->getMimeType();
        t_data.atom = XwlUtils::mimeTypeToAtom(t_data.minetype.c_str(), selection()->x11Connection());
        t_data.path = vDataR->getDataPath();
        m_offers.push_back(make_pair(t_data.minetype, t_data.atom));
        if (ClipboardDataProcess::getDataLength(vDataR->getDataPath().c_str()) < XWL_MAX_DATA_SIZE)
            t_data.data = ClipboardDataProcess::getData(vDataR->getDataPath().c_str());
        m_sourceData.push_back(t_data);
    }
    selection()->ownSelection(true);
}


static void callbackTimeoutOwnSelection(void *arg)
{
    X11Source *source = (X11Source *)arg;
    source->resetOwnSelectionTimeout();
}

void X11Source::timeoutOwnSelection()
{
    if (m_readPropertyWindow == XCB_WINDOW_NONE)
        return;
    auto curOwner = XwlUtils::getSelectionOwner(selection()->x11Connection());
    if (m_endReading && curOwner == XCB_WINDOW_NONE) {
        xcb_screen_t *screen = xcb_setup_roots_iterator(xcb_get_setup(selection()->x11Connection())).data;
        xcb_window_t rootWindow = screen->root;
        xcb_query_tree_cookie_t treeCookie = xcb_query_tree(selection()->x11Connection(), rootWindow);
        xcb_query_tree_reply_t *treeReply = xcb_query_tree_reply(selection()->x11Connection(), treeCookie, NULL);
        if (treeReply == nullptr)
            return;
        xcb_window_t *windows = xcb_query_tree_children(treeReply);
        int numWindows = xcb_query_tree_children_length(treeReply);
        bool exist = false;
        for (int i = 0; i < numWindows; ++i) {
            if (windows[i] == m_readPropertyWindow) {
                exist = true;
                break;
            }
        }
        free(treeReply);
        if (!exist) {
            selection()->ownSelection(true);
        } else {
            SystemClipboard::getInstance().clearClipboardData();
        }
    }
}

void X11Source::processOwnSelection()
{
    if (m_readPropertyWindow == XCB_WINDOW_NONE)
        return;
    auto curOwner = XwlUtils::getSelectionOwner(selection()->x11Connection());
    if (m_ownSelectionTimer && m_endReading && curOwner == XCB_WINDOW_NONE) {
        m_ownSelectionTimer->start(callbackTimeoutOwnSelection, 10);
    }
}

void X11Source::getTargets()
{
    auto *xcbConn = selection()->x11Connection();
    /* will lead to a selection request event for the new owner */
    xcb_convert_selection(xcbConn,
                          window(),
                          selection()->atom(),
                          selection()->targetsAtom(),
                          selection()->wlSelectionAtom(),
                          timestamp());
    xcb_flush(xcbConn);
}

using Mime = pair<string, xcb_atom_t>;
bool X11Source::handleTargets()
{
// receive targets
    auto *xcbConn = selection()->x11Connection();
    xcb_get_property_cookie_t cookie = xcb_get_property(xcbConn,
                                                        1,
                                                        window(),
                                                        selection()->wlSelectionAtom(),
                                                        XCB_GET_PROPERTY_TYPE_ANY,
                                                        0,
                                                        4096);
    auto *reply = xcb_get_property_reply(xcbConn, cookie, nullptr);
    if (!reply) {
        log_error("Failed to get selection property\n");
        return false;
    }
    if (reply->type != XCB_ATOM_ATOM) {
        log_error("Wrong reply type\n");
        free(reply);
        return false;
    }

    vector<string> added;
    vector<string> removed;

    Mimes all;
    xcb_atom_t *value = static_cast<xcb_atom_t *>(xcb_get_property_value(reply));
    for (uint32_t i = 0; i < reply->value_len; i++) {
        if (value[i] == XCB_ATOM_NONE) {
            continue;
        }

        const auto mimeStrings = XwlUtils::atomName(value[i], xcbConn);
        if (mimeStrings.empty() || !strcmp(mimeStrings.c_str(), "TARGETS")
                || !strcmp(mimeStrings.c_str(), "MULTIPLE") || !strcmp(mimeStrings.c_str(), "SAVE_TARGETS")) {
            // TODO: this should never happen? assert?
            continue;
        }

        const auto mimeIt = find_if(m_offers.begin(), m_offers.end(),
        [value, i](const Mime & mime) {
            return mime.second == value[i];
        });

        auto mimePair = Mime(mimeStrings, value[i]);
        if (mimeIt == m_offers.end()) {
            added.push_back(mimePair.first);
        } else {
            for (int i = m_offers.size() - 1; i >= 0; i--) {
                if (m_offers[i].first == mimePair.first && m_offers[i].second == mimePair.second) {
                    m_offers.erase(m_offers.begin() + i);
                }
            }
        }
        const auto allIt = find_if(all.begin(), all.end(),
        [value, i](const Mime & mime) {
            return mime.second == value[i];
        });
        if (allIt == all.end())
            all.push_back(mimePair);
    }
    // all left in m_offers are not in the updated targets
    for (auto &mimePair : m_offers) {
        removed.push_back(mimePair.first);
    }
    m_offers = all;

    free(reply);

    return (!added.empty() || !removed.empty());
}

int X11Source::dataProcessToclip()
{
    vector<string> curFormats;
    vector<string> computeFormats;
    long long lastOfferTime = ClipboardDataProcess::getCurrentTime();
    for (int i = 0; i < m_sourceData.size(); i++) {
        curFormats.push_back(m_sourceData[i].minetype);
        auto mimeStrings = XwlUtils::atomToMimeTypes(m_sourceData[i].atom, selection()->x11Connection());
        if (!mimeStrings.empty()) {
            computeFormats.push_back(mimeStrings[0]);
        }
    }
    // 适配厂商云桌面粘贴问题
    if (ClipboardDataProcess::isContains(curFormats, "uos/remote-copy")) {
        log_error("Data from SHENXINFU_CLIPBOARD_MANAGER, ignored \n");
        return -2;
    }

    // 转移系统剪贴板所有权时造成的两次内容变化不需要显示，以下为与系统约定好的标识
    if (ClipboardDataProcess::isContains(curFormats, "FROM_DEEPIN_CLIPBOARD_MANAGER")) {
        log_error("Data from DEEPIN_CLIPBOARD_MANAGER, ignored \n");
        return -3;
    }

    ClipDataProperty *t_dataProperty = new ClipDataProperty();
    ClipData *t_clipdata = new ClipData();
    t_clipdata->setProperty(t_dataProperty);
    string t_path = SystemClipboard::getInstance().getPath();
    map<string, string> &t_formatMap = t_dataProperty->getMimeTypes();

    vector<string> vMimeNames;

    for (int i = 0; i < m_sourceData.size(); i++) {
        xwlsourcedata &t_sourcedata = m_sourceData[i];
        ClipDataRecord *t_datarecord = new ClipDataRecord();
        auto mimeStrings = XwlUtils::atomToMimeTypes(t_sourcedata.atom, selection()->x11Connection());
        if (mimeStrings.empty() || ClipboardDataProcess::isExists(vMimeNames, mimeStrings[0])) {
            delete t_datarecord;
            t_datarecord = nullptr;
            continue;
        }
        if (mimeStrings[0] != t_sourcedata.minetype && ClipboardDataProcess::isExists(curFormats, mimeStrings[0])
                && ClipboardDataProcess::isExists(curFormats, t_sourcedata.minetype)) {
            delete t_datarecord;
            t_datarecord = nullptr;
            continue;
        }
        t_datarecord->setMimeType(mimeStrings[0]);
        // t_datarecord->setMimeType(t_sourcedata.minetype);
        t_datarecord->setDataPath(t_sourcedata.path);
        // 图片类型的数据直接把数据拿出来，不去调用mimeData->data()方法，会导致很卡
        if (ClipboardDataProcess::isContains(computeFormats, "image")) {
            vector<char> imageData = ClipboardDataProcess::readImage(t_sourcedata.path.c_str());

            if (imageData.empty()) {
                delete t_datarecord;
                t_datarecord = nullptr;
                continue;
            }

            int t_w = 0;
            int t_h = 0;
            int rw = 0;
            int rh = 0;
            vector<char> simageData = ImageDataProcess::scaleImage(imageData, t_sourcedata.minetype, t_w, t_h, rw, rh);
            if (simageData.empty()) {
                vector<char> t_str = ClipboardDataProcess::getLenData(t_sourcedata.path.c_str(), TextSmalllen);
                t_datarecord->setData(&t_str[0], t_str.size());
            } else {
                ClipboardDataProcess::update_file_img_size(t_sourcedata.path.c_str(), rw, rh);
                t_datarecord->setImageSize(t_w, t_h);
                t_datarecord->setData(&simageData[0], simageData.size());
                if (t_formatMap.empty()) {
                    int len = 0;
                    t_formatMap.insert(make_pair(ApplicationXQtImageLiteral, string(t_datarecord->getData(len))));
                    t_formatMap.insert(make_pair("TIMESTAMP", std::to_string(lastOfferTime)));
                }
            }

            t_datarecord->setDataType("Image");
        } else if (ClipboardDataProcess::isContains(computeFormats, "file")) {
            if (string("x-dfm-copied/file-icons") == t_sourcedata.minetype) {
                t_datarecord->setData("x-dfm-copied/file-icons", string("x-dfm-copied/file-icons").size());
                t_formatMap.insert(make_pair(t_sourcedata.minetype, ""));
            } else {
                vector<char> t_str = ClipboardDataProcess::readText(t_sourcedata.path.c_str());
                vector<string> t_urls = ClipboardDataProcess::splitString(t_str, "\n");
                for (size_t k = 0; k < t_urls.size(); k++) {
                    /* code */
                    if (t_urls[k].empty()) {
                        /* code */
                        t_urls.erase(t_urls.begin() + k);
                        k--;
                    } else {
                        auto rPos = t_urls[k].find_last_of('\r');
                        if (rPos != string::npos) {
                            t_urls[k].erase(rPos);
                        }
                    }
                }
                if (t_urls.empty()) {
                    delete t_datarecord;
                    t_datarecord = nullptr;
                    continue;
                }
                t_datarecord->setData(&t_str[0], t_str.size());
                t_datarecord->setUri(t_urls);
                t_formatMap.insert(make_pair(t_sourcedata.minetype, string(t_str.begin(), t_str.end())));
            }
            t_datarecord->setDataType("File");
        } else {
            if (ClipboardDataProcess::isContains(computeFormats, "text/plain")) {
                vector<char> t_str = ClipboardDataProcess::getLenData(t_sourcedata.path.c_str(), TextSmalllen);
                t_datarecord->setData(&t_str[0], t_str.size());
                t_formatMap.insert(make_pair(t_sourcedata.minetype, string(t_str.begin(), t_str.end())));
                t_datarecord->setDataType("Text");
            } else if (ClipboardDataProcess::isContains(computeFormats, "html")) {
                vector<char> t_str = ClipboardDataProcess::getLenData(t_sourcedata.path.c_str(), TextSmalllen);
                t_datarecord->setData(&t_str[0], t_str.size());
                t_formatMap.insert(make_pair(t_sourcedata.minetype, string(t_str.begin(), t_str.end())));
                // t_datarecord->setDataType("Html");
                t_datarecord->setDataType("Text");
            }
        }

        t_clipdata->addRecordData(t_datarecord);
        vMimeNames.push_back(mimeStrings[0]);
    }

    if (t_formatMap.empty()) {
        delete t_clipdata;
        t_clipdata = nullptr;
        return -4;
    }
    t_dataProperty->setTimestamp(ClipboardDataProcess::getCurrentTime());
    t_clipdata->setDataState(true);
    t_clipdata->setName(std::to_string(ClipboardDataProcess::getCurrentTime()) + "_clip");

    ClipData *vDataOld = ClipboardDataProcess::compareData(t_clipdata);
    if (vDataOld) {
        int vDataCount = SystemClipboard::getInstance().getClipDataCount();
        bool flag = false;
        ClipData *vClip = SystemClipboard::getInstance().getClipDataAt(vDataCount - 1);
        if (vDataOld == vClip) {
            int cancelState = SystemClipboard::getInstance().cancelTopData();
            if (cancelState == 0) {
                vDataOld->getProperty()->setTimestamp(ClipboardDataProcess::getCurrentTime());
                SystemClipboard::getInstance().dataAddProcess(vDataCount - 1);
            }
            return -5;
        }
        // WaylandClip_Set_Selection(clip);
        vDataOld->getProperty()->setTimestamp(ClipboardDataProcess::getCurrentTime());
        int index = SystemClipboard::getInstance().setDataTop(vDataOld);
        SystemClipboard::getInstance().dataDeleteProcess(vDataOld->getName());
        SystemClipboard::getInstance().dataAddProcess(index);
        SystemClipboard::getInstance().setDataToClipboard(t_clipdata, false);
        log_info("Data is exits! \n");
        return -5;
    }
    int index = SystemClipboard::getInstance().addClipData(t_clipdata);
    SystemClipboard::getInstance().dataAddProcess(index);
    SystemClipboard::getInstance().setDataToClipboard(t_clipdata, false);
    return 1;
}

bool X11Source::isSpecialMimetype(string mimetype)
{
    for (size_t i = 0; i < m_offers.size(); i++) {
        if (m_offers[i].first == mimetype) {
            return  true;
        }
    }
    return false;
}

bool X11Source::handleSelectionNotify(xcb_selection_notify_event_t *event)
{
    if (event->requestor != window()) {
        return false;
    }
    if (event->selection != selection()->atom()) {
        return false;
    }
    if (event->property == XCB_ATOM_NONE) {
        log_error("Incoming X selection conversion failed\n");
        return true;
    }
    if (event->target == selection()->targetsAtom()) {
        handleTargets();
        return true;
    }
    return false;
}

bool X11Source::handleSelectionRequest(xcb_selection_request_event_t *event)
{
    if (event->target == selection()->targetsAtom()) {
        sendTargets(event);
    } else if (event->target == selection()->timestampAtom()) {
        sendTimestamp(event);
    } else if (event->target == selection()->deleteAtom()) {
        selection()->sendSelectionNotify(event, true);
    } else {
        // try to send mime data
        if (!startSendProperty(event)) {
            selection()->sendSelectionNotify(event, false);
        }
    }
    return true;
}

bool X11Source::handleReadSelectionNotify(xcb_selection_notify_event_t *event)
{
    for (auto readProperty : m_readPropertys) {
        if (readProperty && readProperty->handleSelectionNotify(event)) {
            if (readProperty->incr()) {
                m_readTimer->setCount(100);
            } else {
                m_readTimer->setCount(20);
            }
            return true;
        }
    }
    return false;
}

bool X11Source::handleReadPropertyNotify(xcb_property_notify_event_t *event)
{
    for (auto readProperty : m_readPropertys) {
        if (readProperty->handlePropertyNotify(event)) {
            return true;
        }
    }
    return false;
}

bool X11Source::handleSendPropertyNotify(xcb_property_notify_event_t *event)
{
    for (auto property : m_sendPropertys) {
        if (property->handlePropertyNotify(event)) {
            return true;
        }
    }
    return false;
}

static void callbackTimeoutReadPropertys(void *arg)
{
    X11Source *source = (X11Source *)arg;
    source->resetReadTimeout();
}

void X11Source::timeoutReadPropertys()
{
    for (auto property : m_readPropertys) {
        if (property)
            property->timeout();
    }
}

bool X11Source::isValidOffer(const std::string& offer) const {

    const std::vector<std::string> acceptedTypes = { "text/plain", "STRING", "TEXT" };

    if (std::find(acceptedTypes.begin(), acceptedTypes.end(), offer) != acceptedTypes.end()) {
        return true;
    }
    return !(offer.find("image") == std::string::npos);
}

void X11Source::startReadPropertys()
{
    m_readTimer->stop();

    if (isSpecialMimetype("uos/remote-copy")) {
        return;
    }

    log_info("Start reading propertys time: %lld\n", ClipboardDataProcess::getCurrentTime());
    bool kingsoftStatus = false;
    for (size_t i = 0; i < m_offers.size(); i++) {
        if (m_offers[i].first.find("Kingsoft") != std::string::npos) {
            kingsoftStatus = true;
            break;
        }
    }
    for (size_t i = 0; i < m_offers.size(); i++) {
        if (kingsoftStatus && !isValidOffer(m_offers[i].first)) {
            continue;
        }
        auto readProperty = make_shared<XwlReadProperty>(selection()->atom(), m_offers[i].second, timestamp(), selection()->requestorWindow(), selection()->x11Connection(), this, i, m_offers[i].first);
        m_readPropertys.push_back(readProperty);
    }
    m_readTimer->start(callbackTimeoutReadPropertys, 20);
}

static void callbackTimeoutSendPropertys(void *arg)
{
    X11Source *source = (X11Source *)arg;
    source->resetSendTimeout();
}

void X11Source::timeoutSendPropertys()
{
    for (auto property : m_sendPropertys) {
        if (property) {
            property->timeout();
        }
    }
}

bool X11Source::startSendProperty(xcb_selection_request_event_t *event)
{
    const auto targets = XwlUtils::atomName(event->target, selection()->x11Connection());
    if (targets.empty()) {
        log_error("Unknown selection atom. Ignoring request.\n");
        return false;
    }
    const auto firstTarget = targets;

    auto cmp = [event](const Mime & b) {
        return event->target == b.second;
    };
    // check supported mimes
    const auto mimeIt = find_if(m_offers.begin(), m_offers.end(), cmp);
    if (mimeIt == m_offers.end()) {
        // Requested Mime not supported. Not sending selection.
        return false;
    }

    auto sendProperty = make_shared<XwlSendProperty>(selection()->atom(), new xcb_selection_request_event_t(*event), selection()->x11Connection(), this);

    for (int i = 0; i < m_sourceData.size(); i++) {
        if (m_sourceData[i].atom == sendProperty->targetProperty()) {
            if (m_sourceData[i].data.empty()) {
                sendProperty->loadData(m_sourceData[i].path);
            } else {
                sendProperty->loadData(m_sourceData[i].data);
            }
        }
    }
    if (!sendProperty->isFinished()) {
        m_sendPropertys.push_back(sendProperty);
        m_sendTimer->start(callbackTimeoutSendPropertys, 50);
    }

    return true;
}

void X11Source::sendTargets(xcb_selection_request_event_t *event)
{
    vector<xcb_atom_t> targets;
    targets.resize(m_offers.size() + 2);
    targets[0] = selection()->timestampAtom();
    targets[1] = selection()->targetsAtom();

    size_t cnt = 2;
    for (const auto &mime : m_offers) {
        if (mime.second == selection()->timestampAtom() || mime.second == selection()->targetsAtom()) {
            continue;
        }
        targets[cnt] = mime.second;
        cnt++;
    }
    targets.resize(cnt);

    xcb_change_property(selection()->x11Connection(),
                        XCB_PROP_MODE_REPLACE,
                        event->requestor,
                        event->property,
                        XCB_ATOM_ATOM,
                        32, cnt, targets.data());
    selection()->sendSelectionNotify(event, true);
}

void X11Source::sendTimestamp(xcb_selection_request_event_t *event)
{
    const xcb_timestamp_t time = timestamp();
    xcb_change_property(selection()->x11Connection(),
                        XCB_PROP_MODE_REPLACE,
                        event->requestor,
                        event->property,
                        XCB_ATOM_INTEGER,
                        32, 1, &time);

    selection()->sendSelectionNotify(event, true);
}

void X11Source::clearEndPropertys()
{
    bool finished = true;
    bool readPropertysEmpty = m_readPropertys.empty();
    for (int i = m_readPropertys.size() - 1; i >= 0; i--) {
        if (m_readPropertys[i] == nullptr) {
            m_readPropertys.erase(m_readPropertys.begin() + i);
            continue;
        }
        bool eraseFlag = false;
        if (m_readPropertys[i]->isFinished()) {
            if (!m_readPropertys[i]->isConvertFlag()) {
                xwlsourcedata t_data;
                t_data.atom = m_readPropertys[i]->targetProperty();
                t_data.minetype = m_readPropertys[i]->getMimeType();
                t_data.path = m_readPropertys[i]->getPath();
                t_data.data = m_readPropertys[i]->data();
                if (!t_data.path.empty()) {
                    const auto mimeIt = find_if(m_sourceData.begin(), m_sourceData.end(),
                    [t_data](const xwlsourcedata & mime) {
                        return mime.atom == t_data.atom;
                    });
                    if (mimeIt != m_sourceData.end()) {
                        m_sourceData.erase(mimeIt);
                    }
                    m_sourceData.push_back(t_data);
                }
                m_readPropertys[i]->setConcertFlag(true);
            }
            if (m_readPropertys[i]->isWirteFinished()) {
                m_readPropertys.erase(m_readPropertys.begin() + i);
                eraseFlag = true;
            }
        }
        if (!eraseFlag && (!m_readPropertys[i]->isFinished() || !m_readPropertys[i]->isConvertFlag())) {
            finished = false;
        }
    }

    if (!m_readTimer->isStop() && finished) {
        m_readTimer->stop();

        // bool spreadsheets = false;
        // for (int i = 0; i < m_sourceData.size(); i++) {
        //     if (m_sourceData[i].minetype.size() == 14 && m_sourceData[i].minetype.compare(0, 14, "WeWork Message") == 0) {
        //         spreadsheets = true;
        //         break;
        //     }
        // }
        // if (spreadsheets)
        //     selection()->ownSelection(true);
        // else
        m_endReading = true;
    }
    if (!readPropertysEmpty && m_readPropertys.empty()) {
        dataProcessToclip();
        log_info("Time to end reading propertys: %lld\n", ClipboardDataProcess::getCurrentTime());
    }

    for (int i = m_sendPropertys.size() - 1; i >= 0; i--) {
        if (m_sendPropertys[i] != nullptr && m_sendPropertys[i]->isFinished()) {
            m_sendPropertys.erase(m_sendPropertys.begin() + i);
        }
    }
    if (m_sendPropertys.empty() && !m_sendTimer->isStop()) {
        m_sendTimer->stop();
    }
}
