/*
 *  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 "constants.h" /* XXX just to get MAXLEN */
#include "cmap_8bits.h"


Cmap8_t *
Cmap8_new(void)
{
  Cmap8_t *cmap = xcalloc(1, sizeof(Cmap8_t));

  cmap->name = strdup("grey");
  cmap->id = -1;

  for (uint16_t i = 0; i < 256; i++)
    cmap->colors[i].col.r =
      cmap->colors[i].col.g =
        cmap->colors[i].col.b =
          cmap->colors[i].col.a = i;

  cmap->min = 0;
  cmap->max = 255;

  return cmap;
}


void
Cmap8_delete(Cmap8_t *cmap)
{
  xfree(cmap->name);
  xfree(cmap->filename);
  xfree(cmap);
}


int
Cmap8_load(Cmap8_t *cmap, const char *filename)
{
  FILE *stream;
  char ccmap[MAXLEN];

  /* check if we have a binary version of the colormap */
  /* of course this is ugly, we should stat() the file, or whatever */
  /* well... */
  memset(ccmap, '\0', MAXLEN*sizeof(char));
  snprintf(ccmap, (MAXLEN-1)*sizeof(char), "%s.bin", filename);
  if (Cmap8_load_binary(cmap, ccmap) == 0) {
    /* printf("[i] Succeeded loading binary version of %s from %s\n", filename, ccmap); */
    cmap->filename = strdup(filename);
    return 0;
  }

  stream = fopen(filename, "r");
  if (NULL == stream) {
    xperror("fopen");
  }

  /* TODO: fscanf() version */
  for (uint16_t i = 0; i < 256; ) {
    int r = -1, g = -1, b = -1;
    int ret;
    char prout[1024];
    char *strret = NULL;

    /* try to read an RGB */
    strret = fgets(prout, 1024, stream);
    if (NULL == strret) {
      fclose(stream);
      return -1;
    }

    ret = sscanf(prout, "%d %d %d", &r, &g, &b);

    if (EOF == ret) {
      fclose(stream);
      return -1;
    }

    if (ret == 3) {
      /* success */
      cmap->colors[i].col.r = r;
      cmap->colors[i].col.g = g;
      cmap->colors[i].col.b = b;
      cmap->colors[i].col.a = 255; /* alpha channel */
      i++;
    }
  }

  fclose(stream);
  cmap->filename = strdup(filename);

  return 0;
}


void
Cmap8_copy(const Cmap8_t *from, Cmap8_t *to)
{
  assert(NULL != from);
  assert(NULL != to);

  xfree(to->name);
  assert(NULL != from->name);
  to->name = strdup(from->name);
  to->id = from->id;

  for (uint16_t i = 0; i < 256; i++) {
    to->colors[i] = from->colors[i];
  }

  to->min = from->min;
  to->max = from->max;
}


void
Cmap8_findMinMax(Cmap8_t *cmap)
{
  uint16_t min = 256;
  int16_t max = -1;

  for (uint16_t i = 0; i < 256; i++) {
    uint16_t  sum;
    sum =  cmap->colors[i].col.r * 0.299;
    sum += cmap->colors[i].col.g * 0.587;
    sum += cmap->colors[i].col.b * 0.114;

    if (sum < min) {
      min = sum;
      cmap->min = i;
    }

    /* >= is a trick so that we get as high
     * as possible in the indices */
    if (sum >= max) {
      max = sum;
      cmap->max = i;
    }
  }
}


int
Cmap8_load_binary(Cmap8_t *cmap, const char *filename)
{
  int fd;
  int r;
  size_t res;

  fd = open(filename, O_RDONLY);
  if (fd == -1) {
    return -1;
  }

#define BTR (256*sizeof(rgba_t)) /* Bytes To Read */
  res = read(fd, (void *)cmap->colors, BTR);
  if (res != BTR) {
    printf("[!] short read in Cmap8_load_binary '%s'\n", filename);
    r = close(fd);
    if (r == -1) {
      xperror("close");
    }
    return -1;
  }

  r = close(fd);
  if (r == -1) {
    xperror("close");
  }

  cmap->compressed = 1;

  return 0;
}


void
Cmap8_shift_left(Cmap8_t *cmap)
{
  rgba_t col0 = cmap->colors[0];

  for (uint8_t i = 0; i < 255; i++) {
    cmap->colors[i] = cmap->colors[i+1];
  }
  cmap->colors[255] = col0;
}


// #define INTEGER_ARRAY // no need for now
json_t *
Cmap8_to_json(const Cmap8_t *cmap)
{
#ifdef INTEGER_ARRAY
  json_t *rgb = json_array();
#endif
  json_t *res = json_pack("{ss}", "name", cmap->name);

  if (!getenv("NO_PALETTE")) {
    json_t *w3c = json_array();

    for (uint16_t i = 0; i < 256; i++) {
#ifdef INTEGER_ARRAY
      json_t *t = json_array();
#endif
      gchar *hex;

#ifdef INTEGER_ARRAY
      json_array_append_new(t, json_integer(cmap->colors[i].col.r));
      json_array_append_new(t, json_integer(cmap->colors[i].col.g));
      json_array_append_new(t, json_integer(cmap->colors[i].col.b));
      json_array_append_new(rgb, t);
#endif
      hex = g_strdup_printf("#%02x%02x%02x",
                            cmap->colors[i].col.r, cmap->colors[i].col.g, cmap->colors[i].col.b);
      json_array_append_new(w3c, json_string(hex));
      g_free(hex);
    }
#ifdef INTEGER_ARRAY
    json_object_set_new(res, "rgb", rgb);
#endif
    json_object_set_new(res, "w3c", w3c);
  }

  return res;
}
