#include "settings.h"
#include "json.h"
#include "string.h"
#include "arena.h"
#include "scratch.h"
#include "math.h"

struct buffer settings_serialize(struct arena *arena, const struct sys_window_settings *settings)
{
    __prof;

    struct string minimized = settings->flags & SYS_WINDOW_SETTINGS_FLAG_MINIMIZED  ? STR("true") : STR("false");
    struct string maximized = settings->flags & SYS_WINDOW_SETTINGS_FLAG_MAXIMIZED ? STR("true") : STR("false");
    struct string fullscreen = settings->flags & SYS_WINDOW_SETTINGS_FLAG_FULLSCREEN  ? STR("true") : STR("false");
    i32 x = settings->floating_x;
    i32 y = settings->floating_y;
    i32 width = settings->floating_width;
    i32 height = settings->floating_height;

    struct string fmt = STR(
        "{\n"
        "  \"window\": {\n"
        "    \"minimized\": %F,\n"
        "    \"maximized\": %F,\n"
        "    \"fullscreen\": %F,\n"
        "    \"x\": %F,\n"
        "    \"y\": %F,\n"
        "    \"width\": %F,\n"
        "    \"height\": %F\n"
        "  }\n"
        "}\n"
    );

    struct string formatted = string_format(arena,
                                            fmt,
                                            FMT_STR(minimized),
                                            FMT_STR(maximized),
                                            FMT_STR(fullscreen),
                                            FMT_SINT(x),
                                            FMT_SINT(y),
                                            FMT_SINT(width),
                                            FMT_SINT(height));

    return BUFFER_FROM_STRING(formatted);
}

struct sys_window_settings *settings_deserialize(struct arena *arena, struct buffer src, struct string *error_out)
{
    __prof;
    struct temp_arena scratch = scratch_begin(arena);

    struct string error = { 0 };
    struct json_error json_error = { 0 };

    struct sys_window_settings *settings = arena_push_zero(arena, struct sys_window_settings);
    struct json_parse_result parse_res = json_from_string(scratch.arena, STRING_FROM_BUFFER(src));

    if (parse_res.errors.count > 0) {
        json_error = *parse_res.errors.first;
        goto abort;
    }

    struct json *root = parse_res.root;
    if (!root) {
        error = STR("Root object not found.");
        goto abort;
    }

    struct json *window = root->child_first;
    if (!window || window->type != JSON_TYPE_OBJECT || !string_eq(window->key, STR("window"))) {
        error = STR("\"window\" object not found");
        goto abort;
    }

    b32 found_maximized = false;
    b32 found_fullscreen = false;
    b32 found_x = false;
    b32 found_y = false;
    b32 found_width = false;
    b32 found_height = false;
    for (struct json *child = window->child_first; child; child = child->next) {
        struct string key = child->key;

        if (string_eq(key, STR("maximized"))) {
            if (child->type != JSON_TYPE_BOOL) {
                error = STR("Expected boolean for \"maximized\"");
                goto abort;
            }
            if (child->value.boolean) {
                settings->flags |= SYS_WINDOW_SETTINGS_FLAG_MAXIMIZED;
            }
            found_maximized = true;
        } else if (string_eq(key, STR("fullscreen"))) {
            if (child->type != JSON_TYPE_BOOL) {
                error = STR("Expected boolean for \"fulscreen\"");
                goto abort;
            }
            if (child->value.boolean) {
                settings->flags |= SYS_WINDOW_SETTINGS_FLAG_FULLSCREEN;
            }
            found_fullscreen = true;
        } else if (string_eq(key, STR("x"))) {
            if (child->type != JSON_TYPE_NUMBER) {
                error = STR("Expected number for \"x\"");
                goto abort;
            }
            settings->floating_x = math_round_to_int(child->value.number);
            found_x = true;
        } else if (string_eq(key, STR("y"))) {
            if (child->type != JSON_TYPE_NUMBER) {
                error = STR("Expected number for \"y\"");
                goto abort;
            }
            settings->floating_y = math_round_to_int(child->value.number);
            found_y = true;
        } else if (string_eq(key, STR("width"))) {
            if (child->type != JSON_TYPE_NUMBER) {
                error = STR("Expected number for \"width\"");
                goto abort;
            }
            settings->floating_width = math_round_to_int(child->value.number);
            found_width = true;
        } else if (string_eq(key, STR("height"))) {
            if (child->type != JSON_TYPE_NUMBER) {
                error = STR("Expected number for \"height\"");
                goto abort;
            }
            settings->floating_height = math_round_to_int(child->value.number);
            found_height = true;
        }
    }

    if (!found_maximized) { error = STR("Missing \"maximized\""); goto abort; }
    if (!found_fullscreen) { error = STR("Missing \"fullscreen\""); goto abort; }
    if (!found_x) { error = STR("Missing \"x\""); goto abort; }
    if (!found_y) { error = STR("Missing \"y\""); goto abort; }
    if (!found_width) { error = STR("Missing \"width\""); goto abort; }
    if (!found_height) { error = STR("Missing \"height\""); goto abort; }

    abort:

    if (error_out && (error.len > 0 || json_error.msg.len > 0)) {
        if (json_error.msg.len > 0) {
            *error_out = string_format(arena,
                                       STR("%F\n(%F:%F)"),
                                       FMT_STR(json_error.msg),
                                       FMT_UINT(json_error.start),
                                       FMT_UINT(json_error.end));
        } else {
            *error_out = string_copy(arena, error);
        }
    }

    scratch_end(scratch);

    return settings;
}
