/*
 * mdview2_raw.c — RAW-просмотр исходного текста (без markdown-форматирования).
 *
 * Работает по АКТИВНОМУ буферу документа через fb() (8-бит = исходные байты,
 * UTF-8 = декодированный в CP866 буфер), ремап ≥0x80 (CP1251/KOI8) — на
 * отрисовке. Индекс/кэш markdown не используются: позиция — байт-offset,
 * 1 байт = 1 ячейка, \t показываем пробелом. Два режима:
 *   VIEW_RAW_WRAP    — длинные строки переносятся кратно 80 (рвём слова);
 *   VIEW_RAW_HSCROLL — строка в один ряд, горизонтальный скролл.
 *
 * Память: 0 доп. EMM; near-буфер строки + переиспользование g_scratch_phys.
 */

#include <stdint.h>
#include <stdio.h>
#include <conio.h>
#include <bios/text.h>
#include "mdview2.h"

#if WITH_RAW   /* весь модуль — опционален (mdview_conf.h); при 0 пустой объектник */

static uint32_t raw_top;   /* байт-offset верхней строки экрана */
static uint16_t raw_hpan;   /* гориз. сдвиг (только HSCROLL) */

/* ---- примитивы навигации по физическим строкам / переносам -------- */

static uint32_t raw_line_start(uint32_t off)
{
    while (off && fb(off - 1) != '\n') off--;
    return off;
}

/* WRAP: начало следующего ряда от off (\n завершает строку; иначе +80). */
static uint32_t raw_next_wrap(uint32_t off)
{
    uint32_t e = off, lim = off + SCREEN_W;
    while (e < file_size) {
        if (fb(e) == '\n') return e + 1;
        e++;
        if (e >= lim) {                       /* прошли 80 без \n */
            if (e < file_size && fb(e) == '\n') return e + 1;  /* ровно 80 + \n */
            return e;                         /* длинная строка → продолжение */
        }
    }
    return e;                                 /* EOF */
}

static uint32_t raw_prev_wrap(uint32_t off)
{
    if (off == 0) return 0;
    uint32_t pls = raw_line_start(off - 1);    /* off-1 — это \n пред. строки */
    while (pls + SCREEN_W < (off - 1)) pls += SCREEN_W;
    return pls;
}

/* HSCROLL: начало следующей/предыдущей физической строки. */
static uint32_t raw_next_line(uint32_t off)
{
    while (off < file_size && fb(off) != '\n') off++;
    return (off < file_size) ? off + 1 : off;
}

static uint32_t raw_prev_line(uint32_t off)
{
    if (off == 0) return 0;
    return raw_line_start(off - 1);
}

/* ---- отрисовка ряда (общий near-буфер → g_scratch_phys → win_rest) - */

static uint8_t raw_buf[SCREEN_W + 1];   /* только символы (атрибут единый, задаётся при выводе) */

/* В RAW атрибут единый (ATTR_TEXT): буфер — только 80 символов, без пар
 * (char,attr). Кладём один байт-глиф (\t→пробел, ремап ≥0x80). */
static void raw_cell(uint8_t i, uint8_t ch)
{
    if (ch >= 0x80 && g_remap) ch = g_remap[ch - 0x80];
    raw_buf[i] = ch;
}
/* Вывод готового 80-символьного буфера в строку экрана одним BIOS-вызовом
 * (char-буфер + единый attr — без WINREST/scratch-страницы). */
static void raw_flush(uint8_t row)
{
    bios_set_place(row, 0);
    // bios_writeattr((const char *)raw_buf, SCREEN_W, ATTR_TEXT);
    // bios_writeattr_until((const char *)raw_buf, SCREEN_W, ATTR_TEXT, 0);
    bios_write_until((const char *)raw_buf, SCREEN_W, 0);
}

static uint32_t raw_draw_wrap_row(uint32_t off, uint8_t row)
{
    uint8_t  i = 0;
    uint32_t e = off, lim = off + SCREEN_W;
    while (i < SCREEN_W && e < file_size) {
        char c = fb(e);
        if (c == '\n') { 
            raw_cell(i, 0);
            break;
        }
        raw_cell(i, (uint8_t)(c == '\t' ? ' ' : c));
        i++; e++;
    }
    raw_flush(row);
    return raw_next_wrap(off);
}

static uint32_t raw_draw_hscroll_row(uint32_t off, uint8_t row)
{
    uint32_t e = off;
    while (e < file_size && fb(e) != '\n') e++;     /* e = конец строки */
    uint16_t len = e - off;

    uint8_t  i = 0;
    uint32_t p = off + raw_hpan;
    while (i < SCREEN_W && p < e) {
        char c = fb(p);
        raw_cell(i, (uint8_t)(c == '\t' ? ' ' : c));
        i++; p++;
    }
    // raw_buf[i] = 0;

    if (raw_hpan > 0)
        raw_buf[0] = '<';
    if (len > (uint16_t)raw_hpan + SCREEN_W)
        raw_buf[SCREEN_W - 1] = '>';
    if (i < SCREEN_W)
        raw_buf[i] = 0;
    raw_flush(row);
    return (e < file_size) ? e + 1 : e;
}

/* Рисует один ряд от off (по режиму) и возвращает offset следующего ряда. */
static uint32_t raw_draw_one(uint32_t off, uint8_t row)
{
    if (off >= file_size) { fill_row(row, ATTR_TEXT); return off; }
    return (g_view == VIEW_RAW_WRAP) ? raw_draw_wrap_row(off, row)
                                     : raw_draw_hscroll_row(off, row);
}

void raw_draw(void)
{
    uint32_t off = raw_top;
    for (uint8_t r = 0; r < VIEW_H; r++)
        off = raw_draw_one(off, (uint8_t)(VIEW_TOP_ROW + r));
}

/* Прокрутка на одну строку: аппаратный scroll окна документа + отрисовка
 * ОДНОЙ новой строки (как в MD-режиме). Смещение нижнего ряда вычисляется
 * проходом VIEW_H рядов от raw_top — без отдельного инкрементального счётчика,
 * который рассинхронизировался, когда контент кончается в середине экрана
 * (короче окна после wrap→unwrap у конца файла). */
static uint32_t raw_next(uint32_t off)
{ 
    return (g_view == VIEW_RAW_WRAP) ? raw_next_wrap(off) : raw_next_line(off); 
}
static uint32_t raw_prev(uint32_t off)
{ 
    return (g_view == VIEW_RAW_WRAP) ? raw_prev_wrap(off) : raw_prev_line(off); 
}

/* ---- статус-бар: % по байтам (отрисовка — render_raw_status_numbers
 *      в mdview2_status.c, читает позицию через этот raw_pct) ---- */

/* Процент по байтам без 32-битного деления: масштабируем offset'ы (>>8 →
 * ≤1024), дальше 16-битный pct16 (без __divulong). raw_top приватен модулю,
 * поэтому % считаем здесь и отдаём готовым числом в статус-модуль. */
uint8_t calc_raw_pct(void)
{
    uint16_t a = (uint16_t)(raw_top    >> 8);
    uint16_t b = (uint16_t)(file_size  >> 8);
    return pct16(a, b);                  /* den==0 (файл < 1 КБ) → pct16 вернёт 0 */
}

/* ---- посев позиции / пересев при смене активного буфера ----------- */

uint32_t raw_pos(void)
{
    return raw_top;
}

/* Поставить RAW на начало строки, содержащей байт off (позиционирование
 * при F2 MD→RAW и F8-переносе позиции между наборами). */
void raw_reanchor(uint32_t off)
{
    if (off > file_size) off = file_size;
    raw_top  = raw_line_start(off);
    raw_hpan = 0;
}

void raw_seed_from(uint16_t md_top_line)
{
    raw_reanchor(seg_off(md_top_line));
}

/* Сброс в начало (клавиша Home). */
static void raw_home(void)
{
    raw_top  = 0;
    raw_hpan = 0;
}

void raw_end(void)
{
    raw_top = file_size;                            /* отмотать VIEW_H рядов назад */
    for (uint8_t k = 0; k < VIEW_H; k++) 
        raw_top = raw_prev(raw_top);
    raw_hpan = 0;
}

void raw_screen_init(void) {
    bios_clearwin(1, 0, 30, 80, ATTR_TEXT);
}

/* Нормализация позиции при смене под-режима RAW (F3 Wrap/Unwrap): выровнять
 * raw_top на начало физической строки и снять горизонтальный сдвиг. */
void raw_renorm(void)
{
    raw_top  = raw_line_start(raw_top);
    raw_hpan = 0;
}

void raw_scroll_down(uint8_t delta) {
    uint32_t off = raw_top;
    for (uint8_t r = 0; r < VIEW_H; r++) {
        if (off >= file_size) 
            return;               /* экран не заполнен контентом */
        off = raw_next(off);
    }
    if (off >= file_size) 
        return;                   /* контент ровно по экран, ниже пусто */

    if(delta ==1) {
        scroll(0, VIEW_TOP_ROW, SCREEN_W, VIEW_H, 1, 0);            /* содержимое вверх */
        off = raw_draw_one(off, (uint8_t)(VIEW_TOP_ROW + VIEW_H - 1));    /* новый нижний ряд */
        raw_top = raw_next(raw_top);
    } else {
        for (uint8_t k = 0; k < delta; k++) {
            uint32_t n2 = raw_next(raw_top);
            if (n2 >= file_size) 
                break;
            raw_top = n2;
        }
        raw_draw();                                        /* PgUp/PgDn/Home/End — полный экран */
    }
}

void raw_scroll_up(uint8_t delta) {
    if (raw_top == 0) 
        return;

    if (delta == 1) {
        raw_top = raw_prev(raw_top);
        scroll(0, VIEW_TOP_ROW, SCREEN_W, VIEW_H, 2, 0);            /* содержимое вниз */
        (void)raw_draw_one(raw_top, VIEW_TOP_ROW);
    } else {
        for (uint8_t k = 0; k < delta; k++) 
            raw_top = raw_prev(raw_top);
        raw_draw();                                        /* PgUp/PgDn/Home/End — полный экран */
    }
}

/* ---- навигация (вызывается из главного цикла при g_view != VIEW_MD) - */

uint8_t raw_key(uint8_t scan)
{
    switch (scan) {
        case KEY_DOWN:  
            raw_scroll_down(1); 
            break;
        case KEY_UP:    
            raw_scroll_up(1);   
            break;
        case KEY_PGDN:
            raw_scroll_down(VIEW_H); 
            break;
        case KEY_PGUP:
            raw_scroll_up(VIEW_H); 
            break;
        case KEY_HOME:
            raw_home();
            raw_draw(); 
            break;
        case KEY_END:
            raw_end();
            raw_draw(); 
            break;
        case KEY_LEFT:
            if (g_view == VIEW_RAW_HSCROLL && raw_hpan) {
                raw_hpan = (raw_hpan >= HPAN_STEP) ? (uint16_t)(raw_hpan - HPAN_STEP) : 0;
                raw_draw(); 
            }
            break;
        case KEY_RIGHT:
            if (g_view == VIEW_RAW_HSCROLL && raw_hpan < 248u) {
                raw_hpan = (uint16_t)(raw_hpan + HPAN_STEP);
                raw_draw(); 
            }
            break;
        default:
            return 0;                                  /* не наша клавиша */
    }

    // render_raw_status_numbers();
    render_percent_progress(calc_raw_pct());
    return 1;
}

#endif /* WITH_RAW */
