/*
 * wordprocessor.hpp
 * Shared declarations for the Word Processor app
 * Copyright (c) 2026 Daniel Hammer
 */

#pragma once

#include <montauk/syscall.h>
#include <montauk/string.h>
#include <montauk/heap.h>
#include <gui/gui.hpp>
#include <gui/standalone.hpp>
#include <gui/truetype.hpp>
#include <gui/svg.hpp>
#include <gui/mtk/widgets.hpp>

extern "C" {
#include <string.h>
#include <stdio.h>
}

using namespace gui;

static constexpr int INIT_W            = 900;
static constexpr int INIT_H            = 600;
static constexpr int WP_TOOLBAR_H      = 36;
static constexpr int WP_PATHBAR_H      = 32;
static constexpr int WP_STATUS_H       = 24;
static constexpr int WP_SCROLLBAR_W    = 12;
static constexpr int WP_MARGIN         = 16;
// A4 page dimensions at 96 DPI screen resolution (210mm x 297mm = 8.27" x 11.69")
static constexpr int WP_A4_PAGE_W      = 794;   // 8.27" * 96 DPI
static constexpr int WP_A4_PAGE_H      = 1122;  // 11.69" * 96 DPI
static constexpr int WP_A4_MARGIN_X    = 96;    // 1 inch = 96px margins (same as print)
static constexpr int WP_A4_MARGIN_Y    = 96;
// Content width inside A4 page (794 - 96*2 - 16*2 = 570... but we use WP_A4 margins internally)
// A4 text area = WP_A4_PAGE_W - WP_A4_MARGIN_X * 2 = 794 - 192 = 602
static constexpr int WP_A4_CONTENT_W   = 602;
// Vertical page offset from top of edit area (page top at edit_y + WP_PAGE_TOP_MARGIN)
static constexpr int WP_PAGE_TOP_MARGIN = 20;
static constexpr int WP_PAGE_SHADOW_OFFSET = 6;
static constexpr int WP_MAX_RUNS       = 1024;
static constexpr int WP_MAX_TEXT       = 262144;
static constexpr int WP_MAX_WRAP_LINES = 4096;
static constexpr int WP_MAX_PARAGRAPHS = 4096;
static constexpr int WP_DEFAULT_SIZE   = 18;
static constexpr int WP_UNDO_MAX       = 24;
static constexpr int WP_SCREEN_DPI     = 96;
static constexpr int WP_PRINT_DPI      = 150;
static constexpr int WP_PARA_STEP      = 12;
static constexpr int WP_SPACE_STEP     = 6;
static constexpr int WP_LIST_LEFT      = 28;
static constexpr int WP_LIST_HANGING   = -18;
static constexpr int WP_LIST_MARKER_W  = 20;
static constexpr int WP_BTN_OPEN_X     = 4;
static constexpr int WP_BTN_SAVE_X     = 32;
static constexpr int WP_BTN_PRINT_X    = 60;
static constexpr int WP_BTN_EXPORT_X   = 88;
static constexpr int WP_BTN_UNDO_X     = 122;
static constexpr int WP_BTN_REDO_X     = 150;
static constexpr int WP_BTN_BOLD_X     = 184;
static constexpr int WP_BTN_ITALIC_X   = 212;
static constexpr int WP_FONT_DD_X      = 244;
static constexpr int WP_FONT_DD_W      = 74;
static constexpr int WP_SIZE_DD_X      = 324;
static constexpr int WP_SIZE_DD_W      = 36;
static constexpr int WP_BTN_ALIGN_L_X  = 372;
static constexpr int WP_BTN_ALIGN_C_X  = 400;
static constexpr int WP_BTN_ALIGN_R_X  = 428;
static constexpr int WP_BTN_BULLET_X   = 460;
static constexpr int WP_BTN_NUMBER_X   = 488;
static constexpr int WP_BTN_OUTDENT_X  = 524;
static constexpr int WP_BTN_INDENT_X   = 552;
static constexpr int WP_LINE_DD_X      = 586;
static constexpr int WP_LINE_DD_W      = 48;
static constexpr int WP_BTN_DIVIDER_X  = 642;
static constexpr int WP_BTN_SECTION_X  = 674;
static constexpr int WP_BTN_PAGEVIEW_X  = 712;
static constexpr int WP_DIVIDER_ROW_H  = 24;
static constexpr int WP_DIVIDER_FLYOUT_W = 216;
static constexpr int WP_SPECIAL_CHAR_ROW_H = 24;
static constexpr int WP_SPECIAL_CHAR_FLYOUT_W = 144;

static constexpr int FONT_ROBOTO    = 0;
static constexpr int FONT_NOTOSERIF = 1;
static constexpr int FONT_C059      = 2;
static constexpr int FONT_COUNT     = 3;

static constexpr uint8_t STYLE_BOLD   = 0x01;
static constexpr uint8_t STYLE_ITALIC = 0x02;

inline constexpr const char* WP_FONT_NAMES[FONT_COUNT] = {
    "Roboto",
    "NotoSerif",
    "C059",
};

inline constexpr int WP_SIZE_OPTIONS[] = { 12, 14, 16, 18, 20, 24, 28, 36 };
static constexpr int WP_SIZE_OPTION_COUNT = 8;
inline constexpr int WP_LINE_SPACING_OPTIONS[] = { 100, 125, 150, 200 };
static constexpr int WP_LINE_SPACING_OPTION_COUNT = 4;

inline int wp_points_to_pixels(int points, int dpi) {
    if (points <= 0) points = 1;
    if (dpi <= 0) dpi = WP_SCREEN_DPI;
    int px = (points * dpi + 36) / 72;
    return px > 0 ? px : 1;
}

inline int wp_screen_font_pixels(int points) {
    return wp_points_to_pixels(points, WP_SCREEN_DPI);
}

inline int wp_print_font_pixels(int points) {
    return wp_points_to_pixels(points, WP_PRINT_DPI);
}

inline int wp_scale_layout_units(int units, int dpi) {
    if (dpi <= 0) dpi = WP_SCREEN_DPI;
    int scaled = (units * dpi + (units >= 0 ? WP_SCREEN_DPI / 2 : -WP_SCREEN_DPI / 2)) / WP_SCREEN_DPI;
    return scaled;
}

struct WpSpecialCharOption {
    uint8_t code;
    const char* label;
};

struct WpDividerOption {
    uint8_t type;
    const char* label;
};

inline constexpr WpSpecialCharOption WP_SPECIAL_CHAR_OPTIONS[] = {
    { 0xA7, "Section" },
    { 0xB6, "Pilcrow" },
    { 0x96, "En Dash" },
    { 0x97, "Em Dash" },
    { 0xA9, "Copyright" },
    { 0xAE, "Registered" },
    { 0xB0, "Degree" },
    { 0xB1, "Plus/Minus" },
};
static constexpr int WP_SPECIAL_CHAR_OPTION_COUNT = 8;

enum ParagraphAlign : uint8_t {
    PARA_ALIGN_LEFT = 0,
    PARA_ALIGN_CENTER = 1,
    PARA_ALIGN_RIGHT = 2,
};

enum ParagraphListType : uint8_t {
    PARA_LIST_NONE = 0,
    PARA_LIST_BULLET = 1,
    PARA_LIST_NUMBER = 2,
};

enum ParagraphDividerType : uint8_t {
    PARA_DIVIDER_NONE = 0,
    PARA_DIVIDER_SINGLE = 1,
    PARA_DIVIDER_DOUBLE = 2,
    PARA_DIVIDER_DOTTED = 3,
    PARA_DIVIDER_DASHED = 4,
    PARA_DIVIDER_HEAVY = 5,
    PARA_DIVIDER_THIN_THICK = 6,
    PARA_DIVIDER_THICK_THIN = 7,
};

inline constexpr WpDividerOption WP_DIVIDER_OPTIONS[] = {
    { PARA_DIVIDER_SINGLE, "Single Line" },
    { PARA_DIVIDER_DOUBLE, "Double Line" },
    { PARA_DIVIDER_DOTTED, "Dotted Line" },
    { PARA_DIVIDER_DASHED, "Dashed Line" },
    { PARA_DIVIDER_HEAVY, "Heavy Line" },
    { PARA_DIVIDER_THIN_THICK, "Thin/Thick Rule" },
    { PARA_DIVIDER_THICK_THIN, "Thick/Thin Rule" },
};
static constexpr int WP_DIVIDER_OPTION_COUNT = 7;

struct WPFontTable {
    TrueTypeFont* fonts[FONT_COUNT][4];
    bool loaded;
};

struct StyledRun {
    char* text;
    int len;
    int cap;
    uint8_t font_id;
    uint8_t size;
    uint8_t flags;
};

struct WrapLine {
    int run_idx;
    int run_offset;
    int char_count;
    int y;
    int height;
    int baseline;
    int x;
    int width;
    int paragraph_idx;
    int list_number;
    uint8_t divider_type;
    bool first_in_paragraph;
};

struct ParagraphStyle {
    uint8_t align;
    uint8_t list_type;
    uint8_t line_spacing;
    uint8_t divider_type;
    int16_t left_indent;
    int16_t first_line_indent;
    int16_t space_before;
    int16_t space_after;
};

struct UndoSnapshot {
    uint8_t* data;
    int size;
    int cursor_abs;
    int sel_anchor;
    int sel_end;
    bool has_selection;
    bool modified;
};

inline bool wp_left_held(uint8_t buttons) {
    return (buttons & 0x01) != 0;
}

inline bool wp_left_pressed(uint8_t buttons, uint8_t prev_buttons) {
    return (buttons & 0x01) && !(prev_buttons & 0x01);
}

inline bool wp_left_released(uint8_t buttons, uint8_t prev_buttons) {
    return !(buttons & 0x01) && (prev_buttons & 0x01);
}

inline bool wp_right_pressed(uint8_t buttons, uint8_t prev_buttons) {
    return (buttons & 0x02) && !(prev_buttons & 0x02);
}

enum WpContextItem : int {
    WP_CTX_CUT = 0,
    WP_CTX_COPY,
    WP_CTX_PASTE,
    WP_CTX_DELETE,
    WP_CTX_COUNT,
};

struct WpScrollbar {
    Rect bounds;
    int content_height;
    int view_height;
    int scroll_offset;
    bool dragging;
    int drag_start_y;
    int drag_start_offset;
    Color bg;
    Color fg;
    Color hover_fg;
    bool hovered;

    void init(int x, int y, int w, int h) {
        bounds = {x, y, w, h};
        content_height = 0;
        view_height = h;
        scroll_offset = 0;
        dragging = false;
        drag_start_y = 0;
        drag_start_offset = 0;
        bg = colors::SCROLLBAR_BG;
        fg = colors::SCROLLBAR_FG;
        hover_fg = Color::from_rgb(0xA0, 0xA0, 0xA0);
        hovered = false;
    }

    int thumb_height() const {
        if (content_height <= view_height) return bounds.h;
        int th = (view_height * bounds.h) / content_height;
        return th < 20 ? 20 : th;
    }

    int thumb_y() const {
        if (content_height <= view_height) return bounds.y;
        int range = bounds.h - thumb_height();
        int max_scroll = content_height - view_height;
        if (max_scroll <= 0) return bounds.y;
        return bounds.y + (scroll_offset * range) / max_scroll;
    }

    int max_scroll() const {
        int ms = content_height - view_height;
        return ms > 0 ? ms : 0;
    }

    void handle_mouse(int x, int y, uint8_t buttons, uint8_t prev_buttons, int scroll) {
        if (content_height <= view_height) return;

        Rect thumb_rect = {bounds.x, thumb_y(), bounds.w, thumb_height()};
        hovered = thumb_rect.contains(x, y);

        if (hovered && wp_left_pressed(buttons, prev_buttons)) {
            dragging = true;
            drag_start_y = y;
            drag_start_offset = scroll_offset;
        }

        if (dragging && wp_left_held(buttons)) {
            int dy = y - drag_start_y;
            int range = bounds.h - thumb_height();
            if (range > 0) {
                int ms = max_scroll();
                scroll_offset = drag_start_offset + (dy * ms) / range;
                if (scroll_offset < 0) scroll_offset = 0;
                if (scroll_offset > ms) scroll_offset = ms;
            }
        }

        if (!wp_left_held(buttons)) {
            dragging = false;
        }

        if (bounds.contains(x, y) && scroll != 0) {
            scroll_offset += scroll * 20;
            int ms = max_scroll();
            if (scroll_offset < 0) scroll_offset = 0;
            if (scroll_offset > ms) scroll_offset = ms;
        }
    }
};

struct WordProcessorState {
    StyledRun runs[WP_MAX_RUNS];
    int run_count;
    int total_text_len;

    int cursor_run;
    int cursor_offset;

    int sel_anchor;
    int sel_end;
    bool has_selection;
    bool mouse_selecting;

    uint8_t cur_font_id;
    uint8_t cur_size;
    uint8_t cur_flags;

    WpScrollbar scrollbar;
    int content_height;

    WrapLine* wrap_lines;
    int wrap_line_count;
    int wrap_line_cap;
    bool wrap_dirty;
    int last_wrap_width;
    int last_wrap_dpi;

    ParagraphStyle paragraphs[WP_MAX_PARAGRAPHS];
    int paragraph_count;

    bool modified;
    char filepath[256];
    char filename[64];
    char preferred_printer_uri[256];
    char preferred_printer_name[128];
    uint32_t preferred_print_copies;
    char status_msg[160];

    bool show_pathbar;
    bool pathbar_save_mode;
    char pathbar_text[256];
    int pathbar_cursor;
    int pathbar_len;

    bool font_dropdown_open;
    bool size_dropdown_open;
    bool line_spacing_dropdown_open;
    bool divider_flyout_open;
    bool special_char_flyout_open;
    mtk::ContextMenuState context_menu;
    bool page_view_mode;
    bool pending_page_view_toggle;

    UndoSnapshot undo[WP_UNDO_MAX];
    int undo_count;
    int undo_pos;
};

extern int g_win_w;
extern int g_win_h;
extern WsWindow g_win;
extern WordProcessorState g_wp;
extern WPFontTable g_wp_fonts;
extern SvgIcon g_icon_folder;
extern SvgIcon g_icon_save;
extern SvgIcon g_icon_print;
extern SvgIcon g_icon_export_pdf;
extern SvgIcon g_icon_undo;
extern SvgIcon g_icon_redo;
extern SvgIcon g_icon_align_left;
extern SvgIcon g_icon_align_center;
extern SvgIcon g_icon_align_right;
extern SvgIcon g_icon_list_bullet;
extern SvgIcon g_icon_list_number;
extern SvgIcon g_icon_indent_less;
extern SvgIcon g_icon_indent_more;
extern SvgIcon g_icon_page_view;
extern TrueTypeFont* g_ui_font;
extern TrueTypeFont* g_ui_bold;

void wp_load_fonts();
void wp_load_icons();
void wp_init_paragraph_style(ParagraphStyle* para);
void wp_init_empty_document(WordProcessorState* wp);
void wp_free_document(WordProcessorState* wp);
void wp_cleanup_state();

TrueTypeFont* wp_get_font(int font_id, uint8_t flags);

int  wp_abs_pos(WordProcessorState* wp, int run, int offset);
void wp_pos_to_run(WordProcessorState* wp, int abs_pos, int* out_run, int* out_offset);
char wp_char_at(WordProcessorState* wp, int abs_pos);
int  wp_find_paragraph_at(WordProcessorState* wp, int abs_pos);
void wp_selected_paragraph_range(WordProcessorState* wp, int* out_start_para, int* out_end_para);

void wp_insert_char(WordProcessorState* wp, char c);
void wp_delete_char(WordProcessorState* wp);
void wp_backspace(WordProcessorState* wp);

void wp_cursor_left(WordProcessorState* wp);
void wp_cursor_right(WordProcessorState* wp);
void wp_cursor_up(WordProcessorState* wp);
void wp_cursor_down(WordProcessorState* wp);

void wp_clear_selection(WordProcessorState* wp);
void wp_sel_range(WordProcessorState* wp, int* out_start, int* out_end);
void wp_start_selection(WordProcessorState* wp);
void wp_update_selection_to_cursor(WordProcessorState* wp);
void wp_apply_style_to_selection(WordProcessorState* wp, int mode, int value);
void wp_apply_alignment(WordProcessorState* wp, uint8_t align);
void wp_adjust_paragraph_indent(WordProcessorState* wp, int delta);
void wp_adjust_paragraph_first_line_indent(WordProcessorState* wp, int delta);
void wp_adjust_paragraph_spacing_before(WordProcessorState* wp, int delta);
void wp_adjust_paragraph_spacing_after(WordProcessorState* wp, int delta);
void wp_cycle_line_spacing(WordProcessorState* wp);
void wp_set_line_spacing(WordProcessorState* wp, int value);
void wp_toggle_list(WordProcessorState* wp, uint8_t list_type);
void wp_insert_divider(WordProcessorState* wp, uint8_t divider_type);
void wp_delete_selection(WordProcessorState* wp);
char* wp_selection_plain_text_alloc(WordProcessorState* wp, int* out_len);
bool wp_insert_text(WordProcessorState* wp, const char* text, int len);
bool wp_replace_selection_with_text(WordProcessorState* wp, const char* text, int len);

void wp_recompute_wrap(WordProcessorState* wp, int content_w, int layout_dpi);
int  wp_find_wrap_line(WordProcessorState* wp, int abs_pos);
int  wp_wrap_line_start(WordProcessorState* wp, int line_idx);
void wp_ensure_cursor_visible(WordProcessorState* wp, int view_h);

void wp_set_filepath(WordProcessorState* wp, const char* path);
void wp_start_pathbar(WordProcessorState* wp, bool save_mode, const char* initial_text);
void wp_open_save_pathbar(WordProcessorState* wp);
void wp_save_file(WordProcessorState* wp);
void wp_load_file(WordProcessorState* wp, const char* path);

void wp_history_reset(WordProcessorState* wp);
void wp_history_checkpoint(WordProcessorState* wp);
void wp_history_mark_saved(WordProcessorState* wp);
bool wp_undo(WordProcessorState* wp);
bool wp_redo(WordProcessorState* wp);

bool wp_print_document(WordProcessorState* wp, char* out_status, int out_status_len);
bool wp_export_pdf_document(WordProcessorState* wp, const char* path, char* out_status, int out_status_len);
bool wp_context_item_enabled(WordProcessorState* wp, int item);
void wp_build_context_items(WordProcessorState* wp,
                            mtk::ContextMenuItem items[WP_CTX_COUNT]);

void wp_render();
void wp_handle_mouse(const Montauk::WinEvent& ev);
void wp_handle_key(const Montauk::KeyEvent& key);
