/*
 *  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 "globals.h"
#include "colormaps.h"
#include "brandom.h"


Colormaps_t *colormaps = NULL;


static int
Colormaps_compare(const void *_a, const void *_b)
{
  Cmap8_t **a = (Cmap8_t **)_a;
  Cmap8_t **b = (Cmap8_t **)_b;

  assert(NULL != *a);
  assert(NULL != *b);
  assert(NULL != (*a)->name);
  assert(NULL != (*b)->name);

  return strcasecmp((*a)->name, (*b)->name);
}


void
Colormaps_new(const char *directoryname)
{
  DIR *dir;
  struct dirent *entry;
  GSList *tmp = NULL;
  uint16_t size = 0;
  GSList *t;

  if (NULL == directoryname) {
    goto error;
  }

  dir = opendir(directoryname);
  if (NULL == dir) {
    fprintf(stderr, "[!] Error while reading colormaps directory content: %s\n",
            strerror(errno));
    goto error;
  }

  while (NULL != (entry = readdir(dir))) {
    uint32_t hash;
    Cmap8_t *map;
    gchar *filename;
    const char *sentry = entry->d_name;

    if (sentry[0] == '.') {
      continue;
    }

    /* we only look for filenames that end with
     * ".map" (fractint colormaps)
     * or ".gpl" (GIMP colormaps)
     */
    if (NULL == (filename = strrchr(sentry, '.')))
      /* does not have a trailing '.' -> next one */
    {
      continue;
    }

    if (strncmp(filename, ".map", 4*sizeof(char)) && strncmp(filename, ".gpl", 4*sizeof(char))) {
      continue;
    }

    map = Cmap8_new();
    hash = FNV_hash(sentry);
    filename = g_strdup_printf("%s/%s", directoryname, sentry);

    if (Cmap8_load(map, filename) != 0) {
      Cmap8_delete(map);
      g_free(filename);
      continue;
    } else {
      xfree(map->name);
      map->name = strdup(sentry);
      map->id = hash;
      g_free(filename);

      /* we reuse char *filename here */
      if (NULL != (filename = strrchr(map->name, '.'))) {
        *filename = '\0';  /* spr0tch */
      }

      Cmap8_findMinMax(map);
    }

    tmp = g_slist_prepend(tmp, (gpointer)map);
    size++;
  }
  if (closedir(dir) == -1) {
    xperror("closedir");
  }

  colormaps = xcalloc(1, sizeof(Colormaps_t));
  if (libbiniou_verbose) {
    printf("[c] Loaded %d colormaps\n", size);
  }

  if (size) {
    uint16_t i;

    colormaps->cmaps = xcalloc(size, sizeof(Cmap8_t *));
    for (i = 0, t = tmp; NULL != t; t = g_slist_next(t), i++) {
      colormaps->cmaps[i] = (Cmap8_t *)t->data;
    }
    g_slist_free(tmp);
    colormaps->size = size;

    qsort((void *)colormaps->cmaps, (size_t)colormaps->size,
          (size_t)sizeof(Cmap8_t *), &Colormaps_compare);

    return;
  }

error:
  colormaps = xcalloc(1, sizeof(Colormaps_t));
  colormaps->cmaps = xcalloc(1, sizeof(Cmap8_t *));
  colormaps->size = 1;
  colormaps->cmaps[0] = Cmap8_new();
}


void
Colormaps_delete(void)
{
  if (NULL != colormaps) {
    uint16_t i;

    for (i = 0; i < colormaps->size; i++) {
      Cmap8_delete(colormaps->cmaps[i]);
    }
    xfree(colormaps->cmaps);
    xfree(colormaps);
  }
}


const char *
Colormaps_name(const uint32_t id)
{
  uint16_t i;

  assert(NULL != colormaps);

  for (i = 0; i < colormaps->size; i++)
    if (colormaps->cmaps[i]->id == id) {
      return colormaps->cmaps[i]->name;
    }

  if (id == 0) {
    return colormaps->cmaps[0]->name;
  }

  xerror("Colormaps_name: id %li not found\n", id);

  return NULL; /* NOTREACHED */
}


int32_t
Colormaps_index(const uint32_t id)
{
  uint16_t i;

  assert(NULL != colormaps);

  for (i = 0; i < colormaps->size; i++)
    if (colormaps->cmaps[i]->id == id) {
      return i;
    }

  xerror("Colormaps_index: id %li not found\n", id);

  return -1; /* NOTREACHED */
}


int32_t
Colormaps_find(const char *name)
{
  uint16_t i;

  assert(NULL != colormaps); /* should have at least a grayscale colormap */

  for (i = 0; i < colormaps->size; i++)
    if (is_equal(colormaps->cmaps[i]->name, name)) {
      return colormaps->cmaps[i]->id;
    }
  fprintf(stderr, "[!] Colormap '%s' not found\n", name);

  return colormaps->cmaps[0]->id; /* Use the first colormap by default */
}


uint32_t
Colormaps_random_id(void)
{
  uint16_t idx = 0;

  assert(NULL != colormaps);
  assert(colormaps->size); // There is at least a default colormap

  if (colormaps->size > 1) {
    idx = b_rand_uint32_range(0, colormaps->size - 1);
  } else {
    /* no colormaps loaded but the default */
#ifdef DEBUG
    printf("[i] Tututut no colormaps loaded, random gives the default\n");
#endif
    /* idx = 0; */ /* already initialized */
  }

  return colormaps->cmaps[idx]->id;
}


uint16_t
Colormaps_name_to_index(const char *name)
{
  assert(NULL != colormaps);

  for (uint16_t i = 0; i < colormaps->size; i++)
    if (is_equal(colormaps->cmaps[i]->name, name)) {
      return i;
    }

  VERBOSE(fprintf(stderr, "[!] Colormap '%s' not found\n", name));

  return 0; /* Return the default colormap */
}
