/**
 * @file
 * @brief Qualpal API for generating qualitative color palettes
 *
 * This file defines the Qualpal class, which provides a builder-style
 * interface for configuring and generating maximally distinct color palettes
 * using perceptual color difference. The API supports multiple input formats,
 * color vision deficiency simulation, and flexible configuration options.
 *
 * Usage example:
 * @code{.cpp}
 * qualpal::Qualpal qp;
 * qp.setInputRGB(rgb_colors)
 *   .setCvd(cvd_params)
 *   .setBackground(bg)
 *   .setMetric(metric)
 *   .setMemoryLimit(2.0);
 * auto palette = qp.generate(n);
 * @endcode
 */

#pragma once

#include <map>
#include <optional>
#include <qualpal/colors.h>
#include <qualpal/metrics.h>
#include <vector>

/**
 * @namespace qualpal
 * @brief Qualitative color palette generation library
 */
namespace qualpal {

/**
 * @enum ColorspaceType
 * @brief Supported cylindrical color spaces for input colors
 *
 * This enum defines the available cylindrical color spaces that can be used
 * for generating palettes. Currently, it supports HSL (Hue, Saturation,
 * Lightness) and LCHab (Lightness, Chroma, Hue in CIE Lab).
 */
enum class ColorspaceType
{
  HSL,
  LCHab
};

/**
 * @class Qualpal
 * @brief Builder for qualitative color palette generation
 *
 * The Qualpal class allows step-by-step configuration of palette generation
 * options. Users can set the input source (RGB, hex, palette name, or HSL
 * colorspace), color vision deficiency simulation parameters, background color,
 * color difference metric, and memory limit. The palette is generated by
 * calling generate().
 */
class Qualpal
{
public:
  /**
   * @brief Default constructor. Initializes with no input source.
   */
  Qualpal() = default;

  /**
   * @brief Set input colors from a vector of RGB values.
   * @param colors Vector of RGB colors to use as input.
   * @return Reference to this object for chaining.
   */
  Qualpal& setInputRGB(const std::vector<colors::RGB>& colors);

  /**
   * @brief Set input colors from a vector of hex color strings.
   * @param colors Vector of hex color strings (e.g., "#ff0000").
   * @return Reference to this object for chaining.
   * @throws std::invalid_argument if any hex string is invalid.
   */
  Qualpal& setInputHex(const std::vector<std::string>& colors);

  /**
   * @brief Set input colors from a named palette.
   * @param palette_name Palette name in the format "Package:Palette".
   * @return Reference to this object for chaining.
   * @throws std::invalid_argument if the palette name is invalid.
   */
  Qualpal& setInputPalette(const std::string& palette_name);

  /**
   * @brief Set input colors by sampling HSL colorspace.
   * @param h_lim Hue range in degrees [-360, 360].
   * @param s_or_c_lim Saturation or Chroma (depending on `ColorspaceType`)
   * range [0, 1] or >= 0.
   * @param l_lim Lightness range [0, 1] or [0, 100].
   * @param space Colorspace type
   * @return Reference to this object for chaining.
   * @throws std::invalid_argument for invalid ranges.
   */
  Qualpal& setInputColorspace(const std::array<double, 2>& h_lim,
                              const std::array<double, 2>& s_or_c_lim,
                              const std::array<double, 2>& l_lim,
                              ColorspaceType space = ColorspaceType::HSL);

  /**
   * @brief Set color vision deficiency simulation parameters.
   * @param cvd_params Map of {"protanomaly"|"deutananomaly"|"tritanomaly" ->
   * severity [0,1]}.
   * @return Reference to this object for chaining.
   */
  Qualpal& setCvd(const std::map<std::string, double>& cvd_params);

  /**
   * @brief Set the background color for palette generation.
   * @param bg_color RGB background color.
   * @return Reference to this object for chaining.
   */
  Qualpal& setBackground(const colors::RGB& bg_color);

  /**
   * @brief Set the color difference metric to use.
   * @param metric Metric type (e.g., DIN99d).
   * @return Reference to this object for chaining.
   */
  Qualpal& setMetric(metrics::MetricType metric);

  /**
   * @brief Set the maximum memory limit (in GB) for color difference matrix.
   * @param gb Memory limit in gigabytes.
   * @return Reference to this object for chaining.
   * @throws std::invalid_argument if gb <= 0.
   */
  Qualpal& setMemoryLimit(double gb);

  /**
   * @brief Set the number of points in the colorspace grid for HSL input.
   * @param n_points Number of points to sample in the colorspace grid.
   * @return Reference to this object for chaining.
   * @throws std::invalid_argument if n_points <= 0.
   */
  Qualpal& setColorspaceSize(std::size_t n_points);

  /**
   * @brief Generate a qualitative color palette with the configured options.
   * @param n Number of colors to generate.
   * @return Vector of n selected RGB colors.
   * @throws std::runtime_error if no input source is configured.
   * @throws std::invalid_argument for invalid configuration.
   */
  std::vector<colors::RGB> generate(std::size_t n);

  /**
   * @brief Extend an existing palette by adding n new colors.
   * @param palette Existing palette (RGB colors) to keep fixed.
   * @param n Size of the new palette to generate, which includes
   * the existing palette.
   * @return Vector of palette + n new RGB colors.
   */
  std::vector<colors::RGB> extend(const std::vector<colors::RGB>& palette,
                                  std::size_t n);

private:
  std::vector<colors::RGB> selectColors(
    std::size_t n,
    const std::vector<colors::RGB>& fixed_palette = {});

  std::vector<colors::RGB> rgb_colors_in;

  std::vector<std::string> hex_colors;

  std::string palette;

  std::array<double, 2> h_lim = { 0, 360 };
  std::array<double, 2> s_or_c_lim = { 0, 1 };
  std::array<double, 2> l_lim = { 0, 1 };
  std::size_t n_points = 100;

  /**
   * @brief Internal mode for tracking input source.
   */
  enum class Mode
  {
    NONE,
    RGB,
    HEX,
    PALETTE,
    COLORSPACE
  } mode = Mode::NONE;

  std::map<std::string, double> cvd;
  std::optional<colors::RGB> bg;
  metrics::MetricType metric = metrics::MetricType::CIEDE2000;
  double max_memory = 1;
  ColorspaceType colorspace_input = ColorspaceType::HSL;
};

} // namespace qualpal
