#ifndef HUSH_EDITOR_H
#define HUSH_EDITOR_H
#include "document.h"
#include "layout_cache.h"
#include "undo.h"
#include "fonts.h"
#include "clay.h"
#include <stdbool.h>
typedef struct {
Document doc;
int cursorBlock;
int cursorOffset;
float preferredX; /* sticky x used across consecutive up/down moves; <0 means "recompute" */
char *filePath; /* owned, may be NULL if the editor was started without a file */
bool dirty;
UndoManager undo;
int selAnchorBlock, selAnchorOffset; /* selection anchor; anchor == cursor means "no selection" */
} EditorState;
void editor_init(EditorState *ed);
void editor_free(EditorState *ed);
/* Loads `path` into the editor, remembering it for future saves regardless of success
(so Ctrl+S can create a brand new file). Returns false if the file could not be read. */
bool editor_load(EditorState *ed, const char *path);
bool editor_save(EditorState *ed);
/* Basename of ed->filePath, or "Untitled" if the editor has no associated file yet. */
const char *editor_display_name(const EditorState *ed);
void editor_insert_utf8(EditorState *ed, const char *bytes, int n);
void editor_paste(EditorState *ed, const char *text, int len);
void editor_backspace(EditorState *ed);
void editor_delete_forward(EditorState *ed);
void editor_enter(EditorState *ed);
bool editor_undo(EditorState *ed);
bool editor_redo(EditorState *ed);
/* Flips a task-list block between checked/unchecked (undo-wrapped). Returns false if
`blockIndex` is out of range or isn't a task block. */
bool editor_toggle_task(EditorState *ed, int blockIndex);
/* Converts ed->cursorBlock to `newType`, undo-wrapped. quick_insert clears the block's text
first (it's only ever called on a bare "@word" typed for exactly this purpose); turn_into
preserves whatever the block already held. Both scrub embedded '\n' bytes and clear `lang`
when leaving BLOCK_CODE, and both handle BLOCK_HR's "insert a following empty paragraph and
move the cursor there" special case (quick_insert only -- turn_into never offers BLOCK_HR). */
void editor_quick_insert_apply(EditorState *ed, BlockType newType);
void editor_turn_into_apply(EditorState *ed, BlockType newType);
/* Inserts a rows*cols GFM table (row 0 as BLOCK_TABLE_HEADER_CELL, the rest BLOCK_TABLE_CELL,
empty text, left-aligned), undo-wrapped. Only ever called on the bare "@word" the user just
typed (like editor_quick_insert_apply) -- always replaces ed->cursorBlock, discarding
whatever it held. Cursor lands in the first header cell. */
void editor_insert_table(EditorState *ed, int rows, int cols);
/* Tab (dir=+1) / Shift+Tab (dir=-1) cell navigation: table cells are always laid out as one
contiguous, row-major run of blocks, so moving to the next/previous cell is just cursorBlock
+/- 1 (no row/col arithmetic needed) -- wrapping row-to-row happens for free. At either end of
the table, moves into the following/preceding block if one exists, or inserts a fresh empty
paragraph after the table if it's document-final. No-op if ed->cursorBlock isn't a table cell,
or (Shift+Tab from the table's very first cell) nowhere to go. Not undo-wrapped itself -- the
occasional "insert a trailing paragraph" side effect is treated as harmless bookkeeping, same
as how plain cursor movement never touches undo; callers that need it wrapped (none today) can
wrap it themselves. */
void editor_table_move_cell(EditorState *ed, int dir);
/* `extend`: false moves the caret and collapses/resets the selection anchor to it (plain
movement); true moves only the caret, extending the selection from the existing anchor
(Shift+movement). */
void editor_move_left(EditorState *ed, bool extend);
void editor_move_right(EditorState *ed, bool extend);
void editor_move_home(EditorState *ed, bool extend);
void editor_move_end(EditorState *ed, bool extend);
void editor_move_vertical(EditorState *ed, LayoutCache *cache, SDL2_Font *fonts, int dir, bool extend);
/* Mouse press: moves the caret and (re)establishes the selection anchor there. */
void editor_click(EditorState *ed, LayoutCache *cache, SDL2_Font *fonts, Clay_Vector2 point);
/* Mouse drag (button held after the initial press): moves only the caret, extending the
selection from whatever editor_click most recently set as the anchor. */
void editor_drag_to(EditorState *ed, LayoutCache *cache, SDL2_Font *fonts, Clay_Vector2 point);
bool editor_has_selection(const EditorState *ed);
/* Normalizes the anchor/cursor pair into forward document order. */
void editor_selection_range(const EditorState *ed, int *startBlock, int *startOffset, int *endBlock, int *endOffset);
/* Returns the selected text (blocks joined with '\n'), or NULL if there is no selection.
Caller must free() the result. */
char *editor_selection_to_text(const EditorState *ed, int *outLen);
#endif