#pragma once
#include "a83/macro.hpp"
#include <algorithm>
/** Индексированные цвета HPW
*@details Диапазоны: 0 B, 0..222 BW, 223..254 Red, 255 W. */
struct Color {
  constx uint8_t BLACK = 0;
  constx uint8_t WHITE2 = 222;
  constx uint8_t BLACK2 = 223; ///< red start
  constx uint8_t RED_START = BLACK2;
  constx uint8_t OPTION_START = BLACK2;
  constx uint8_t RED = 254; ///< red end
  constx uint8_t RED_COUNT = 32;
  constx uint8_t OPTION_END = 254;
  constx uint8_t WHITE = 255;
  constx uint8_t GRAY_COUNT = WHITE - 1 - RED_COUNT;
  uint8_t val = BLACK;
  constexpr Color() = default;
  constexpr Color(CN(Color)) = default;
  constexpr Color(uint8_t nval): val(nval) {}
  constexpr Color& operator = (CN(Color)) = default;
  constexpr Color& operator = (uint8_t nval) { val = nval; return *this; }
  constexpr bool operator == (CN(Color) nval) const { return val == nval.val; }
  constexpr bool operator != (CN(Color) nval) const { return val ^ nval.val; }
  constexpr bool operator == (uint8_t nval) const { return val == nval; }
  constexpr bool operator != (uint8_t nval) const { return val ^ nval; }
  constexpr void set(int _val) { val = _val; }
  void set_gray(int gray);
  void set_red(int red)
    { val = std::min(uint(red) + uint(RED_START), uint(RED)); }
  void set_red_nosafe(int red)
    { val = RED_START + uint8_t(red % int(RED_COUNT)); }
  static Color get_gray(int gray);
  static Color get_red(int red);
  static Color get_red_nosafe(int red);
  constexpr bool is_red() const { return val > WHITE2 && val < WHITE; }
  constexpr bool is_white() const { return val == WHITE2 || val == WHITE; }
  void add(int x);
  void sub(int x);
  void mul(int x);
  void inv();
  constexpr CN(uint8_t) operator()() const { return val; }
  Color& operator += (int x) { add(x); return *this; }
  Color& operator -= (int x) { sub(x); return *this; }
  Color& operator *= (int x) { mul(x); return *this; }
  Color operator +(int x);
  Color operator -(int x);
  Color operator *(int x);
  Color operator /(int x);
  Color operator +(const Color& x);
  Color operator -(const Color& x);
  Color operator *(const Color& x);
  Color operator /(const Color& x);
  Color operator ~() const { Color ret(*this); ret.invert(); return ret; }
  Color& operator +=(const Color& x);
  Color& operator -=(const Color& x);
  Color& operator *=(const Color& x);
  Color& operator /=(const Color& x);
  void invert();
  void to_rgb24(uint8_t &r, uint8_t &g, uint8_t &b) const;
  //! преобразование цвета в число 0..1
  double to_d() const {
    if (is_red()) {
      constexpr double red_mul = 1.0 / RED_COUNT;
      return (val - BLACK2) * red_mul;
    } else if (is_white())
      return 1.0;
    // gray:
    constexpr double gray_mul = 1.0 / GRAY_COUNT;
    return val * gray_mul;
  } // to_d
  //! создаёт цвет из числа в диапазоне 0..1
  static Color from_d(double src, bool red = false) {
    if (src >= 1.0)
      return red ? RED : WHITE;
    if (src <= 0.0)
      return red ? RED_START : BLACK;
    return red
      ? get_red(src * RED_COUNT)
      : Color(src * GRAY_COUNT);
  } // from_d
  //! преобразование цвета в число 0..1 (float)
  float to_f() const {
    if (is_red()) {
      constexpr float red_mul = 1.0f / RED_COUNT;
      return (val - BLACK2) * red_mul;
    } else if (is_white())
      return 1.0f;
    // gray:
    constexpr float gray_mul = 1.0f / GRAY_COUNT;
    return val * gray_mul;
  } // to_f
  //! создаёт цвет из числа в диапазоне 0..1 (float)
  static Color from_f(float src, bool red = false) {
    if (src >= 1.0f)
      return red ? RED : WHITE;
    if (src <= 0.0f)
      return red ? RED_START : BLACK;
    return red
      ? get_red(src * RED_COUNT)
      : Color(src * GRAY_COUNT);
  } // from_f
}; // Color
void Color::set_gray(int gray) { val = std::clamp<int>(gray, BLACK, WHITE2); }
//! обесцвечивание - шаблон
template <uint8_t (*kernel)(int R, int G, int B)>
Color make_desaturate_f(int R, int G, int B) {
  Color ret;
  constexpr float RED_MUL = 32.0f / 255.0f;
  constexpr float GRAY_MUL = 222.0f / 255.0f;
  if (G < 64 && B < 64 && R > G) { // is red:
    ret.set_red(R * RED_MUL);
    return ret;
  } else if ((G & B & R & 0xFF) == 0xFF) { // is white:
    ret = Color(Color::WHITE);
    return ret;
  }
  // is gray:
  ret.set_gray(kernel(R, G, B) * GRAY_MUL);
    return ret;
}
constexpr uint8_t desr_BT601(int R, int G, int B)
  { return R * 0.299f + G * 0.587f + B * 0.114f; }
constexpr uint8_t desr_average(int R, int G, int B) { return (R + G + B) / 3; }
#define desaturate_bt601 make_desaturate_f<desr_BT601>
#define desaturate_average make_desaturate_f<desr_average>
#define desaturate_mid desaturate_average
// ------------------ операторы вне оьъекта ----------------
Color operator + (CN(Color) a, CN(Color) b) { return Color(a) + b; }
Color operator - (CN(Color) a, CN(Color) b) { return Color(a) - b; }
Color operator * (CN(Color) a, CN(Color) b) { return Color(a) * b; }
Color operator / (CN(Color) a, CN(Color) b) { return Color(a) / b; }
Color operator + (CN(Color) a, int b) { return Color(a) + b; }
Color operator - (CN(Color) a, int b) { return Color(a) - b; }
Color operator * (CN(Color) a, int b) { return Color(a) * b; }
Color operator / (CN(Color) a, int b) { return Color(a) / b; }
#include "image-color.cpp"