/*
 * cw-wide.h: the wide-character curses calls of Dungeon Crawl Stone Soup's console front end (libunix.cc)
 * for the fredygames.com browser build, on top of the curses-web layer (Builds/curses-web), which keeps a
 * Unicode code point in every cell but only has the narrow (chtype) API. Included after <curses.h> in
 * libunix.cc (-include); nothing in curses-web changes.
 *
 * cchar_t holds one code point, the attributes and the colour pair. The cell writes and reads go straight
 * to stdscr's cells (cw_cell), the way curses.c's own set_cell does, and mark the row as touched so the
 * next refresh draws it. Text strings go through waddnstr as UTF-8, which curses-web decodes.
 *
 * Copyright (C) 2026 FredyGames. GNU GPL version 2 or later, like Dungeon Crawl Stone Soup.
 */
#ifndef FREDYGAMES_CW_WIDE_H
#define FREDYGAMES_CW_WIDE_H

#include <wchar.h>
#include <curses.h>

/* the WA_* names of the wide API are the A_* attributes */
#ifndef WA_NORMAL
#define WA_NORMAL A_NORMAL
#define WA_STANDOUT A_STANDOUT
#define WA_UNDERLINE A_UNDERLINE
#define WA_REVERSE A_REVERSE
#define WA_BLINK A_BLINK
#define WA_DIM A_DIM
#define WA_BOLD A_BOLD
#define WA_ITALIC A_ITALIC
#endif

#ifndef CCHARW_MAX
#define CCHARW_MAX 5
#endif

typedef struct
{
    attr_t attr;
    short color_pair;
    wchar_t chars[CCHARW_MAX];
} cchar_t;

static inline int setcchar(cchar_t *wcval, const wchar_t *wch, const attr_t attrs, short color_pair,
                           const void *opts)
{
    (void)opts;
    if (!wcval) return ERR;
    wcval->attr = attrs & (A_ATTRIBUTES & ~A_COLOR);
    wcval->color_pair = color_pair ? color_pair : (short)PAIR_NUMBER(attrs);
    wcval->chars[0] = wch ? wch[0] : L' ';
    wcval->chars[1] = 0;
    return OK;
}

static inline int getcchar(const cchar_t *wcval, wchar_t *wch, attr_t *attrs, short *color_pair, void *opts)
{
    (void)opts;
    if (!wcval) return ERR;
    if (wch) { wch[0] = wcval->chars[0]; wch[1] = 0; }
    if (attrs) *attrs = wcval->attr;
    if (color_pair) *color_pair = wcval->color_pair;
    return OK;
}

static inline void cw_wide_touch(WINDOW *w, int y)
{
    if (w->_touch && y >= 0 && y <= w->_maxy) w->_touch[y] = 1;
}

/* Writes n cells (n < 0: until a zero character) from (y, x) without moving the cursor, like ncurses. */
static inline int mvwadd_wchnstr(WINDOW *w, int y, int x, const cchar_t *wchstr, int n)
{
    if (!w || !wchstr || y < 0 || y > w->_maxy || x < 0 || x > w->_maxx) return ERR;
    for (int i = 0; (n < 0 ? wchstr[i].chars[0] != 0 : i < n) && x + i <= w->_maxx; i++)
    {
        cw_cell *c = &w->_cells[y * w->_stride + x + i];
        uint32_t ch = (uint32_t)wchstr[i].chars[0];
        c->ch = ch > 0xFFFF ? '?' : ch;
        c->attr = (uint16_t)((wchstr[i].attr & (A_ATTRIBUTES & ~A_COLOR)) >> 16);
        c->pair = (uint8_t)wchstr[i].color_pair;
    }
    cw_wide_touch(w, y);
    return OK;
}
#define mvadd_wchnstr(y, x, s, n) mvwadd_wchnstr(stdscr, (y), (x), (s), (n))
#define add_wchnstr(s, n) mvwadd_wchnstr(stdscr, stdscr->_cury, stdscr->_curx, (s), (n))

static inline int mvwin_wch(WINDOW *w, int y, int x, cchar_t *wcval)
{
    if (!w || !wcval || y < 0 || y > w->_maxy || x < 0 || x > w->_maxx) return ERR;
    const cw_cell *c = &w->_cells[y * w->_stride + x];
    wcval->chars[0] = (wchar_t)c->ch;
    wcval->chars[1] = 0;
    wcval->attr = (attr_t)c->attr << 16;
    wcval->color_pair = c->pair;
    wmove(w, y, x);
    return OK;
}
#define mvin_wch(y, x, c) mvwin_wch(stdscr, (y), (x), (c))

/* Wide string out: encoded as UTF-8 for waddnstr, which curses-web decodes back into code points. */
static inline int waddnwstr(WINDOW *w, const wchar_t *wstr, int n)
{
    if (!w || !wstr) return ERR;
    char buf[1024];
    int len = 0;
    for (int i = 0; (n < 0 || i < n) && wstr[i]; i++)
    {
        uint32_t c = (uint32_t)wstr[i];
        if (len > (int)sizeof buf - 5)
        {
            waddnstr(w, buf, len);
            len = 0;
        }
        if (c < 0x80) buf[len++] = (char)c;
        else if (c < 0x800) { buf[len++] = (char)(0xC0 | c >> 6); buf[len++] = (char)(0x80 | (c & 63)); }
        else if (c < 0x10000)
        {
            buf[len++] = (char)(0xE0 | c >> 12);
            buf[len++] = (char)(0x80 | ((c >> 6) & 63));
            buf[len++] = (char)(0x80 | (c & 63));
        }
        else buf[len++] = '?';
    }
    return len ? waddnstr(w, buf, len) : OK;
}
#define addnwstr(s, n) waddnwstr(stdscr, (s), (n))
#define addwstr(s) waddnwstr(stdscr, (s), -1)

/* Key in: special keys (KEY_*) come back as KEY_CODE_YES, everything else as a character. */
static inline int wget_wch(WINDOW *w, wint_t *wch)
{
    int c = wgetch(w);
    if (c == ERR) return ERR;
    if (wch) *wch = (wint_t)c;
    return (c >= KEY_MIN && c <= KEY_MAX) ? KEY_CODE_YES : OK;
}
#define get_wch(c) wget_wch(stdscr, (c))

#endif
