#include "etc.h"
#include "font.h"

#include "../include/limine.h"

u32 bg = 0x00000000;
u32 fg = (0x007F1754 << 1);
static size_t row;
static size_t col;
static size_t term_width, term_height;
static size_t fb_width, fb_height;
static volatile u32 *fb_scr;

#define TAB_STOP 4

// buffer at each edge for readability
#define GLYPH_WIDTH (FONT_WIDTH + 2)
#define GLYPH_HEIGHT (FONT_HEIGHT + 2)

void print_init(struct limine_framebuffer *fb) {
  term_width = fb->width / GLYPH_WIDTH;
  term_height = fb->height / GLYPH_HEIGHT;
  fb_width = fb->width;
  fb_height = fb->height;
  fb_scr = fb->address;
}

static void scrollback() {
  memmove((void *)fb_scr, (void *)(fb_scr + fb_width * GLYPH_HEIGHT),
          fb_width * (fb_height - GLYPH_HEIGHT) * sizeof(u32));
  size_t last_row_start = (term_height - 1) * fb_width;
  for (size_t i = 0; i < fb_width; i++) {
    fb_scr[last_row_start + i] = bg;
  }
}

void put(int ch) {
  ch = (unsigned char)ch;

  if (!isprint(ch)) {
    if (ch == '\n') {
      col = 0;
      row++;
    } else if (ch == '\t') {
      size_t target = (col + TAB_STOP) & ~(TAB_STOP - 1);
      if (target >= term_width) {
        col = 0;
        row++;
      } else {
        while (col < target)
          put(' ');
      }
    }
    return;
  }

  if (col >= term_width) {
    col = 0;
    row++;
  }

  if (row >= term_height) {
    scrollback();
    row = term_height - 1;
  }

  size_t idx = ch - ' ';
  u32 glyph = font[idx];

  size_t px = col * GLYPH_WIDTH;
  size_t py = row * GLYPH_HEIGHT;

  for (size_t y = 0; y < GLYPH_HEIGHT; y++) {
    for (size_t x = 0; x < GLYPH_WIDTH; x++) {
      size_t off = (py + y) * fb_width + (px + x);
      bool padding =
          (y == 0 || y == GLYPH_HEIGHT - 1 || x == 0 || x == GLYPH_WIDTH - 1);
      fb_scr[off] = ((~glyph & 1) | padding) ? ((fb_scr[off] == fg) ? bg : fb_scr[off]) : fg;
      glyph >>= !padding;
    }
  }

  col++;
}

void print(const char *msg) {
  while (*msg) {
    put(*(msg++));
  }
}

void printn(const char *msg, size_t len) {
  while (len-- > 0) {
    put(*(msg++));
  }
}

static u8 digits[] = "0123456789ABCDEF";

void print8(u8 n) {
  put(digits[n >> 4]);
  put(digits[n & 0xf]);
}

void print16(u16 n) {
  print8(n >> 8);
  print8(n & 0xFF);
}

void print32(u32 n) {
  print16(n >> 16);
  print16(n & 0xFFFF);
}

void print64(u64 n) {
  print32(n >> 32);
  print32(n & 0xFFFFFFFF);
}

void unput(void) {
    if (col > 0) {
        col--;
    } else if (row > 0) {
        row--;
        col = term_width - 1;
    } else {
        return;  /* already at (0,0), nothing to erase */
    }

    size_t px = col * GLYPH_WIDTH;
    size_t py = row * GLYPH_HEIGHT;
    for (size_t y = 0; y < GLYPH_HEIGHT; y++)
        for (size_t x = 0; x < GLYPH_WIDTH; x++)
            fb_scr[(py + y) * fb_width + (px + x)] = bg;
}
