#include <stdio.h>
#include <string.h>

#include "menu.h"
#include "theme.h"
#include "../interface/logger.h"

extern struct theme g_theme;

#define MENU_LOG_TAIL_BYTES 4096
#define MENU_LOG_MARGIN 24.0f

static char log_text[MENU_LOG_TAIL_BYTES + 1] = "";
static int log_len = 0;
static int log_scroll_to_bottom = 0;

static void log_set_message(const char *msg)
{
    snprintf(log_text, sizeof(log_text), "%s", msg ? msg : "");
    log_len = (int)strlen(log_text);
    log_scroll_to_bottom = 1;
}

static int log_count_lines(void)
{
    int lines = 1;

    for (int i = 0; i < log_len; ++i) {
        if (log_text[i] == '\n')
            lines++;
    }

    return lines;
}

static float log_initial_scroll_y(struct nk_context *ctx, float box_h)
{
    float row_h = ctx->style.font->height + ctx->style.edit.row_padding;
    float viewport_h = box_h - (2.0f * ctx->style.edit.padding.y +
                                2.0f * ctx->style.edit.border);
    float text_h = (float)log_count_lines() * row_h;

    if (viewport_h < row_h)
        viewport_h = row_h;

    if (text_h <= viewport_h)
        return 0.0f;

    return text_h - viewport_h;
}

void menu_log_open(struct option_widget *wgt, void *data)
{
    const char *filename;
    FILE *fp;
    long file_size;
    long start = 0;
    int seek_tail = 0;
    size_t bytes_read;

    (void)wgt;
    (void)data;

    filename = logger_get_filename();
    if (!filename || !filename[0]) {
        log_set_message("Log file is not available.");
        return;
    }

    fp = fopen(filename, "rb");
    if (!fp) {
        snprintf(log_text, sizeof(log_text),
                 "Unable to open log file:\n%s", filename);
        log_len = (int)strlen(log_text);
        log_scroll_to_bottom = 1;
        return;
    }

    if (fseek(fp, 0, SEEK_END) != 0) {
        fclose(fp);
        log_set_message("Unable to read log file.");
        return;
    }

    file_size = ftell(fp);
    if (file_size <= 0) {
        fclose(fp);
        log_set_message("(log is empty)");
        return;
    }

    if (file_size > MENU_LOG_TAIL_BYTES) {
        start = file_size - MENU_LOG_TAIL_BYTES;
        seek_tail = 1;
    }

    if (fseek(fp, start, SEEK_SET) != 0) {
        fclose(fp);
        log_set_message("Unable to read log file.");
        return;
    }

    bytes_read = fread(log_text, 1, MENU_LOG_TAIL_BYTES, fp);
    fclose(fp);

    log_text[bytes_read] = '\0';
    log_len = (int)bytes_read;

    if (seek_tail && log_len > 0) {
        char *line = memchr(log_text, '\n', (size_t)log_len);

        if (line && line + 1 < log_text + log_len) {
            size_t skip = (size_t)(line + 1 - log_text);
            size_t keep = (size_t)log_len - skip;

            memmove(log_text, line + 1, keep);
            log_len = (int)keep;
            log_text[log_len] = '\0';
        }
    }

    if (log_len <= 0)
        log_set_message("(log is empty)");
    else
        log_scroll_to_bottom = 1;
}

int menu_options_log(struct option_widget *wid_ary)
{
    for (int i = 0; i < MAX_OPTIONS; ++i) {
        memset(&wid_ary[i], 0, sizeof(struct option_widget));
        wid_ary[i].widget_type = WIDGET_TYPE_LABEL;
        wid_ary[i].data_type = WIDGET_DATA_TYPE_INT;
        wid_ary[i].menu_prev = KAT_MENU_OPTIONS_MISC;
        wid_ary[i].accept_input = 1;
    }

    return 0;
}

void menu_log_render(struct nk_context *ctx)
{
    struct nk_rect content = nk_window_get_content_region(ctx);
    struct nk_rect bounds = nk_widget_bounds(ctx);
    float remaining = content.h - (bounds.y - content.y);
    float side_margin = MENU_LOG_MARGIN;
    float edit_w;
    nk_flags flags = NK_EDIT_BOX | NK_EDIT_READ_ONLY;
    int edit_len = log_len;

    remaining -= MENU_LOG_MARGIN;
    if (remaining < g_theme.row_height_double)
        remaining = g_theme.row_height_double;

    if (content.w < side_margin * 3.0f)
        side_margin = content.w / 4.0f;
    edit_w = content.w - side_margin * 2.0f;
    if (edit_w < 1.0f)
        edit_w = 1.0f;

    nk_layout_row_begin(ctx, NK_STATIC, remaining, 3);
    nk_layout_row_push(ctx, side_margin);
    nk_spacing(ctx, 1);
    nk_layout_row_push(ctx, edit_w);

    if (log_scroll_to_bottom) {
        nk_edit_focus(ctx, flags);
        ctx->current->edit.cursor = log_len;
        ctx->current->edit.sel_start = log_len;
        ctx->current->edit.sel_end = log_len;
        ctx->current->edit.scrollbar.x = 0;
        ctx->current->edit.scrollbar.y =
            (nk_uint)log_initial_scroll_y(ctx, remaining);
        log_scroll_to_bottom = 0;
    }

    nk_edit_string(ctx, flags, log_text, &edit_len,
                   (int)sizeof(log_text), nk_filter_default);
    log_text[log_len] = '\0';

    nk_layout_row_push(ctx, side_margin);
    nk_spacing(ctx, 1);
    nk_layout_row_end(ctx);
}
