/*
 * Copyright (C) 2024 Uniontech Technology Co., Ltd.
 *
 * Author:     liuzheng <liuzheng@uniontech.com>
 *
 * Maintainer: liuzheng <liuzheng@uniontech.com>
 *
 * 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
 * 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/>.
 */

#include "systemclipboard.h"
#include "datamanage/clipdata.h"
#include "datamanage/clipdatarecord.h"
#include "x11clipboard.h"
#include "waylandclipboard.h"
#include "clipboarddataprocess.h"
#include "displayjack_clipboard.h"
extern "C"
{
#include "log.h"
}

#include <fstream>
#include <string.h>
#include <stdlib.h>
#include <filesystem>
#include <algorithm>
SystemClipboard *SystemClipboard::m_systemClipboard = new SystemClipboard; // 定义并初始化静态成员变
ClipData *SystemClipboard::getClipDataAt(int index)
{
    lock_guard<std::mutex> lock(m_clipboardMutex);
    if (index < m_clipdata.size()) {
        return m_clipdata[index];
    }
    return nullptr;
}

int SystemClipboard::getClipDataCount()
{
    lock_guard<std::mutex> lock(m_clipboardMutex);
    return m_clipdata.size();
}

void SystemClipboard::removeAt(int index)
{
    m_clipboardMutex.lock();
    if (index >= 0 && index < m_clipdata.size() && m_CurrentTopData == nullptr) {
        //数据不为空的情况下最后一个不删除
        if (index == m_clipdata.size() - 1 && m_clipdata.size() > 0) {
            m_CurrentTopData = m_clipdata[index];
            m_CurrentTopData->setDataState(false);
        } else {
            delete m_clipdata[index];
            m_clipdata[index] = nullptr;
            m_clipdata.erase(m_clipdata.begin() + index);
        }
    } else if (index >= 0 && index < m_clipdata.size() && m_CurrentTopData) {
        delete m_clipdata[index];
        m_clipdata[index] = nullptr;
        m_clipdata.erase(m_clipdata.begin() + index);
    }
    saveClipdata();
    m_clipboardMutex.unlock();
    if (m_clipdata.empty())
        clear();
}

void SystemClipboard::moveAt(int oldindex, int newindex)
{
    lock_guard<std::mutex> lock(m_clipboardMutex);
    if (oldindex < m_clipdata.size() && newindex < m_clipdata.size()) {
        ClipData *t_data = m_clipdata[oldindex];
        m_clipdata[oldindex] = m_clipdata[newindex];
        m_clipdata[newindex] = t_data;
        saveClipdata();
    }
}

ClipData *SystemClipboard::createClipData()
{
    ClipData *t_data = new ClipData();
    deleteTopData();
    m_clipdata.push_back(t_data);
    if (m_clipdata.size() > m_clipNum) {
        delete m_clipdata[0];
        m_clipdata[0] = nullptr;
        m_clipdata.erase(m_clipdata.begin() + 0);
        saveClipdata();
        if (m_clipdata.size() == 0)
            setDataToClipboard(m_clipdata[m_clipdata.size() - 1]);
    }
    return t_data;
}

int SystemClipboard::addClipData(ClipData *_data)
{
    lock_guard<std::mutex> lock(m_clipboardMutex);
    deleteTopData();
    m_clipdata.push_back(_data);
    if (m_clipdata.size() > m_clipNum) {
        delete m_clipdata[0];
        m_clipdata[0] = nullptr;
        m_clipdata.erase(m_clipdata.begin() + 0);
        saveClipdata();
    }
    return m_clipdata.size() - 1;
}

void SystemClipboard::clear()
{
    lock_guard<std::mutex> lock(m_clipboardMutex);
//    if (m_waylandclipboard) {
//        m_waylandclipboard->clearClipboard();
//    }

    // if (m_x11clipboard)
    // {
    //     m_x11clipboard->clearClipboard();
    // }
    //始终保留最后一个数据
    int vCount = m_clipdata.size() - 1;
    for (int i = vCount - 1; i >= 0; i--) {
        delete m_clipdata[i];
        m_clipdata[i] = nullptr;
        m_clipdata.erase(m_clipdata.begin() + i);
    }
    if (m_clipdata.size() == 1) {
        m_CurrentTopData = m_clipdata[0];
        m_CurrentTopData->setDataState(false);
    }

    //m_clipdata.clear();
    saveClipdata();
}

int SystemClipboard::getClipIndexByName(string name)
{
    lock_guard<std::mutex> lock(m_clipboardMutex);
    int t_index = -1;
    for (int i = 0; i < m_clipdata.size(); i++) {
        if (m_clipdata[i]->getName() == name) {
            t_index = i;
            break;
        }
    }
    return t_index;
}

int SystemClipboard::loadClipData(string path)
{
    lock_guard<std::mutex> lock(m_clipboardMutex);
    if (m_Isload)
        return -2;

    m_Isload = true;
    if (path.empty())
        path = ClipboardDataProcess::getClipboardDir(cachePropertyDir) + "/clipboardData.bin";
    // 打开文件，如果失败则返回
    ifstream file(path, ios::binary);
    if (!file.is_open()) {
        //initConnect();
        return -1;
    }

    file.read((char *)&m_version, sizeof(int));
    // 读取剪贴板数据的数量
    int count = 0;
    file.read((char *)&count, sizeof(int));

    // 遍历每个剪贴板数据对象，并调用其load方法将其从文件中读取
    for (int i = 0; i < count; i++) {
        ClipData *clip = createClipData();
        clip->load(file);
        if (!clip->getDataState()) {
            int vIndex = m_clipdata.size() - 1;
            delete m_clipdata[vIndex];
            m_clipdata[vIndex] = nullptr;
            m_clipdata.erase(m_clipdata.begin() + vIndex);
        }
    }

    // 关闭文件
    file.close();
    if (!m_clipdata.empty())
        setDataToClipboard(m_clipdata[m_clipdata.size() - 1]);
    vector<string> filelist;
    for (int i = 0; i < m_clipdata.size(); i++) {
        int vRcount = m_clipdata[i]->getRecordCount();
        for (size_t j = 0; j < vRcount; j++) {
            /* code */
            ClipDataRecord *vClipDataRecord = m_clipdata[i]->getRecordAt(j);
            if (!vClipDataRecord->getDataPath().empty())
                filelist.push_back(vClipDataRecord->getDataPath());
        }
    }

    ClipboardDataProcess::delete_non_list_files(ClipboardDataProcess::getClipboardDir(cachedataDir), filelist);
    // 返回读取的数据数量
    //initConnect();
    log_info("load successfully.\n");
    return count;
}

void SystemClipboard::saveClipdata(string path)
{
    string savePath = "";
    if (path.empty()) {
        path = ClipboardDataProcess::getClipboardDir(cachePropertyDir) + "/clipboardData.bin";
        savePath = ClipboardDataProcess::getClipboardDir(cachePropertyDir) + "/clipboardData_new.bin";
    } else {
        savePath = "/tmp/clipboardData_new.bin";
    }
    // 打开文件，如果失败则返回
    ofstream file(savePath, ios::binary);
    if (!file.is_open())
        return;

    file.write((char *)&m_version, sizeof(int));

    // 获取剪贴板数据的数量，并写入文件
    int count = m_clipdata.size();
    file.write((char *)&count, sizeof(int));

    // 遍历每个剪贴板数据对象，并调用其save方法将其写入文件
    for (int i = 0; i < count; i++) {
        ClipData *clip = m_clipdata[i];
        clip->save(file);
    }

    // 关闭文件
    file.close();

    int result = std::rename(savePath.c_str(), path.c_str());
    if (result == 0) {
        log_info("File renamed successfully.\n");
    } else {
        log_error("Error renaming file \n");
    }
}

void SystemClipboard::removeData()
{
    lock_guard<std::mutex> lock(m_clipboardMutex);
    string path = ClipboardDataProcess::getClipboardDir(cachePropertyDir);
    ClipboardDataProcess::delete_directory(path.c_str());
    saveClipdata();
}

bool SystemClipboard::initDir()
{
    string vDatadir = ClipboardDataProcess::getClipboardDir(cachePropertyDir);
    if (!ClipboardDataProcess::isDirExist(vDatadir.c_str())) {
        ClipboardDataProcess::create_multi_dir(vDatadir.c_str());
    }
    m_dataptah = ClipboardDataProcess::getClipboardDir(cachedataDir);
    if (!ClipboardDataProcess::isDirExist(m_dataptah.c_str())) {
        ClipboardDataProcess::create_multi_dir(m_dataptah.c_str());
    }
    return true;
}

ClipData *SystemClipboard::getActiveClipData()
{
    lock_guard<std::mutex> lock(m_clipboardMutex);
    if (m_clipdata.empty())
        return NULL;
    return m_clipdata[m_clipdata.size() - 1];
}

string SystemClipboard::getPath()
{
    lock_guard<std::mutex> lock(m_clipboardMutex);
    return m_dataptah;
}

SystemClipboard::~SystemClipboard()
{
    if (m_waylandclipboard) {
        delete m_waylandclipboard;
        m_waylandclipboard = NULL;
    }

    if (m_x11clipboard) {
        delete m_x11clipboard;
        m_x11clipboard = NULL;
    }
    for (int i = 0; i < m_clipdata.size(); i++) {
        delete m_clipdata[i];
        m_clipdata[i] = nullptr;
    }
    m_clipdata.clear();
}

void SystemClipboard::destroyData()
{
    lock_guard<std::mutex> lock(m_clipboardMutex);
    delete m_systemClipboard;
    m_systemClipboard = NULL;
}

SystemClipboard &SystemClipboard::getInstance()
{
    return *m_systemClipboard;
}

void SystemClipboard::dataAddProcess(int index)
{
    if (pClipHandle) {
        if (pClipHandle->ClipDataAddListener) {
            pClipHandle->ClipDataAddListener(index, pClipHandle->addUserData);
        }
    }
    saveClipdata();
}

void SystemClipboard::dataDeleteProcess(string name)
{
    if (pClipHandle) {
        if (pClipHandle->ClipDataDeleteListener) {
            pClipHandle->ClipDataDeleteListener(name.c_str(), pClipHandle->deleteUserData);
        }
    }
}

void SystemClipboard::dataStateProcess(int state)
{
    if (pClipHandle) {
        if (pClipHandle->ClipDataStateListener) {
            pClipHandle->ClipDataStateListener(state, pClipHandle->stateUserData);
        }
    }
}

int SystemClipboard::setDataTop(ClipData *clip)
{
    lock_guard<std::mutex> lock(m_clipboardMutex);

    if (m_clipdata.size() >= 1) {
        deleteTopData();
        int i = 0;
        for (; i < m_clipdata.size(); i++) {
            if (m_clipdata[i] == clip) {
                break;
            }
        }
        if (i != m_clipdata.size()) {
            m_clipdata.erase(m_clipdata.begin() + i);
            m_clipdata.push_back(clip);
        }
        return m_clipdata.size() - 1;
    }
    return -1;
}

int SystemClipboard::setDataTop(int index)
{
    lock_guard<std::mutex> lock(m_clipboardMutex);
    if (m_clipdata.size() >= 1 && index < m_clipdata.size()) {
        deleteTopData();
        ClipData *clip = m_clipdata[index];
        m_clipdata.erase(m_clipdata.begin() + index);
        m_clipdata.push_back(clip);
        return m_clipdata.size() - 1;
    }
    return -1;
}

void SystemClipboard::setDataToClipboard(ClipData *vClip, bool flag)
{
    if (m_waylandclipboard) {
        m_waylandclipboard->setDataToClipboard(vClip, flag);
    }

    if (m_x11clipboard && flag) {
        m_x11clipboard->setDataToClipboard(vClip);
    }
}

void SystemClipboard::setClipState(int state)
{
    lock_guard<std::mutex> lock(m_clipboardMutex);
    m_Clipstate = state;
}

int SystemClipboard::getClipState()
{
    lock_guard<std::mutex> lock(m_clipboardMutex);
    return m_Clipstate;
}

SystemClipboard::SystemClipboard()
{
    m_Clipstate = -1;
    m_version = 1;
    m_x11clipboard = nullptr;
    m_waylandclipboard = nullptr;
    m_Isload = false;
    m_clipNum = 100000;
    m_CurrentTopData = nullptr;
}

void SystemClipboard::deleteTopData()
{
    if (m_CurrentTopData == nullptr) return;
    for (int i = 0; i < m_clipdata.size(); i++) {
        if (m_clipdata[i] == m_CurrentTopData) {
            delete m_clipdata[i];
            m_clipdata[i] = nullptr;
            m_clipdata.erase(m_clipdata.begin() + i);
            m_CurrentTopData = nullptr;
            break;
        }
    }
}

void SystemClipboard::init()
{
    lock_guard<std::mutex> lock(m_clipboardMutex);
    initDir();
    if (strcmp(getenv("XDG_SESSION_TYPE"), "wayland") == 0) {
        m_waylandclipboard = new WaylandClipboard(this, pClipHandle->pathDir);
    } else {
        m_x11clipboard = new X11Clipboard(this);
    }
    initConnect();
}

void SystemClipboard::initConnect()
{
    if (m_waylandclipboard) {
        m_waylandclipboard->initConnect();
    }

    if (m_x11clipboard) {
        m_x11clipboard->initConnect();
    }
}

void SystemClipboard::setClipNum(int num)
{
    lock_guard<std::mutex> lock(m_clipboardMutex);
    m_clipNum = num;
}

int SystemClipboard::getClipNum()
{
    lock_guard<std::mutex> lock(m_clipboardMutex);
    return m_clipNum;
}

void SystemClipboard::clearClipboardData()
{
    lock_guard<std::mutex> lock(m_clipboardMutex);
    if (m_waylandclipboard) {
        m_waylandclipboard->clearClipboard();
    }

    if (m_x11clipboard) {
        m_x11clipboard->clearClipboard();
    }
}

int SystemClipboard::cancelTopData()
{
    if (m_CurrentTopData != nullptr) {
        m_CurrentTopData->setDataState(true);
        m_CurrentTopData = nullptr;
        return 0;
    }
    return 1;
}
