#include "mr4th/mr4th_base.h"
#include "mr4th/mr4th_stdio.h"
#include "mr4th/mr4th_prng.h"
#include <ncurses.h>

#include "bfr.main.h"

#include "mr4th/mr4th_base.c"
#include "mr4th/mr4th_stdio.c"
#include "mr4th/mr4th_prng.c"

MR4TH_SYMBOL NCRS_Bfr
ncrs_bfr_alloc(Arena *arena, U32 w, U32 h){
  NCRS_Bfr result = { w, h };
  result.chr = push_array(arena, U8, w*h);
  result.color = push_array(arena, U8, w*h);
  result.clip.x1 = w;
  result.clip.y1 = h;
  return(result);
}

MR4TH_SYMBOL void
ncrs_bfr_clip_clear(NCRS_Bfr *bfr){
  bfr->clip.x0 = 0;
  bfr->clip.y0 = 0;
  bfr->clip.x1 = bfr->w;
  bfr->clip.y1 = bfr->h;
}

MR4TH_SYMBOL void
ncrs_bfr_clip(NCRS_Bfr *bfr, RectS32 rect){
  bfr->clip.x0 = ClampBot(0, rect.x0);
  bfr->clip.y0 = ClampBot(0, rect.y0);
  bfr->clip.x1 = ClampTop(rect.x1, bfr->w);
  bfr->clip.y1 = ClampTop(rect.y1, bfr->h);
}

MR4TH_SYMBOL void
ncrs_bfr_fill_rect(NCRS_Bfr *bfr, RectS32 rect, U8 chr, U8 color){
  S32 x0 = ClampBot(rect.x0, bfr->clip.x0);
  S32 x1 = ClampTop(rect.x1, bfr->clip.x1);
  S32 y0 = ClampBot(rect.y0, bfr->clip.y0);
  S32 y1 = ClampTop(rect.y1, bfr->clip.y1);
  
  for (S32 iy = y0; iy < y1; iy += 1){
    S32 iyb = iy*bfr->w;
    for (S32 ix = x0; ix < x1; ix += 1){
      bfr->chr[iyb + ix] = chr;
      bfr->color[iyb + ix] = color;
    }
  }
}

MR4TH_SYMBOL void
ncrs_bfr_outline_rect(NCRS_Bfr *bfr, RectS32 rect, U8 chr, U8 color){
  S32 x0 = ClampBot(rect.x0, bfr->clip.x0);
  S32 x1 = ClampTop(rect.x1, bfr->clip.x1);
  S32 y0 = ClampBot(rect.y0, bfr->clip.y0);
  S32 y1 = ClampTop(rect.y1, bfr->clip.y1);
  
  // top
  if (bfr->clip.y0 <= rect.y0 && rect.y0 < bfr->clip.y1){
    S32 iyb = rect.y0*bfr->w;
    for (S32 ix = x0; ix < x1; ix += 1){
      bfr->chr[iyb + ix] = chr;
      bfr->color[iyb + ix] = color;
    }
  }
  
  // bottom
  if (bfr->clip.y0 < rect.y1 && rect.y1 <= bfr->clip.y1){
    S32 iyb = (rect.y1 - 1)*bfr->w;
    for (S32 ix = x0; ix < x1; ix += 1){
      bfr->chr[iyb + ix] = chr;
      bfr->color[iyb + ix] = color;
    }
  }
  
  // left
  if (bfr->clip.x0 <= rect.x0 && rect.x0 < bfr->clip.x1){
    S32 ix = rect.x0;
    for (S32 iy = y0; iy < y1; iy += 1){
      S32 iyb = iy*bfr->w;
      bfr->chr[iyb + ix] = chr;
      bfr->color[iyb + ix] = color;
    }
  }
  
  // right
  if (bfr->clip.x0 < rect.x1 && rect.x1 <= bfr->clip.x1){
    S32 ix = rect.x1 - 1;
    for (S32 iy = y0; iy < y1; iy += 1){
      S32 iyb = iy*bfr->w;
      bfr->chr[iyb + ix] = chr;
      bfr->color[iyb + ix] = color;
    }
  }
  
}

MR4TH_SYMBOL void
ncrs_bfr_write(NCRS_Bfr *bfr, S32 x, S32 y, String8 str, U8 color){
  if (str.size > 0 &&
      bfr->clip.x0 < x + str.size && x < bfr->clip.x1 &&
      bfr->clip.y0 <= y && y < bfr->clip.y1){
    S32 iyb = y*bfr->w;
    S32 x1 = ClampTop(x + str.size, bfr->clip.x1);
    S32 xskip = ClampBot(0, bfr->clip.x0 - x);
    for (U32 xi = x + xskip, i = xskip; xi < x1; xi += 1, i += 1){
      bfr->chr[iyb + xi] = str.str[i];
      bfr->color[iyb + xi] = color;
    }
  }
}

MR4TH_SYMBOL void
ncrs_bfr_point(NCRS_Bfr *bfr, S32 x, S32 y, U8 chr, U8 color){
  if (bfr->clip.x0 <= x && x < bfr->clip.x1 &&
      bfr->clip.y0 <= y && y < bfr->clip.y1){
    bfr->chr[y*bfr->w + x] = chr;
    bfr->color[y*bfr->w + x] = color;
  }
}

MR4TH_SYMBOL void
ncrs_bfr_to_window(NCRS_Bfr *bfr, WINDOW *window){
  S32 w = 0;
  S32 h = 0;
  getmaxyx(window, h, w);
  
  S32 x1 = ClampTop(bfr->w, w);
  S32 y1 = ClampTop(bfr->h, h);
  
  for (U32 iy = 0; iy < y1; iy += 1){
    S32 iyb = iy*bfr->w;
    wmove(window, iy, 0);
    for (U32 ix = 0; ix < x1; ix += 1){
      S16 color = (S16)bfr->color[iyb + ix];
      if (color != 0){
        attron(COLOR_PAIR(color));
      }
      waddch(window, bfr->chr[iyb + ix]);
      attroff(COLOR_PAIR(color));
    }
  }
  
  wrefresh(window);
}

int main(){
  ArenaTemp scratch = arena_get_scratch(0, 0);
  
  // prng
  PRNG prng = {0};
  {
    PRNG_Seed seed;
    os_get_entropy(&seed, sizeof(seed));
    prng = prng_from_seed(&seed);
  }
  
  // initialize ncurses
  initscr();
  
  if (!has_colors()){
    goto no_color;
  }
  start_color();
  
  noecho();
  cbreak();
  curs_set(0);
  
  // initialize ncurses colors
  {
#define X(F,B) init_pair(NCRS_ColorPair_##F##_##B, COLOR_##F, COLOR_##B);
    NCRS_ColorPair_XList(X)
#undef X
  }
  
  // virtual buffer
  NCRS_Bfr bfr = ncrs_bfr_alloc(scratch.arena, 160, 40);
  
  // background decorations
  static U8 chr_array[] = {
    '#', '%', '+', '~',
    '*', '|', '-', '$',
    '<', '\\', '/', '>',
    ',', ',', '.', '.',
    ' ', ' ', ' ', ' ',
    ' ', ' ', ' ', ' ',
    ' ', ' ', ' ', ' ',
    ' ', ' ', ' ', ' ',
    ' ', ' ', ' ', ' ',
    ' ', ' ', ' ', ' ',
  };
  
  static U8 color_array[] = {
    NCRS_ColorPair_RED_BLACK, NCRS_ColorPair_GREEN_BLACK, NCRS_ColorPair_BLUE_BLACK,
    NCRS_ColorPair_YELLOW_BLACK, NCRS_ColorPair_MAGENTA_BLACK, NCRS_ColorPair_CYAN_BLACK,
  };
  
  // program
  for (;;){
    // render to bfr
    ncrs_bfr_fill_rect(&bfr, (RectS32){0, 0, 160, 40}, ' ', 0);
    ncrs_bfr_outline_rect(&bfr, (RectS32){0, 0, 160, 40},
                          '#', NCRS_ColorPair_WHITE_BLACK);
    
    ncrs_bfr_clip(&bfr, (RectS32){1, 1, 159, 39});
    for (S32 iy = 0; iy < bfr.h; iy += 1){
      for (S32 ix = 0; ix < bfr.w; ix += 1){
        U8 chr = chr_array[prng_u32_bounded(&prng, ArrayCount(chr_array))];
        U8 color = color_array[prng_u32_bounded(&prng, ArrayCount(color_array))];
        ncrs_bfr_point(&bfr, ix, iy, chr, color);
      }
    }
    ncrs_bfr_clip_clear(&bfr);
    
    ncrs_bfr_fill_rect(&bfr,    (RectS32){58, 8, 88, 19}, ' ', NCRS_ColorPair_WHITE_BLACK);
    ncrs_bfr_outline_rect(&bfr, (RectS32){58, 8, 88, 19}, '#', NCRS_ColorPair_WHITE_BLACK);
    ncrs_bfr_write(&bfr, 60, 10, str8_lit("Continue"), NCRS_ColorPair_BLACK_WHITE);
    ncrs_bfr_write(&bfr, 60, 12, str8_lit("New Game (Abandon Current)"), NCRS_ColorPair_WHITE_BLACK);
    ncrs_bfr_write(&bfr, 60, 14, str8_lit("Journal"), NCRS_ColorPair_WHITE_BLACK);
    ncrs_bfr_write(&bfr, 60, 16, str8_lit("Quit"), NCRS_ColorPair_WHITE_BLACK);
    
    // bfr -> screen
    ncrs_bfr_to_window(&bfr, stdscr);
    
    int c = getch();
  }
  
  // end ncurses
  endwin();
  
  if (0){ no_color:
    endwin();
    printf("no ncurses color support\n");
  }
  
  arena_release_scratch(&scratch);
  return(0);
}
