#include "fastfetch.h"
#include "common/jsonconfig.h"
#include "common/processing.h"
#include "common/stringUtils.h"
#include "options/general.h"

#include <unistd.h>

const char* ffOptionsParseGeneralJsonConfig(FFOptionsGeneral* options, yyjson_val* root) {
    yyjson_val* object = yyjson_obj_get(root, "general");
    if (!object) {
        return NULL;
    }
    if (!yyjson_is_obj(object)) {
        return "Property 'general' must be an object";
    }

    yyjson_val *key, *val;
    size_t idx, max;
    yyjson_obj_foreach (object, idx, max, key, val) {
        if (unsafe_yyjson_equals_str(key, "thread")) {
            options->multithreading = yyjson_get_bool(val);
        } else if (unsafe_yyjson_equals_str(key, "processingTimeout")) {
            options->processingTimeout = (int32_t) yyjson_get_int(val);
        } else if (unsafe_yyjson_equals_str(key, "preRun")) {
            if (!yyjson_is_str(val)) {
                return "general.preRun must be a string";
            }
            if (system(unsafe_yyjson_get_str(val)) < 0) {
                return "Failed to execute preRun command";
            }
        } else if (unsafe_yyjson_equals_str(key, "detectVersion")) {
            options->detectVersion = yyjson_get_bool(val);
        } else if (unsafe_yyjson_equals_str(key, "playerName")) {
            ffStrbufSetJsonVal(&options->playerName, val);
        }
#if defined(__linux__) || defined(__FreeBSD__) || defined(__sun) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__HAIKU__) || defined(__GNU__)
        else if (unsafe_yyjson_equals_str(key, "dsForceDrm")) {
            if (yyjson_is_str(val)) {
                int value;
                const char* error = ffJsonConfigParseEnum(val, &value, (FFKeyValuePair[]) {
                                                                           { "sysfs-only", FF_DS_FORCE_DRM_TYPE_SYSFS_ONLY },
                                                                           { "false", FF_DS_FORCE_DRM_TYPE_FALSE },
                                                                           { "true", FF_DS_FORCE_DRM_TYPE_TRUE },
                                                                           {},
                                                                       });
                if (error) {
                    return "Invalid enum value of `dsForceDrm`";
                } else {
                    options->dsForceDrm = (FFDsForceDrmType) value;
                }
            } else {
                options->dsForceDrm = yyjson_get_bool(val) ? FF_DS_FORCE_DRM_TYPE_TRUE : FF_DS_FORCE_DRM_TYPE_FALSE;
            }
        }
#endif
        else {
            return "Unknown general property";
        }
    }

    return NULL;
}

bool ffOptionsParseGeneralCommandLine(FFOptionsGeneral* options, const char* key, const char* value) {
    if (ffStrEqualsIgnCase(key, "--thread") || ffStrEqualsIgnCase(key, "--multithreading")) {
        options->multithreading = ffOptionParseBoolean(value);
    } else if (ffStrEqualsIgnCase(key, "--processing-timeout")) {
        options->processingTimeout = ffOptionParseInt32(key, value);
    } else if (ffStrEqualsIgnCase(key, "--detect-version")) {
        options->detectVersion = ffOptionParseBoolean(value);
    } else if (ffStrEqualsIgnCase(key, "--player-name")) {
        ffOptionParseString(key, value, &options->playerName);
    }
#if defined(__linux__) || defined(__FreeBSD__) || defined(__sun) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__HAIKU__) || defined(__GNU__)
    else if (ffStrEqualsIgnCase(key, "--ds-force-drm")) {
        if (ffOptionParseBoolean(value)) {
            options->dsForceDrm = FF_DS_FORCE_DRM_TYPE_TRUE;
        } else if (ffStrEqualsIgnCase(value, "sysfs-only")) {
            options->dsForceDrm = FF_DS_FORCE_DRM_TYPE_SYSFS_ONLY;
        } else {
            options->dsForceDrm = FF_DS_FORCE_DRM_TYPE_FALSE;
        }
    }
#endif
    else {
        return false;
    }

    return true;
}

void ffOptionsInitGeneral(FFOptionsGeneral* options) {
    options->processingTimeout = 5000;
    options->multithreading = true;
    options->detectVersion = true;
    ffStrbufInit(&options->playerName);

#if defined(__linux__) || defined(__FreeBSD__) || defined(__sun) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__HAIKU__) || defined(__GNU__)
    options->dsForceDrm = FF_DS_FORCE_DRM_TYPE_FALSE;
#endif
}

void ffOptionsDestroyGeneral(FFOptionsGeneral* options) {
    ffStrbufDestroy(&options->playerName);
}

void ffOptionsGenerateGeneralJsonConfig(FFdata* data, FFOptionsGeneral* options) {
    yyjson_mut_doc* doc = data->resultDoc;
    yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, doc->root, "general");

    yyjson_mut_obj_add_bool(doc, obj, "thread", options->multithreading);

    yyjson_mut_obj_add_int(doc, obj, "processingTimeout", options->processingTimeout);

    yyjson_mut_obj_add_bool(doc, obj, "detectVersion", options->detectVersion);

    yyjson_mut_obj_add_strbuf(doc, obj, "playerName", &options->playerName);

#if defined(__linux__) || defined(__FreeBSD__) || defined(__sun) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__HAIKU__) || defined(__GNU__)

    switch (options->dsForceDrm) {
        case FF_DS_FORCE_DRM_TYPE_FALSE:
            yyjson_mut_obj_add_bool(doc, obj, "dsForceDrm", false);
            break;
        case FF_DS_FORCE_DRM_TYPE_SYSFS_ONLY:
            yyjson_mut_obj_add_str(doc, obj, "dsForceDrm", "sysfs-only");
            break;
        case FF_DS_FORCE_DRM_TYPE_TRUE:
            yyjson_mut_obj_add_bool(doc, obj, "dsForceDrm", true);
            break;
    }
#endif
}
