/*
 * graphics.h — Turbo-C-совместимый (функционально) BGI-слой для Sprinter.
 *
 * Слой поверх низкоуровневого gfx.h.  Состояние (текущий цвет, позиция,
 * границы экрана) хранится внутри — как в оригинальном BGI, где функции
 * рисуют «текущим» цветом от «текущей» позиции.
 *
 * РЕЖИМ фиксируется на этапе ЛИНКОВКИ выбором driver-библиотеки:
 *   sprinter-cc --gfx 256   → 320×256×256 (эта версия)
 *   (--gfx 16 → 640×256×16 будет позже; API идентичен, менять код не надо)
 * Одновременно два режима использовать нельзя.
 *
 * Отличия от Turbo-C (функциональная, а не буквальная совместимость):
 *   - initgraph() без аргументов-указателей на драйвер: драйвер задан
 *     линковкой, грузить .bgi-файл с диска не нужно.
 *   - в 256-режиме initgraph() загружает EGA-совместимые цвета 0..15 в
 *     палитру, так что setcolor(RED) и т.п. работают как в Turbo-C;
 *     индексы 16..255 свободны под свои цвета (getmaxcolor() = 255).
 *   - fill-паттерны/стили линий/стили текста — Фаза 2 (пока bar/bar-подобное
 *     заливается текущим цветом, линии сплошные, текст 8×8).
 */

#ifndef GRAPHICS_H
#define GRAPHICS_H

#include <stdint.h>

/* ---- Стандартные EGA/VGA цвета (индексы палитры 0..15) ------------ */
enum {
    BLACK = 0, BLUE, GREEN, CYAN, RED, MAGENTA, BROWN, LIGHTGRAY,
    DARKGRAY, LIGHTBLUE, LIGHTGREEN, LIGHTCYAN, LIGHTRED, LIGHTMAGENTA,
    YELLOW, WHITE
};

/* 8-битный цвет (индекс палитры).  При смене формата достаточно поменять
 * один typedef — все public-функции используют этот тип. */
typedef uint8_t color_t;

/* ---- Коды graphresult() ------------------------------------------ */
#define grOk        0
#define grNoInitGraph (-1)
#define grError     (-11)

/* ---- Внутреннее состояние BGI ------------------------------------- *
 * НЕ трогать напрямую — только через функции ниже.  Объявлено здесь,
 * чтобы тривиальные аксессоры были inline (без call/ret и без
 * .rel-модуля на каждый геттер).  Именно `inline` БЕЗ static: SDCC
 * инлайнит вызов и НЕ эмитит standalone-тело (static inline эмитил бы
 * мёртвую копию в каждый включивший модуль — проверено 2026-07-10);
 * если SDCC когда-нибудь откажется инлайнить — будет громкая ошибка
 * линковки, не тихая деградация.  Хранилище — libbgi/common/
 * _bgi_state.c и _bgi_fill_state.c. */
extern uint8_t _bgi_fg, _bgi_bg;
extern int     _bgi_cx, _bgi_cy;
extern int     _bgi_maxx, _bgi_maxy, _bgi_maxcolor;
extern int     _bgi_result;
extern uint8_t _bgi_fill_pattern, _bgi_fill_color;

/* ---- Setup / teardown -------------------------------------------- */

/* Перейти в графический режим (для этой либы — 320×256×256), загрузить
 * EGA-палитру, сбросить состояние: цвет = WHITE, фон = BLACK, текущая
 * позиция = (0,0).  Предыдущий видеорежим запоминается для closegraph. */
void initgraph(void);

/* Вернуться в текстовый режим, действовавший до initgraph(). */
void closegraph(void);

/* Код последней ошибки; вызов сбрасывает его в grOk (как в BGI). */
inline int graphresult(void)
{
    int r = _bgi_result;
    _bgi_result = grOk;
    return r;
}

/* Очистить экран фоновым цветом и вернуть позицию в (0,0). */
void cleardevice(void);

/* ---- Границы и цвета --------------------------------------------- */

inline int getmaxx(void) { return _bgi_maxx; }           /* 319 */
inline int getmaxy(void) { return _bgi_maxy; }           /* 255 */
inline color_t getmaxcolor(void) { return (color_t)_bgi_maxcolor; } /* 255 */

/* текущий цвет рисования */
inline void setcolor(color_t color) { _bgi_fg = (uint8_t)color; }
inline color_t getcolor(void) { return _bgi_fg; }
/* фоновый цвет (для cleardevice/текста) */
inline void setbkcolor(color_t color) { _bgi_bg = (uint8_t)color; }
inline color_t getbkcolor(void) { return _bgi_bg; }

/* ---- Точки ------------------------------------------------------- */

void putpixel(int x, int y, color_t color);
color_t getpixel(int x, int y);

/* ---- Текущая позиция и линии ------------------------------------- */

/* задать текущую позицию (CP) */
inline void moveto(int x, int y) { _bgi_cx = x; _bgi_cy = y; }
/* сдвинуть CP относительно */
inline void moverel(int dx, int dy) { _bgi_cx += dx; _bgi_cy += dy; }
inline int getx(void) { return _bgi_cx; }
inline int gety(void) { return _bgi_cy; }

void lineto(int x, int y);          /* линия CP→(x,y), CP := (x,y) */
void linerel(int dx, int dy);       /* линия CP→CP+(dx,dy), CP сдвигается */
void line(int x1, int y1, int x2, int y2);  /* линия, CP не меняет */

/* ---- Фигуры ------------------------------------------------------ */

void rectangle(int left, int top, int right, int bottom);   /* контур */
void bar(int left, int top, int right, int bottom);         /* заливка */
void circle(int x, int y, int radius);                      /* окружность */

/* Дуги/эллипсы: угол в градусах, 0°=восток, против часовой стрелки.
 * Полный эллипс — ellipse(x,y,0,360,xr,yr). */
void arc(int x, int y, int stangle, int endangle, int radius);
void ellipse(int x, int y, int stangle, int endangle,
             int xradius, int yradius);

/* Ломаная по numpoints точкам {x0,y0,x1,y1,…}; НЕ замыкается сама. */
void drawpoly(int numpoints, const int *polypoints);

/* ---- Стиль линий ------------------------------------------------- *
 * Действует на line/lineto/linerel/rectangle/drawpoly.  USERBIT_LINE
 * использует 16-битный upattern.  thickness: NORM_WIDTH или THICK_WIDTH.
 * (Окружности/дуги пока всегда сплошные 1px — упрощение.) */
enum { SOLID_LINE = 0, DOTTED_LINE, CENTER_LINE, DASHED_LINE, USERBIT_LINE };
#define NORM_WIDTH  1
#define THICK_WIDTH 3

struct linesettingstype { int linestyle; unsigned upattern; int thickness; };

void setlinestyle(int linestyle, unsigned upattern, int thickness);
void getlinesettings(struct linesettingstype *lineinfo);

/* ---- Заливки ----------------------------------------------------- *
 * Стиль заливки — паттерн (8×8) + цвет; действует на bar/bar3d/
 * fillpoly/fillellipse.  SOLID_FILL заполняет сплошняком, EMPTY_FILL —
 * фоновым цветом.  USER_FILL пока трактуется как SOLID. */
enum {
    EMPTY_FILL = 0, SOLID_FILL, LINE_FILL, LTSLASH_FILL, SLASH_FILL,
    BKSLASH_FILL, LTBKSLASH_FILL, HATCH_FILL, XHATCH_FILL,
    INTERLEAVE_FILL, WIDE_DOT_FILL, CLOSE_DOT_FILL, USER_FILL
};

struct fillsettingstype { int pattern; color_t color; };

inline void setfillstyle(int pattern, color_t color)
{
    _bgi_fill_pattern = (uint8_t)pattern;
    _bgi_fill_color   = (uint8_t)color;
}
void getfillsettings(struct fillsettingstype *fillinfo);

/* bar — залитый прямоугольник (текущий стиль заливки, без рамки). */
/* bar3d — 3D-брусок: перёд залит стилем заливки, рёбра — тек. цветом;
 * topflag != 0 рисует верхнюю грань. */
void bar3d(int left, int top, int right, int bottom, int depth, int topflag);

/* fillpoly — залитый многоугольник (авто-замыкается); контур тек.
 * цветом, нутро — стилем заливки.  Вогнутые заполняются по выпуклой
 * оболочке строк (min/max X на строку) — упрощение. */
void fillpoly(int numpoints, const int *polypoints);

/* fillellipse — залитый эллипс (полуоси xradius,yradius). */
void fillellipse(int x, int y, int xradius, int yradius);

/* floodfill — заливка области, содержащей (x,y), текущим стилем до
 * границы цвета border.  Заливка сплошная цветом заливки (паттерн в
 * floodfill пока не применяется — упрощение). */
void floodfill(int x, int y, int border);

/* pieslice — залитый сектор круга; sector — эллиптический сектор.
 * Контур (дуга + два радиуса) тек. цветом, нутро — стилем заливки.
 * Для секторов >180° возможен перелив в «выемку» (min/max по строке). */
void pieslice(int x, int y, int stangle, int endangle, int radius);
void sector(int x, int y, int stangle, int endangle,
            int xradius, int yradius);

/* ---- Растровые образы (спрайты) ---------------------------------- *
 * Формат буфера: 2×uint16 (ширина, высота в пикселях) + пиксели
 * построчно (1 байт/пиксель в режиме 256).  Буфер выделяет вызывающий
 * размером imagesize(). */
enum { COPY_PUT = 0, XOR_PUT, OR_PUT, AND_PUT, NOT_PUT };

/* Байт под образ прямоугольника (left,top)-(right,bottom) включительно.
 * ВНИМАНИЕ: результат — 16-бит unsigned; образы ≥64 КБ не поддержаны. */
unsigned imagesize(int left, int top, int right, int bottom);

/* Сохранить прямоугольник экрана в bitmap (размер = imagesize()). */
void getimage(int left, int top, int right, int bottom, void *bitmap);

/* Вывести образ левым-верхним углом в (left,top) операцией op
 * (COPY/XOR/OR/AND/NOT_PUT).  COPY_PUT — через акселератор, с
 * клиппингом по экрану и текущим банком gfx_set_bank (см. <gfx.h>);
 * XOR/OR/AND/NOT — по-пиксельно, без клиппинга. */
void putimage(int left, int top, const void *bitmap, int op);

/* ---- Спрайты (аппаратная прозрачность; docs/sprite-api-design.md) - *
 * Спрайт — тот же буфер getimage-формата; прозрачные точки в данных =
 * байты 0xFF (GFX_TRANSPARENT).  Прозрачность и сохранность фона
 * обеспечивает железо (банк GFX_BANK_SPRITE = 0x5C): прозрачные байты
 * не записываются, ОЗУ-копия фона не трогается — стирание без
 * save-буфера через gfx_heal (см. <gfx.h>).
 * ВАЖНО: фон/сцену рисовать ОБЫЧНЫМ банком (0x50) — то, что нарисовано
 * при активном бите 2 (0x54/0x5C), в ОЗУ-копию не попадает и heal
 * вернёт то, что лежало под ним. */

/* Вывести спрайт в (x,y): банк на время вызова — GFX_BANK_SPRITE,
 * затем прежний.  Клиппится по экрану. */
void putsprite(int x, int y, const void *img);

/* Переместить спрайт: восстановить фон под (oldx,oldy) (gfx_heal по
 * размерам img) и вывести спрайт в (x,y).  Перекрытие старой и новой
 * позиций безопасно (heal читает фон из ОЗУ-копии). */
void movesprite(int oldx, int oldy, int x, int y, const void *img);

/* ---- Текст ------------------------------------------------------- *
 * Шрифт 8×8 (системный).  Рисуется текущим цветом на фоновом. */

void outtextxy(int x, int y, const char *text);  /* в (x,y), CP не меняет */
void outtext(const char *text);                  /* в CP; CP сдвигается вправо */

/* ---- Стиль текста ------------------------------------------------ *
 * Поддержан только DEFAULT_FONT (8×8, растровый); charsize 1..10 —
 * целочисленный масштаб; direction — HORIZ_DIR или VERT_DIR (поворот
 * 90° против часовой).  Фон текста прозрачный (рисуется только цвет). */
enum { DEFAULT_FONT = 0, TRIPLEX_FONT, SMALL_FONT, SANS_SERIF_FONT,
       GOTHIC_FONT };
enum { HORIZ_DIR = 0, VERT_DIR = 1 };

struct textsettingstype {
    int font;
    int direction;
    int charsize;
    int horiz;
    int vert;
};

void settextstyle(int font, int direction, int charsize);
void gettextsettings(struct textsettingstype *textinfo);
int  textwidth(const char *text);
int  textheight(const char *text);

#endif
