#define _POSIX_C_SOURCE 199309L

#include <ctype.h>
#include <signal.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

#include "buffer.h"
#include "input.h"
#include "kilo.h"
#include "terminal.h"
#include "ui.h"
#include "utils.h"

void editor_init(char *filename);
void editor_resize();

struct editor_state E;

int main(int argc, char **argv) {
    editor_init(argc >= 2 ? argv[1] : NULL);

    while (true) {
        ui_draw_screen();
        input_process_key(terminal_read_key());
    }

    return 0;
}

void editor_init(char *filename) {
    if (!isatty(STDIN_FILENO)) {
        printf("kilo only supports a terminal at standard in. Exiting.");
        exit(1);
    }

    if (terminal_enable_raw() == -1) die("term_enable_raw");

    if (terminal_get_win_size(&E.screenrows, &E.screencols) == -1)
        die("term_get_win_size");
    E.quit_times = 3;

    E.current_buf = buffer_create();
    if (filename)
        buffer_read_file(E.current_buf, filename);

    editor_set_message("Welcome to kilo! | CTRL-Q: Quit | CTRL-S: SAVE");
    terminal_clear();

    struct sigaction sa;
    sa.sa_handler = editor_resize;
    sigaction(SIGWINCH, &sa, NULL);
}

void editor_set_message(const char *fmt, ...) {
  va_list ap;
  va_start(ap, fmt);
  vsnprintf(E.message, sizeof(E.message), fmt, ap);
  va_end(ap);

  E.message_time = time(NULL);
}

// TODO: This could use some work
char *editor_prompt(const char *prompt) {
    size_t buf_cap = 64;
    size_t buf_size = 0;
    char *buf = malloc(buf_cap);

    char *prompt_prefix = strstr(prompt, "%s");
    int prefix_len = (prompt_prefix ? prompt_prefix - prompt : 0);

    buf[buf_size] = '\0';

    while (true) {
        editor_set_message(prompt, buf);
        ui_draw_screen();

        if (prompt_prefix)
            terminal_set_cursor_pos(E.screenrows + 2, prefix_len + buf_size + 1);

        KEY key = terminal_read_key();
        switch (key) {
            case BACKSPACE:
                if (buf_size > 0)
                    buf[--buf_size] = '\0';
                break;
            case ENTER:
                if (buf_size > 0) goto success;
                else goto failure;
                break;
            case ESCAPE:
                goto failure;
                break;
            default:
                if (isprint(key)) {
                    if (buf_size + 1 >= buf_cap)
                        buf = realloc(buf, buf_cap *= 2);

                    buf[buf_size] = key;
                    buf_size++;
                    buf[buf_size] = '\0';
                }
        }
    }

failure:
    free(buf);
    buf = NULL;
success:
    if (buf)
        buf = realloc(buf, buf_size + 1);

    editor_set_message("");
    return buf;
}

void editor_resize() {
    if (terminal_get_win_size(&E.screenrows, &E.screencols) == -1)
        die("term_get_win_size");

    ui_draw_screen();
}