#include "FileHelper.h"

#include <QDir>
#include <QApplication>
#include <QDebug>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QFile>
#include <QFileInfo>

const QString kConfigName = "/ProjectDisplay/config.json";
constexpr int16_t kDefualtPort = 9527;

template<typename T>
T clamp(const T& value, const T& minValue, const T& maxValue)
{
    return qMax(minValue, qMin(value, maxValue));
}

bool FileHelper::GetItemFromJsonConfig(QString& addr, QMap<QString, QString>* items) {
    if (nullptr == items) {
        qDebug() << "items is nullptr";
        return false;
    }
    const QString configPath = QApplication::applicationDirPath() + kConfigName;
    QFile inputFile(configPath);
    if (!inputFile.open(QIODevice::ReadOnly))
    {
        qDebug() << "Failed to open file for reading. config=" << configPath;
        return false;
    }

    // 读取文件内容并关闭文件
    QByteArray fileData = inputFile.readAll();
    inputFile.close();

    // 解析 JSON 数据
    QJsonParseError jsonError;
    QJsonDocument jsonDocument = QJsonDocument::fromJson(fileData, &jsonError);
    if (jsonError.error != QJsonParseError::NoError)
    {
        qDebug() << "Failed to parse JSON data:" << jsonError.errorString() << "  config=" << configPath;
        return false;
    }

    // 将 JSON 数据转换为 JSON 对象
    QJsonObject jsonObject = jsonDocument.object();
    if (jsonObject.contains("server") && jsonObject["server"].isString()) {
        addr = jsonObject["server"].toString();
    }
    else {
        addr = "127.0.0.1:9527";
    }

    if (!jsonObject.contains("data") || !jsonObject["data"].isArray()) {
        qDebug() << "Failed to parse JSON not find key of data, or data value is not array config=" << configPath;
        return false;
    }

    QJsonArray jsonArray = jsonObject["data"].toArray();
    for (qint32 i = 0; i < jsonArray.size(); ++i) {
        if (!jsonArray.at(i).isObject()) {
            qDebug() << "jsonArray is not object config=" << configPath;
            continue;
        }

        QJsonObject item = jsonArray.at(i).toObject();
        if (!item.contains("name") || !item["name"].isString() ||
            !item.contains("path") || !item["path"].isString()) {
            qDebug() << "not find index / path in item config config=" << configPath;
            continue;
        }

        items->insert(item["name"].toString(), item["path"].toString());
    }

    return true;
}

bool FileHelper::GetItemFromJsonConfig(Config* config) {
    if (nullptr == config) {
        qDebug() << "config is nullptr";
        return false;
    }
    const QString configPath = QApplication::applicationDirPath() + kConfigName;
    QFile inputFile(configPath);
    if (!inputFile.open(QIODevice::ReadOnly)) {
        qDebug() << "Failed to open file for reading. config=" << configPath;
        return false;
    }

    // 读取文件内容并关闭文件
    QByteArray fileData = inputFile.readAll();
    inputFile.close();

    // 解析 JSON 数据
    QJsonParseError jsonError;
    QJsonDocument jsonDocument = QJsonDocument::fromJson(fileData, &jsonError);
    if (jsonError.error != QJsonParseError::NoError) {
        qDebug() << "Failed to parse JSON data:" << jsonError.errorString() << "  config=" << configPath;
        return false;
    }

    // 将 JSON 数据转换为 JSON 对象
    QJsonObject jsonObject = jsonDocument.object();
    if (jsonObject.contains("port") && jsonObject["port"].isDouble()) {
        config->port = jsonObject["port"].toInt();
    } else {
        config->port = kDefualtPort;
    }

    if (!jsonObject.contains("data") || !jsonObject["data"].isArray()) {
        qDebug() << "Failed to parse JSON not find key of data, or data value is not array config=" << configPath;
        return false;
    }

    const QJsonArray datas = jsonObject["data"].toArray();
    for (int i = 0; i < datas.size(); ++i) {
        const QJsonObject data = datas.at(i).toObject();
        MedianItem mediaItem;
        if (!data.contains("name") || !data["name"].isString()) {
            qDebug() << "Failed to parse JSON not find key of name, or name value is not array config=" << configPath;
            return false;
        }

        mediaItem.name = data["name"].toString();

        if (!data.contains("index") || !data["index"].isDouble()) {
            qDebug() << "Failed to parse JSON not find key of index, or index value is not array config=" << configPath;
            return false;
        }
        mediaItem.index = data["index"].toInt();

        if (!data.contains("type") || !data["type"].isDouble()) {
            qDebug() << "Failed to parse JSON not find key of type, or type value is not array config=" << configPath;
            return false;
        }
        mediaItem.type = data["type"].toInt();

        if (!data.contains("data") || !data["data"].isArray()) {
            qDebug() << "Failed to parse JSON not find key of data, or data value is not array config=" << configPath;
            return false;
        }
        const QJsonArray itemDatas = data["data"].toArray();
        QSet<int32_t> test;
        for (int i = 0; i < itemDatas.size(); ++i) {
            const QJsonObject mediaInfoData = itemDatas.at(i).toObject();
            MediaInfo mediaInfo;
            if (!mediaInfoData.contains("id") || !mediaInfoData["id"].isDouble()) {
                qDebug() << "Failed to parse JSON not find key of id, or name id is not array config=" << configPath;
                return false;
            }
            mediaInfo.id = mediaInfoData["id"].toInt();

            if (!mediaInfoData.contains("button") || !mediaInfoData["button"].isString()) {
                qDebug() << "Failed to parse JSON not find key of button, or button value is not array config=" << configPath;
                return false;
            }
            mediaInfo.button = mediaInfoData["button"].toString();

            if (!mediaInfoData.contains("name") || !mediaInfoData["name"].isString()) {
                // qDebug() << "Failed to parse JSON not find key of name, or name value is not array config=" << configPath;
                mediaInfo.name = "";
                //return false;
            } else {
                mediaInfo.name = mediaInfoData["name"].toString();
            }


            if (!mediaInfoData.contains("describe") || !mediaInfoData["describe"].isString()) {
                qDebug() << "Failed to parse JSON not find key of type, or type value is not array config=" << configPath;
                return false;
            }
            mediaInfo.describe = mediaInfoData["describe"].toString();

            if (!mediaInfoData.contains("images") || !mediaInfoData["images"].isArray()) {
                qDebug() << "Failed to parse JSON not find key of images, or images value is not array config=" << configPath;
                return false;
            }
            const QJsonArray images = mediaInfoData["images"].toArray();
            for (int i = 0; i < images.size(); ++i) {
                mediaInfo.images.append(images[i].toString());
            }

            if (!mediaInfoData.contains("start") || !mediaInfoData["start"].isDouble()) {
                mediaInfo.start = 0;
            } else {
                int start = mediaInfoData["start"].toInt();
                mediaInfo.start = clamp(start, 0, 2);
            }

            if (!mediaInfoData.contains("end") || !mediaInfoData["end"].isString()) {
                mediaInfo.end = QPointF(0.0f, 0.0f);
            } else {
                QString end = mediaInfoData["end"].toString();
                QStringList pointCoordinates = end.split(",");

                if (pointCoordinates.size() == 2) {
                    bool xConversionOk, yConversionOk;
                    qreal x = pointCoordinates[0].trimmed().toDouble(&xConversionOk);
                    qreal y = pointCoordinates[1].trimmed().toDouble(&yConversionOk);

                    if (xConversionOk && yConversionOk) {
                        mediaInfo.end = QPointF(x, y);
                    }
                }
            }

            if (!mediaInfoData.contains("scope") || !mediaInfoData["scope"].isArray()) {
                qDebug() << "Failed to parse JSON not find key of scope, or scope value is not array config=" << configPath;
                return false;
            } else {
                const QJsonArray scope = mediaInfoData["scope"].toArray();
                if (scope.size() == 5) {
                    mediaInfo.scope.x = scope[0].toInt();
                    mediaInfo.scope.y = scope[1].toDouble();
                    mediaInfo.scope.width = scope[2].toInt();
                    mediaInfo.scope.height = scope[3].toInt();
                    mediaInfo.scope.shape = scope[4].toInt();
                }
                else if (scope.size() == 6) {
                    mediaInfo.scope.x = scope[0].toInt();
                    mediaInfo.scope.y = scope[1].toDouble();
                    mediaInfo.scope.width = scope[2].toInt();
                    mediaInfo.scope.height = scope[3].toInt();
                    mediaInfo.scope.shape = scope[4].toInt();
                    mediaInfo.scope.rotate = scope[5].toInt();
                }
                else {
                    qDebug() << "Failed to parse JSON not find key of scope, or scope value is not 5 config=" << configPath;
                    return false;
                }
            }

            if (test.contains(mediaInfo.id)) {
                qDebug() << "test.contains is exits" << mediaInfo.id;
                return false;
            }
            test.insert(mediaInfo.id);

            if (mediaInfo.id != i + 1) {
                qDebug() << "test.contains is exits" << mediaInfo.id;
                return false;
            }
            if (mediaItem.datas.contains(mediaInfo.id)) {
                qDebug() << "dmediaItem.datas is exits" << mediaInfo.button;
                return false;
            }
            mediaItem.datas.insert(mediaInfo.id, std::move(mediaInfo));
        }

        if (config->datas.contains(mediaItem.index)) {
            qDebug() << "datas.contains is exits" << mediaItem.index;
            return false;
        }

        config->datas.insert(mediaItem.index, std::move(mediaItem));
    }
    return true;
}

bool FileHelper::SaveItemToJsonConfig(Config* config, const QString& path) {
    if (nullptr == config) {
        qDebug() << __FUNCTION__ << "config is nullptr";
        return false;
    }

    QJsonObject root;
    root.insert("port", config->port);

    QJsonArray jsonData;

    const QMap<int32_t, MedianItem>& datas = config->datas;
    for (const auto& data : datas) {
        QJsonObject item;
        item.insert("name", data.name);
        item.insert("index", data.index);
        item.insert("type", data.type);

        QJsonArray itemDatas;
        const QMap<int32_t, MediaInfo>& dataDatas = data.datas;
        for (const auto& dataData : dataDatas) {
            QJsonObject dataItem;
            dataItem.insert("id", dataData.id);
            dataItem.insert("button", dataData.button);
            dataItem.insert("name", dataData.name);
            dataItem.insert("describe", dataData.describe);

            QJsonArray images;
            for (const auto& image : dataData.images) {
                images.append(image);
            }
            dataItem.insert("images", images);
            dataItem.insert("start", dataData.start);

            QString end = QString("%1,%2").arg(dataData.end.x()).arg(dataData.end.y());
            dataItem.insert("end", end);

            QJsonArray scope;
            scope.append(dataData.scope.x);
            scope.append(dataData.scope.y);
            scope.append(dataData.scope.width);
            scope.append(dataData.scope.height);
            scope.append(dataData.scope.shape);
            scope.append(dataData.scope.rotate);
            dataItem.insert("scope", scope);
            itemDatas.append(dataItem);
        }
        item.insert("data", itemDatas);
        jsonData.append(item);
    }

    root.insert("data", jsonData);

    QJsonDocument document;
    document.setObject(root);

    QFile jsonFile(path);
    if (!jsonFile.open(QFile::WriteOnly)) {
        qDebug() << __FUNCTION__ << " write json file failed";
        return false;
    }
    jsonFile.write(document.toJson());
    jsonFile.close();
    return true;
}

QString FileHelper::GetResoucePath(const QString& name) {
    const QString resourcePath = QString("%1/%2").arg(QApplication::applicationDirPath()).arg(name);
    return resourcePath;
}

QStringList FileHelper::GetAllMediaFiles(const QString& path, bool* success) {
    QDir dir(path);
    if (!dir.exists()) {
        if (success) { *success = false; }
        return QStringList();
    }

    QStringList nameFilters;
    nameFilters << "*.png" << "*.jpg" << "*.jpeg" << "*.gif" << "*.bmp"
        << "*.mp4" << "*.avi" << "*.mkv" << "*.mov" << "*.wmv";

    QStringList files = dir.entryList(nameFilters, QDir::Files);
    if (success) { *success = true; }
    return files;
}

bool FileHelper::GeneratJsonConfig(const QString& path) {
    QString resourcePath = GetResoucePath(path);
    bool success = false;
    QStringList mediaFiles = GetAllMediaFiles(resourcePath, &success);
    if (!success) {
        qDebug() << "GetAllMediaFiles failes";
        return false;
    }

    QJsonArray jsonArray;
    qint32 index = 0;
    for (const auto& path : mediaFiles) {
        QJsonObject jsonObject;
        jsonObject["index"] = index++;
        jsonObject["path"] = path;
        jsonArray.append(jsonObject);

    }

    QJsonObject jsonObject;
    jsonObject["data"] = jsonArray;

    // 创建 JSON 文档

    const QString configPath = resourcePath + kConfigName;
    QFile outputFile(configPath);
    if (outputFile.open(QIODevice::WriteOnly)) {
        QJsonDocument jsonDocument(jsonObject);
        outputFile.write(jsonDocument.toJson());
        outputFile.close();
        qDebug() << "JSON file saved at:" << configPath;
        return true;
    }
    else {
        qDebug() << "Failed to open file for writing. path=" << configPath;
    }
    return false;
}

bool FileHelper::GetProjectFromJsonConfig(const QString& path, QMap<qint32, QString>* items) {
    if (nullptr == items) {
        qDebug() << "items is nullptr";
        return false;
    }
    const QString configPath = GetResoucePath(path) + kConfigName;
    QFile inputFile(configPath);
    if (!inputFile.open(QIODevice::ReadOnly))
    {
        qDebug() << "Failed to open file for reading. config=" << configPath;
        return false;
    }

    // 读取文件内容并关闭文件
    QByteArray fileData = inputFile.readAll();
    inputFile.close();

    // 解析 JSON 数据
    QJsonParseError jsonError;
    QJsonDocument jsonDocument = QJsonDocument::fromJson(fileData, &jsonError);
    if (jsonError.error != QJsonParseError::NoError)
    {
        qDebug() << "Failed to parse JSON data:" << jsonError.errorString() << "  config=" << configPath;
        return false;
    }

    // 将 JSON 数据转换为 JSON 对象
    QJsonObject jsonObject = jsonDocument.object();
    if (!jsonObject.contains("data") || !jsonObject["data"].isArray()) {
        qDebug() << "Failed to parse JSON not find key of data, or data value is not array config=" << configPath;
        return false;
    }

    QJsonArray jsonArray = jsonObject["data"].toArray();
    for (qint32 i = 0; i < jsonArray.size(); ++i) {
        if (!jsonArray.at(i).isObject()) {
            qDebug() << "jsonArray is not object config=" << configPath;
            continue;
        }

        QJsonObject item = jsonArray.at(i).toObject();
        if (!item.contains("index") || !item["index"].isDouble() ||
            !item.contains("path") || !item["path"].isString()) {
            qDebug() << "not find index / path in item config config=" << configPath;
            continue;
        }

        items->insert(item["index"].toInt(), item["path"].toString());
    }

    return true;
}

FileHelper::MediaType FileHelper::GetMediaType(const QString& path, const QString& name, QString& resource) {
    const QString mediaPath = GetResoucePath(path) + QString("/") + name;
    resource = mediaPath;
    QFileInfo fileInfo(mediaPath);
    QString suffix = fileInfo.suffix();

    if (suffix.isEmpty()) {
        qDebug() << "The file type is unknown.";
        return MediaType::Unknown;
    }
    else if (suffix == "jpg" || suffix == "png" ) {
        qDebug() << "The file is a image.";
        return MediaType::Image;
    }
    else if (suffix == "gif") {
        qDebug() << "The file is a gif.";
        return MediaType::Gif;
    }
    else if (suffix == "mp4" || suffix == "avi" || suffix == "mkv") {
        qDebug() << "The file is a video.";
        return MediaType::Video;
    }
    else {
        qDebug() << "The file type is not supported.";
    }
    return MediaType::Unknown;
}