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


static uint8_t
Schemes_parse(const json_t *array)
{
  size_t index;
  json_t *value;

  json_array_foreach(array, index, value) {
    if (!json_is_object(value)) {
      xerror("Scheme must be an object\n");
    } else {
      json_t *plugins = json_object_get(value, "plugins");

      if (!json_is_array(plugins)) {
        xerror("Scheme 'plugins' value must be an array\n");
      }
      schemes->schemes[index] = Scheme_new(json_array_size(plugins));
      json_t *auto_colormaps = json_object_get(value, "auto_colormaps");
      json_t *auto_images = json_object_get(value, "auto_images");
      schemes->schemes[index]->auto_colormaps = Scheme_str2AutoMode(json_string_value(auto_colormaps));
      schemes->schemes[index]->auto_images = Scheme_str2AutoMode(json_string_value(auto_images));
      size_t index2;
      json_t *value2;
      json_array_foreach(plugins, index2, value2) {
        if (!json_is_object(value2)) {
          xerror("Scheme item must be an object\n");
        } else {
          json_t *proba = json_object_get(value2, "proba");
          json_t *options = json_object_get(value2, "options");

          if (NULL != proba) {
            if (!json_is_real(proba)) {
              xerror("Scheme item 'proba' must be a real\n");
            } else {
              double p = json_real_value(proba);

              if ((p < 0) || (p > 1)) {
                xerror("Scheme item 'proba' must be in the range [0.0..1.0]\n");
              } else {
                schemes->schemes[index]->items[index2]->p = p;
              }
            }
          } else {
            schemes->schemes[index]->items[index2]->p = 1.0;
            schemes->schemes[index]->items[index2]->mandatory = 1;
          }

          if ((NULL == options) || !json_is_array(options) || !json_array_size(options)) {
            xerror("Scheme item 'options' is mandatory and must be a non-empty array of options\n");
          } else {
            size_t index4;
            json_t *value4;

            json_array_foreach(options, index4, value4) {
              if (!json_is_string(value4)) {
                xerror("Options must be strings\n");
              } else {
                const char *option = json_string_value(value4);

                if (is_equal(option, "BO_NORANDOM") || is_equal(option, "BO_FIRST") || is_equal(option, "BO_LAST")) {
                  xerror("Option '%s' is not allowed in schemes\n", option);
                } else {
                  schemes->schemes[index]->items[index2]->options |= Schemes_str2option(option);
                }
              }
            }
          }
        }
      }
    }
  }

  return index;
}


void
Schemes_new(const char *file)
{
  json_error_t error;

  json_t *parsed_json = json_load_file(file, 0, &error);
  if (NULL == parsed_json) {
    fprintf(stderr, "[!] Error loading schemes %s: (line %d, column %d): %s\n", file, error.line, error.column, error.text);
    exit(1);
  }
  schemes = xcalloc(1, sizeof(Schemes_t));
  if (json_is_array(parsed_json)) {
    schemes->schemes = xcalloc(json_array_size(parsed_json), sizeof(Scheme_t *));
    schemes->size = Schemes_parse(parsed_json);
  } else {
    fprintf(stderr, "[!] Schemes data must be a JSON array\n");
  }
  json_decref(parsed_json);
  if (!schemes->size) {
    VERBOSE(printf("[!] No schemes loaded\n"));
    xfree(schemes);
  } else {
    VERBOSE(printf("[i] Loaded %d scheme%s\n", schemes->size, (schemes->size == 1) ? "": "s"));
    schemes->shuffler = Shuffler_new(schemes->size);
#ifdef DEBUG_AUTO_MODES
    Shuffler_set_mode(schemes->shuffler, BS_CYCLE);
#else
    Shuffler_set_mode(schemes->shuffler, BS_RANDOM); // no real need to be 'shuffling'
#endif
#ifdef DEBUG
    Shuffler_verbose(schemes->shuffler);
#endif
  }
}


void
Schemes_delete(void)
{
  if (NULL != schemes) {
    for (uint16_t i = 0; i < schemes->size; i++) {
      Scheme_delete(schemes->schemes[i]);
    }
    xfree(schemes->schemes);
    Shuffler_delete(schemes->shuffler);
    xfree(schemes);
  }
}
