/*
 * Copyright 2024 KylinSoft Co., Ltd.
 *
 * 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 (at your option) 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 <https://www.gnu.org/licenses/>.
 */

#include "systemapi.h"

#include <gio/gdesktopappinfo.h>

#include <array>
#include <cstdio>

#include "utils/logger.h"

CommandSystemApi::CommandSystemApi(const std::string &command) : command_(command) {}

bool CommandSystemApi::execute() {
    g_unsetenv("LD_LIBRARY_PATH");
    system(command_.c_str());
    g_setenv("LD_LIBRARY_PATH", "usr/lib/kylin-ai/depends", true);
    return true;
}

NumericCommandSystemApi::NumericCommandSystemApi(const std::string &cmdName, const std::string &valueName,
                                                 const std::map<std::string, std::string> &commandDetail)
    : cmdName_(cmdName), valueName_(valueName), commandDetail_(commandDetail) {
    command_ = commandDetail_["set"].append(value());
    LOGE("Numeric command: {}", command_);
}

std::string NumericCommandSystemApi::value() {
    if (isNumber(valueName_)) {
        return valueName_;
    }
    std::string type = std::get<0>(commandRangeMap_.at(cmdName_));
    double min = std::get<1>(commandRangeMap_.at(cmdName_));
    double max = std::get<2>(commandRangeMap_.at(cmdName_));
    double value;
    if (isPercentage(valueName_)) {
        value = std::stod(valueName_.substr(0, valueName_.length() - 1)) / 100;
        if (type == "percent") {
            return std::to_string(value);
        }
        value = min + value * (max - min);
    }
    if (numericWordMap_.count(valueName_)) {
        value = min + numericWordMap_.at(valueName_) * (max - min);
    }
    if (deltaWordMap_.count(valueName_)) {
        auto current = std::stod(currentValue());
        if ((cmdName_ == "设置鼠标光标闪烁频率" || cmdName_ == "设置鼠标双击间隔时长") &&
            (valueName_ == "加快" || valueName_ == "减慢")) {
            value = current - deltaWordMap_.at(valueName_) * (max - min);
        } else {
            value = current + deltaWordMap_.at(valueName_) * (max - min);
        }
        value = std::max(min, std::min(max, value));
    }
    if (type == "int") {
        if (cmdName_ == "设置鼠标光标闪烁频率") {
            return std::to_string(int(max - value));
        }
        return std::to_string(int(value));
    }

    return std::to_string(value);
    LOGE("Unknown numeric command value: {}", valueName_);
    return "";
}

std::string NumericCommandSystemApi::currentValue() {
    auto cmd = commandDetail_["get"];
    std::array<char, 256> buffer;
    std::string result;
    std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd.c_str(), "r"), pclose);
    if (!pipe) {
        LOGE("Failed to run command: {}", cmd);
        return "";
    }
    while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
        result += buffer.data();
    }
    return result;
}

const std::map<std::string, double> NumericCommandSystemApi::numericWordMap_ = {
    {"最大", 1}, {"最小", 0}, {"中间", 0.5}};

const std::map<std::string, double> NumericCommandSystemApi::deltaWordMap_ = {
    {"调高", 0.2}, {"调低", -0.2}, {"加快", 0.2}, {"减慢", -0.2}, {"增大", 0.1}, {"减小", -0.1}};

const std::map<std::string, std::tuple<std::string, double, double>> NumericCommandSystemApi::commandRangeMap_ = {
    {"设置显示器亮度", {"double", 0.0, 100.0}},    {"音量控制", {"int", 0, 100}},
    {"设置显示器屏幕缩放", {"percent", 1.0, 1.5}}, {"设置系统字体", {"double", 10.0, 15.0}},
    {"设置鼠标指针速度", {"double", 1.0, 10.0}},   {"设置鼠标滚轮速度", {"int", 0, 10}},
    {"设置鼠标光标闪烁频率", {"int", 100, 2500}},  {"设置鼠标双击间隔时长", {"int", 170, 1000}},
    {"设置触控板指针速度", {"int", 1, 8}}};

AppLaunchSystemApi::AppLaunchSystemApi(const std::string &desktopFile) : desktopFile_(desktopFile) {}

bool AppLaunchSystemApi::execute() {
    GDesktopAppInfo *appInfo = g_desktop_app_info_new_from_filename(desktopFile_.c_str());
    if (!appInfo) {
        LOGE("Failed to create GDesktopAppInfo from {}", desktopFile_);
        return false;
    }
    g_app_info_launch_uris_async(G_APP_INFO(appInfo), nullptr, nullptr, nullptr, nullptr, nullptr);
    return true;
}