// SPDX-FileCopyrightText: 2011 - 2022 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: GPL-3.0-or-later

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

#include "displayjack_clipboardstruct.h"

#include <set>
#include <fstream>
#include <iomanip>
#include <openssl/sha.h>
#include <openssl/md5.h>
#include <algorithm>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <sstream>
#include <experimental/filesystem>
#include <iostream>

long long ClipboardDataProcess::getCurrentTime()
{
    struct timespec ts;
    clock_gettime(CLOCK_REALTIME, &ts);                                          // 获取当前系统时间（毫秒）
    long long milliseconds = (long long)ts.tv_sec * 1000 + ts.tv_nsec / 1000000; // 将时间转换为毫秒
    return milliseconds;
}

vector<string> ClipboardDataProcess::getMapKeys(map<string, string> &formatMap)
{
    vector<string> listkeys;
    // 使用迭代器遍历map
    map<string, string>::iterator iter;
    for (iter = formatMap.begin(); iter != formatMap.end(); ++iter) {
        string key = iter->first;
        listkeys.push_back(key);
    }

    return listkeys;
}

bool ClipboardDataProcess::isContains(const vector<string> &liststr, const string str)
{
    auto it = std::find_if(liststr.begin(), liststr.end(), [&str](const std::string & str1)
    { return str1.find(str) != std::string::npos; });

    if (it != liststr.end()) {
        return true;
    }
    return false;
}

bool ClipboardDataProcess::isExists(const vector<string> &liststr, const string str)
{
    for (const auto &s : liststr) {
        if (s == str) {
            return true;
        }
    }
    return false;
}

bool ClipboardDataProcess::isFileContains(const vector<string> &liststr, const string str)
{
    return false;
}

// 定义一个函数，计算一个文件的MD5哈希值，返回一个16字节的数组
unsigned char *md5_file(const char *filename)
{
    // 打开文件，如果失败，返回空指针
    ifstream file(filename, ios::binary);
    if (!file) {
        cout << "无法打开文件: " << filename << endl;
        return nullptr;
    }
    // 创建一个MD5_CTX结构体，用于存储MD5的状态和结果
    MD5_CTX ctx;
    // 初始化MD5_CTX结构体
    MD5_Init(&ctx);
    // 定义一个缓冲区，用于读取文件的数据
    const int buffer_size = 1024;
    char buffer[buffer_size];
    // 循环读取文件的数据，每次读取buffer_size个字节
    while (file.read(buffer, buffer_size)) {
        // 更新MD5的状态，将读取的数据加入到MD5的计算中
        MD5_Update(&ctx, buffer, buffer_size);
    }
    // 如果文件读取到最后，还有剩余的数据，也加入到MD5的计算中
    if (file.gcount() > 0) {
        MD5_Update(&ctx, buffer, file.gcount());
    }
    // 关闭文件
    file.close();
    // 定义一个数组，用于存储MD5的结果，MD5的结果是16字节的
    unsigned char *result = new unsigned char[MD5_DIGEST_LENGTH];
    // 结束MD5的计算，将结果存储到数组中
    MD5_Final(result, &ctx);
    // 返回结果数组的指针
    return result;
}

// 定义一个函数，使用普通方法计算一个文件的SHA-256哈希值，返回一个32字节的数组
unsigned char *sha256_file_normal(const char *filename)
{
    // 打开文件，如果失败，返回空指针
    ifstream file(filename, ios::binary);
    if (!file) {
        cout << "无法打开文件: " << filename << endl;
        return nullptr;
    }
    // 创建一个SHA256_CTX结构体，用于存储SHA-256的状态和结果
    SHA256_CTX ctx;
    // 初始化SHA256_CTX结构体
    SHA256_Init(&ctx);
    // 定义一个缓冲区，用于读取文件的数据，大小为1024字节
    const int buffer_size = 1024;
    char buffer[buffer_size];
    // 循环读取文件的数据，每次读取buffer_size个字节
    while (file.read(buffer, buffer_size)) {
        // 更新SHA-256的状态，将读取的数据加入到SHA-256的计算中，使用普通方法
        SHA256_Update(&ctx, buffer, buffer_size);
    }
    // 如果文件读取到最后，还有剩余的数据，也加入到SHA-256的计算中，使用普通方法
    if (file.gcount() > 0) {
        SHA256_Update(&ctx, buffer, file.gcount());
    }
    // 关闭文件
    file.close();
    // 定义一个数组，用于存储SHA-256的结果，SHA-256的结果是32字节的
    unsigned char *result = new unsigned char[SHA256_DIGEST_LENGTH];
    // 结束SHA-256的计算，将结果存储到数组中
    SHA256_Final(result, &ctx);
    // 返回结果数组的指针
    return result;
}

// 定义一个函数，比较两个数组是否相等，参数为两个数组的指针和数组的长度
bool compare_array(unsigned char *a, unsigned char *b, int length)
{
    // 循环遍历两个数组的每个元素
    for (int i = 0; i < length; i++) {
        // 如果有任何一个元素不相等，返回false
        if (a[i] != b[i]) {
            return false;
        }
    }
    // 如果所有元素都相等，返回true
    return true;
}

string ClipboardDataProcess::sha256(const string &file)
{
    unsigned char hash[SHA256_DIGEST_LENGTH];
    SHA256_CTX sha256;
    SHA256_Init(&sha256);
    SHA256_Update(&sha256, file.c_str(), file.size());
    SHA256_Final(hash, &sha256);
    std::stringstream ss;
    for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) {
        ss << std::hex << std::setw(2) << std::setfill('0') << (int)hash[i];
    }
    return ss.str();
}

bool ClipboardDataProcess::compareFiles(const string &file1, const string &file2)
{
    // 计算两个文件的MD5哈希值
    unsigned char *md5_1 = md5_file(file1.c_str());
    unsigned char *md5_2 = md5_file(file2.c_str());
    // 如果任何一个文件的MD5哈希值为空，说明文件打开失败，退出函数
    if (md5_1 == nullptr || md5_2 == nullptr) {
        return false;
    }
    // 比较两个文件的MD5哈希值是否相等
    bool md5_equal = compare_array(md5_1, md5_2, MD5_DIGEST_LENGTH);

    // std::string hash1 = sha256(file1);
    // std::string hash2 = sha256(file2);

    delete[] md5_1;
    delete[] md5_2;

    return md5_equal;
}

vector<char> ClipboardDataProcess::readImage(const char *filename)
{
    return getData(filename);
}

vector<char> ClipboardDataProcess::readText(const char *filename)
{
    return getData(filename);
}

vector<char> ClipboardDataProcess::getData(const char *filename)
{
    vector<char> vData;
    // 读取二进制数据
    struct datafile_header header;
    // todo
    FILE *fp = read_file_header(filename, &header);

    uint32_t length = 0;
    if (fp == NULL) {
        log_error("Error opening file for reading \n");
        return vData;
    }

    vData.resize(header.size);
    read_file_data(fp, &vData[0], header.size); // 读取数据
    // 清理
    close_file(fp);
    return vData;
}

vector<char> ClipboardDataProcess::getLenData(const char *filename, const int len)
{
    vector<char> vData;
    // 读取二进制数据
    struct datafile_header header;
    // todo
    FILE *fp = read_file_header(filename, &header);

    uint32_t length = 0;
    if (fp == NULL) {
        log_error("Error opening file for reading \n");
        return vData;
    }
    if (header.size > len) {
        header.size = len;
    }
    vData.resize(header.size);
    read_file_data(fp, &vData[0], header.size); // 读取数据
    // 清理
    close_file(fp);
    return vData;
}

uint32_t ClipboardDataProcess::getDataLength(const char *filename)
{
    struct datafile_header header;
    FILE *fp = read_file_header(filename, &header);

    uint32_t length = 0;
    if (fp == NULL) {
        log_error("Error opening file for reading \n");
        return length;
    }
    close_file(fp);
    length = header.size;
    return length;
}

datafile_header ClipboardDataProcess::getDataHead(const char *filename)
{
    struct datafile_header header;
    header.height = 0;
    header.magic = 0;
    header.size = 0;
    header.width = 0;
    header.version = 0;
    FILE *fp = read_file_header(filename, &header);

    if (fp == NULL) {
        log_error("Error opening file for reading \n");
        return header;
    }
    close_file(fp);
    return header;
}

bool ClipboardDataProcess::scaleImage(const std::vector<char> &original, std::vector<char> &scaled, int scaledWidth, int scaledHeight)
{
    scaled.resize(scaledWidth * scaledHeight);

    int originalWidth = original.size() / original.front();
    int originalHeight = original.size() / original.front();

    for (int y = 0; y < scaledHeight; ++y) {
        for (int x = 0; x < scaledWidth; ++x) {
            int originalX = x * originalWidth / scaledWidth;
            int originalY = y * originalHeight / scaledHeight;
            scaled[y * scaledWidth + x] = original[originalY * originalWidth + originalX];
        }
    }
    return true;
}

// 定义一个函数，用于创建并写入文件头
FILE *ClipboardDataProcess::create_file_header(const char *filename, int version)
{
    struct datafile_header header;
    header.magic = 0x12345678;         // 假设魔数为0x12345678
    header.version = version;          // 写入版本号
    header.width = 0;                  //
    header.height = 0;                 //
    header.size = 0;                   // 文件大小暂时为0，稍后更新
    FILE *fp = fopen(filename, "wb+"); // 以二进制读写模式打开文件
    if (fp == NULL) {
        log_error("Failed to open the file\n");
        return NULL;
    }
    fwrite(&header, sizeof(datafile_header), 1, fp); // 写入文件头
    return fp;                              // 返回文件指针
}

// 定义一个函数，用于写入数据
void ClipboardDataProcess::write_file_data(FILE *fp, char *data, int length)
{
    fwrite(data, sizeof(char), length, fp); // 写入数据
}

// 定义一个函数，用于更新文件头中的文件大小
void ClipboardDataProcess::update_file_size(FILE *fp, int size)
{
    fseek(fp, sizeof(int) * 4, SEEK_SET); // 定位到文件头中的size字段
    fwrite(&size, sizeof(int), 1, fp);    // 写入文件大小
}

// 定义一个函数，用于更新文件头中的文件大小
void ClipboardDataProcess::update_file_img_size(FILE *fp, int w, int h)
{
    fseek(fp, sizeof(int) * 2, SEEK_SET); // 定位到文件头中的size字段
    fwrite(&w, sizeof(int), 1, fp);       // 写入文件大小
    fwrite(&h, sizeof(int), 1, fp);       // 写入文件大小
}

void ClipboardDataProcess::update_file_img_size(const char *filename, int w, int h)
{
    FILE *fp = fopen(filename, "rb+"); // 以二进制读写模式打开文件
    if (fp == NULL) {
        log_error("Failed to open the file\n");
        return;
    }
    fseek(fp, sizeof(int) * 2, SEEK_SET); // 定位到文件头中的size字段
    fwrite(&w, sizeof(int), 1, fp);       // 写入文件大小
    fwrite(&h, sizeof(int), 1, fp);       // 写入文件大小
    fclose(fp);                           // 关闭文件
}

// 定义一个函数，用于关闭文件
void ClipboardDataProcess::close_file(FILE *fp)
{
    fclose(fp); // 关闭文件
}

// 定义一个函数，用于读取文件头
FILE *ClipboardDataProcess::read_file_header(const char *filename, struct datafile_header *header)
{

    // 重新打开文件，假设文件名为test.bin
    FILE *fp = fopen(filename, "rb");
    if (fp == NULL) {
        log_error("Failed to open the file\n");
        return NULL;
    }
    // 读取文件头
    fread(header, sizeof(datafile_header), 1, fp); // 读取文件头
    // 验证文件头的魔数和版本号，假设版本号为1
    if (!verify_file_header(header, 1)) {
        log_error("File header validation failed\n");
        close_file(fp); // 关闭文件
        return NULL;
    }
    return fp;
}

// 定义一个函数，用于读取数据
void ClipboardDataProcess::read_file_data(FILE *fp, char *data, int length)
{
    fread(data, sizeof(char), length, fp); // 读取数据
}

// 定义一个函数，用于验证文件头的魔数和版本号
int ClipboardDataProcess::verify_file_header(struct datafile_header *header, int version)
{
    return header->magic == 0x12345678 && header->version == version; // 返回验证结果
}

int ClipboardDataProcess::convertClipRecordData(ClipDataRecord *vClipRecordData, ClipboardDataRecord *vOut)
{
    if (vClipRecordData->getDataType() == "Image" && vClipRecordData->getMimeType().find("image") == string::npos) {
        vOut->chunkSize = 0;
        vOut->data = NULL;
        return -1;
    }
    // 分配内存空间
    vOut->chunkSize = vClipRecordData->getLength();
    vOut->data = malloc(vOut->chunkSize);
    if (vOut->data == NULL) {
        log_error("Memory allocation failed.\n");
        return -1;
    }
    // 拷贝数据到内存空间
    char *ptr = (char *)vOut->data; // 用一个指针来遍历内存空间

    string vDataType = vClipRecordData->getDataType();

    // 写入数据类型的长度和内容
    int len = vDataType.size();
    memcpy(ptr, &len, sizeof(int)); // 拷贝数据类型
    ptr += sizeof(int);
    memcpy(ptr, vDataType.c_str(), len); // 拷贝数据类型
    ptr += len;

    string vMimetype = vClipRecordData->getMimeType();
    // 写入当前数据类型的长度和内容
    len = vMimetype.size();
    memcpy(ptr, &len, sizeof(int)); // 拷贝数据类型
    ptr += sizeof(int);
    memcpy(ptr, vMimetype.c_str(), len); // 拷贝数据类型
    ptr += len;

    datafile_header vHead = getDataHead(vClipRecordData->getDataPath().c_str());

    uint32_t bufferSize = vHead.size;
    memcpy(ptr, &bufferSize, sizeof(uint32_t)); // 数据大小
    ptr += sizeof(uint32_t);

    string vDataPath = vClipRecordData->getDataPath();
    // 写入当前数据类型的长度和内容
    len = vDataPath.size();
    memcpy(ptr, &len, sizeof(int)); // 拷贝数据类型
    ptr += sizeof(int);

    memcpy(ptr, vDataPath.c_str(), len); // 拷贝数据类型
    ptr += len;

    int vWidth = vHead.width;
    int vHeight = vHead.height;

    memcpy(ptr, &vWidth, sizeof(int)); // 拷贝数据类型
    ptr += sizeof(int);
    memcpy(ptr, &vHeight, sizeof(int)); // 拷贝数据类型
    ptr += sizeof(int);

    char *vSmallData = vClipRecordData->getData(len);
    memcpy(ptr, &len, sizeof(int)); // 拷贝数据类型
    ptr += sizeof(int);
    memcpy(ptr, vSmallData, len); // 拷贝数据类型
    ptr += len;

    vector<string> vUrls = vClipRecordData->getUrls();
    int count = vUrls.size();

    memcpy(ptr, &count, sizeof(int)); // 拷贝数据类型
    ptr += sizeof(int);

    // 遍历每个url地址，并将其长度和内容写入文件
    for (int i = 0; i < count; i++) {
        len = vUrls[i].size();
        memcpy(ptr, &len, sizeof(int)); // 拷贝数据类型
        ptr += sizeof(int);
        memcpy(ptr, vUrls[i].c_str(), len); // 拷贝数据类型
        ptr += len;
    }
    return 0;
}

int ClipboardDataProcess::convertClipPropertyData(ClipDataProperty *vClipPropertyData, ClipboardDataProperty *vOut, string name)
{
    vOut->chunkSize = vClipPropertyData->getLength() + name.size();
    vOut->data = malloc(vOut->chunkSize);
    if (vOut->data == NULL) {
        log_error("Memory allocation failed.\n");
        return -1;
    }
    // 拷贝数据到内存空间
    char *ptr = (char *)vOut->data; // 用一个指针来遍历内存空间

    // 写入名字
    int nlen = name.size();
    memcpy(ptr, &nlen, sizeof(int)); // 拷贝数据类型
    ptr += sizeof(int);
    memcpy(ptr, name.c_str(), nlen); // 拷贝数据类型
    ptr += nlen;

    int vPid = vClipPropertyData->getPID();
    memcpy(ptr, &vPid, sizeof(int)); // 拷贝数据类型
    ptr += sizeof(int);

    char vDatasource = vClipPropertyData->getOwnerPlatform();
    memcpy(ptr, &vDatasource, sizeof(char)); // 拷贝数据类型
    ptr += sizeof(char);

    long long vTimestamp = vClipPropertyData->getTimestamp();
    memcpy(ptr, &vTimestamp, sizeof(long long)); // 拷贝数据类型
    ptr += sizeof(long long);

    map<string, string> &vMimeTypes = vClipPropertyData->getMimeTypes();
    // 写入数据类型的数量
    int count = vMimeTypes.size();
    memcpy(ptr, &count, sizeof(int)); // 拷贝数据类型
    ptr += sizeof(int);

    // 遍历每个数据类型，并将其键值对写入文件
    for (auto it = vMimeTypes.begin(); it != vMimeTypes.end(); it++) {
        // 写入键的长度和内容
        int len = it->first.size();
        memcpy(ptr, &len, sizeof(int)); // 拷贝数据类型
        ptr += sizeof(int);
        memcpy(ptr, it->first.c_str(), len); // 拷贝数据类型
        ptr += len;

        // 写入值的长度和内容
        len = it->second.size();
        memcpy(ptr, &len, sizeof(int)); // 拷贝数据类型
        ptr += sizeof(int);
        memcpy(ptr, it->second.c_str(), len); // 拷贝数据类型
        ptr += len;
    }

    // 写入自定义tag标签属性的长度和内容
    string vTag = vClipPropertyData->getTag();
    int len = vTag.size();

    memcpy(ptr, &len, sizeof(int)); // 拷贝数据类型
    ptr += sizeof(int);
    memcpy(ptr, vTag.c_str(), len); // 拷贝数据类型

    return 0;
}

string ClipboardDataProcess::getClipboardDir(const string &str)
{
    string vDatadir;

    if (pClipHandle) {
        vDatadir = string(pClipHandle->pathDir) + str;
    } else {
        vDatadir = "/tmp" + str;
    }

    if (!isDirExist(vDatadir.c_str())) {
        create_multi_dir(vDatadir.c_str());
    }
    return vDatadir;
}

string ClipboardDataProcess::getClipboardDataFileName()
{
    string vDatapath = getClipboardDir(cachedataDir) + "/" + std::to_string(getCurrentTime()) + ".bin";
    return vDatapath;
}

bool ClipboardDataProcess::create_multi_dir(const char *dir)
{
    string path = dir; // 路径字符串
    int pos = 0;       // 分割位置
    while ((pos = path.find('/', pos)) != string::npos) {
        // 找到下一个分隔符
        string sub = path.substr(0, pos); // 截取子路径
        if (access(sub.c_str(), 0) == -1) {
            // 子路径不存在
            mkdir(sub.c_str(), S_IRWXU); // 创建子路径
        }
        pos++; // 移动到下一个位置
    }

    if (access(path.c_str(), 0) == -1) {
        // 最后一级目录不存在
        mkdir(path.c_str(), S_IRWXU); // 创建最后一级目录
        return true;
    }

    return false;
}
// 判断目录是否存在，返回1:存在 0:不存在
int ClipboardDataProcess::isDirExist(const char *dir)
{
    return !access(dir, F_OK);
}

// 删除指定文件夹及其下的所有文件
void ClipboardDataProcess::delete_directory(const char *dir)
{
    DIR *dp = opendir(dir); // 打开文件夹
    if (dp == nullptr) {
        // 打开失败
        return;
    }
    dirent *entry;       // 文件夹项结构体
    char file_path[256]; // 文件路径
    while ((entry = readdir(dp)) != nullptr) {
        // 读取文件夹项
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
            // 跳过当前目录和父目录
            continue;
        }
        // 构造文件路径
        sprintf(file_path, "%s/%s", dir, entry->d_name);
        // 删除文件
        remove(file_path);
    }
    // 关闭文件夹流
    closedir(dp);
    // 删除空的文件夹
    rmdir(dir);
}

int ClipboardDataProcess::delete_file(const char *dir)
{
    return remove(dir);
}

vector<string> ClipboardDataProcess::splitString(const vector<char> &vData, const string &vStr)
{
    vector<string> result;
    string str(vData.begin(), vData.end()); // 将vector<char>转换为string
    size_t pos = 0;
    string token;
    while ((pos = str.find(vStr)) != string::npos) {
        token = str.substr(0, pos);
        result.push_back(token);
        str.erase(0, pos + vStr.length());
    }
    result.push_back(str); // 将最后一个子字符串添加到结果中
    return result;
}

// 一个函数，用于删除指定目录下非列表的文件
// indir: 指定目录
// liststr: 列表文件
void ClipboardDataProcess::delete_non_list_files(const std::string &indir, const std::vector<std::string> &liststr)
{
    DIR *dir;
    struct dirent *ptr;
    if ((dir = opendir(indir.c_str())) == NULL) {
        log_error("Open dir error...\n");
        return;
    }

    while ((ptr = readdir(dir)) != NULL) {
        if (strcmp(ptr->d_name, ".") == 0 || strcmp(ptr->d_name, "..") == 0) /// current dir OR parrent dir
            continue;
        else if (ptr->d_type == 8) { /// file
            std::string strFile;
            strFile = indir;
            strFile += "/";
            strFile += ptr->d_name;
            // files.push_back(strFile);
            //  检查文件名是否在列表中
            if (std::find(liststr.begin(), liststr.end(), strFile) == liststr.end()) {
                remove(strFile.c_str());
            }
        } else {
            continue;
        }
    }
    closedir(dir);
}

ClipData *ClipboardDataProcess::compareData(ClipData *vClipPtr)
{
    ClipData *clipD = NULL;

    vector<string> curFormats = vClipPtr->getMimeTypes();
    std::sort(curFormats.begin(), curFormats.end());

    int vDataCount = SystemClipboard::getInstance().getClipDataCount();
    bool flag = false;
    for (int i = 0; i < vDataCount; i++) {
        ClipData *vClip = SystemClipboard::getInstance().getClipDataAt(i);
        vector<string> t_lastFormats = vClip->getMimeTypes();
        std::sort(t_lastFormats.begin(), t_lastFormats.end());
        if (curFormats.size() != t_lastFormats.size()) {
            continue;
        }
        if (!std::equal(curFormats.begin(), curFormats.end(), t_lastFormats.begin())) {
            continue;
        }

        int vRecorderCount = vClip->getRecordCount();
        int vSameNum = 0;
        int vPtrCount = vClipPtr->getRecordCount();
        for (int j = 0; j < vRecorderCount; j++) {
            ClipDataRecord *vDataR = vClip->getRecordAt(j);
            if (!vDataR)
                continue;

            if ("TIMESTAMP" == vDataR->getMimeType() || "PIXMAP" == vDataR->getMimeType()
                    || "Wine Marshalled DataObject" == vDataR->getMimeType()) {
                vSameNum++;
                continue;
            }
            for (int k = 0; k < vPtrCount; k++) {
                ClipDataRecord *vDataPtr = vClipPtr->getRecordAt(k);
                if (!vDataPtr)
                    continue;
                if (vDataPtr->getMimeType() == vDataR->getMimeType()) {
                    if (ClipboardDataProcess::compareFiles(vDataR->getDataPath(), vDataPtr->getDataPath())) {
                        vSameNum++;
                    }
                    break;
                }
            }
        }

        if (vSameNum == vRecorderCount) {
            clipD = vClip;
            break;
        }
    }
    return clipD;
}
