/*
 *  Copyright 1994-2022 Olivier Girondel
 *
 *  This file is part of lebiniou.
 *
 *  lebiniou is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  lebiniou is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with lebiniou. If not, see <http://www.gnu.org/licenses/>.
 */

#include <caca.h>

#include "context.h"

uint32_t options = BO_NONE;
uint32_t version = 0;


#define STEP      (1)
#define SWIDTH    (WIDTH/STEP)
#define SHEIGHT   (HEIGHT/STEP)
#define SBUFFSIZE (SWIDTH*SHEIGHT)

static caca_canvas_t *cv = NULL;
static caca_display_t *dp = NULL;
static caca_dither_t *dither = NULL;

static Pixel_t *screen = NULL;
static uint32_t red[256], green[256], blue[256], alpha[256];

static char *export_format = NULL;
#define DEFAULT_EXPORT_PREFIX "/var/www/biniou_caca"
static char *export_prefix = NULL;


static void
display_fonts(void)
{
  char **fonts;
  char **f;

  fonts = (char **)caca_get_font_list();
  printf("[i] caca_get_font_list:\n");
  for (f = fonts; NULL != *f; f++) {
    printf("[i] font: %s\n", *f);
  }
}


int8_t
create(Context_t *ctx)
{
  screen = xcalloc(SBUFFSIZE, sizeof(Pixel_t));

  display_fonts();

  cv = caca_create_canvas(SWIDTH, SHEIGHT);
  if (NULL == cv) {
    xerror("caca_create_canvas\n");
  }

  dp = caca_create_display(cv);
  if (NULL == dp) {
    xerror("caca_create_display\n");
  } else {
    caca_set_display_title(dp, "cacaBiniou");
    caca_set_mouse(dp, 0);
  }

  dither = caca_create_dither(8, SWIDTH, SHEIGHT, SWIDTH, 0, 0, 0, 0);
  if (NULL == dither) {
    xerror("caca_create_dither\n");
  }

  /* cucul_set_dither_charset(dither, "shades"); */
  /* cucul_set_dither_gamma(dither, 0.5); */
  /* cucul_set_dither_contrast(dither, 0.5); */

  export_format = getenv("LEBINIOU_CACA_EXPORT_FORMAT");
  if (NULL != export_format) {
    export_prefix = getenv("LEBINIOU_CACA_EXPORT_PREFIX");
    if (NULL == export_prefix) {
      printf("[!] LEBINIOU_CACA_EXPORT_PREFIX is not set, using default\n");
      export_prefix = DEFAULT_EXPORT_PREFIX;
      printf("[i] exporting caca display with prefix '%s'\n", export_prefix);
    }
  }

  return 1;
}


void
destroy(Context_t *ctx)
{
  caca_free_dither(dither);
  caca_free_display(dp);
  caca_free_canvas(cv);
  xfree(screen);
}


static void
get_events(Context_t *ctx, caca_display_t *dp)
{
  caca_event_t ev;

  while (caca_get_event(dp, CACA_EVENT_QUIT, &ev, 0)) {

    if (caca_get_event_type(&ev) == CACA_EVENT_QUIT) {
      /* FIXME add an event to quit or is this ok ? */
      ctx->running = 0;
      return;
    }

    /* TODO handle key pressed... */
  }
}


static void
export(caca_canvas_t *cv)
{
  char fname[2][MAXLEN]; /* 0: temp file, 1: out filename */
  size_t size = 0;
  void *data = NULL;

  memset(fname[0], '\0', MAXLEN*sizeof(char));
  memset(fname[1], '\0', MAXLEN*sizeof(char));

  snprintf(fname[0], (MAXLEN-1)*sizeof(char), "%s.%s.tmp", export_prefix, export_format);
  snprintf(fname[1], (MAXLEN-1)*sizeof(char), "%s.%s", export_prefix, export_format);

  data = caca_export_canvas_to_memory(cv, export_format, &size);

  if (NULL != data) {
    FILE *stream = NULL;
    size_t res;

    // printf("[i] exporting %zu bytes from %p\n", size, data);
    // printf("[i] tmp_file= %s final= %s\n", fname[0], fname[1]);

    stream = fopen(fname[0], "w");
    if (NULL == stream) {
      xperror("fopen");
    }

    res = fwrite(data, sizeof(char), size, stream);
    if (res != size) {
      xerror("fwrite\n");
    }

    xfree(data);

    if (fclose(stream) != 0) {
      xperror("fclose");
    }

    if (rename(fname[0], fname[1]) != 0) {
      xperror("rename");
    }
  } else {
    printf("[!] caca_export_canvas to format %s failed\n", export_format);
  }
}


void
run(Context_t *ctx)
{
  uint16_t i, j;
  rgba_t *colors = ctx->cf->cur->colors;
  Buffer8_t const *src = active_buffer(ctx);
  Pixel_t *p = screen;

  for (i = 0; i < 256; i++) {
    red[i]   = (uint32_t)(colors[i].col.r / 255.0 * 0xfff);
    green[i] = (uint32_t)(colors[i].col.g / 255.0 * 0xfff);
    blue[i]  = (uint32_t)(colors[i].col.b / 255.0 * 0xfff);
    alpha[i] = (uint32_t)(colors[i].col.a / 255.0 * 0xfff);
  }
  caca_set_dither_palette(dither, red, green, blue, alpha);

  caca_clear_canvas(cv);

  for (j = 0; j < HEIGHT; j += STEP)
    for (i = 0; i < WIDTH; i += STEP) {
      *p++ = get_pixel_nc(src, i, MAXY-j);
    }
  caca_dither_bitmap(cv, 0, 0,
                      caca_get_canvas_width(cv),
                      caca_get_canvas_height(cv),
                      dither, screen);

  caca_refresh_display(dp);

  if (NULL != export_format) {
    export(cv);
  }

  get_events(ctx, dp);
}
