#include "image-color.hpp"
#include "table/table-inv.hpp"
Color Color::get_gray(int gray) {
  Color ret;
  ret.set_gray(gray);
  return ret;
}
Color Color::get_red(int red) {
  Color ret;
  ret.set_red(red);
  return ret;
}
Color Color::get_red_nosafe(int red) {
  Color ret;
  ret.set_red_nosafe(red);
  return ret;
}
#define MAKE_FUNC(NAME, OP) \
void Color::NAME(int x) { \
  x OP##= val; \
  if (is_red()) \
    x = std::clamp<int>(x, BLACK2, RED); \
  else { \
    if (x == WHITE) \
      x = WHITE2; \
    x = std::clamp<int>(x, BLACK, WHITE2); \
  } \
  val = x; \
}
MAKE_FUNC(add, +)
MAKE_FUNC(sub, -)
MAKE_FUNC(mul, *)
#undef MAKE_FUNC
void Color::inv() {
  if (is_red()) val = RED - (val - BLACK2) - 1;
  else if (val == WHITE) val = BLACK;
  else val = WHITE2 - val - 1;
}
//------------------ операторы для чисел ----------------------------
#define OPERATOR(OP) \
Color Color::operator OP(int x) { \
  Color ret; \
  if (is_red()) \
    ret.set_red(std::clamp<int>(val OP x, BLACK2, RED)); \
  else \
    ret.set_gray(std::clamp<int>(val OP x, BLACK, WHITE2)); \
  return ret; \
}
OPERATOR(+)
OPERATOR(-)
OPERATOR(*)
#undef OPERATOR
Color Color::operator /(int x) {
  if ( !x)
    return Color();
  Color ret;
  if (is_red())
    ret.set_red(std::clamp<int>(val / x, BLACK2, RED));
  else
    ret.set_gray(std::clamp<int>(val / x, BLACK, WHITE2));
  return ret;
}
//--------------------- операторы для объектов ----------------------------
#define OPERATOR(OP) \
Color Color::operator OP(const Color& x) { \
  Color ret; \
  if (x.is_red()) { \
    if (is_red()) \
      ret.set_red(std::clamp<int>(val OP x.val, BLACK2, RED)); \
    else \
      ret.set_gray(std::clamp<int>(val OP x.val, BLACK, WHITE2)); \
  } \
  return ret; \
}
OPERATOR(+)
OPERATOR(-)
OPERATOR(*)
#undef OPERATOR
Color Color::operator /(const Color& x) {
  if ( !x.val)
    return Color();
  Color ret;
  if (x.is_red()) {
    if (is_red())
      ret.set_red(std::clamp<int>(val / x.val, BLACK2, RED));
    else
      ret.set_gray(std::clamp<int>(val / x.val, BLACK, WHITE2));
  }
  return ret;
}
void Color::invert() { this->val = table_inv[this->val]; }
// --------------- операторы с применением действия -------------
#define OPERATOR(OP) \
Color& Color::operator OP##=(const Color& x) { \
  if (x.is_red()) { \
    if (is_red()) \
      set_red(std::clamp<int>(val OP x.val, BLACK2, RED)); \
    else \
      set_gray(std::clamp<int>(val OP x.val, BLACK, WHITE2)); \
  } \
  return *this; \
}
OPERATOR(+)
OPERATOR(-)
OPERATOR(*)
#undef OPERATOR
Color& Color::operator /=(const Color& x) {
  if ( !x.val)
    return *this;
  if (x.is_red()) {
    if (is_red())
      set_red(std::clamp<int>(val / x.val, BLACK2, RED));
    else
      set_gray(std::clamp<int>(val / x.val, BLACK, WHITE2));
  }
  return *this;
}
void Color::to_rgb24(uint8_t &r, uint8_t &g, uint8_t &b) const {
  // INFO можно сделать быструю версию с таблицей перекодировки
  if (val < BLACK2) {
    constexpr float GRAY_MUL = 255.0f / GRAY_COUNT;
    r = g = b = val * GRAY_MUL;
    return;
  }
  if (val == WHITE) {
    r = g = b = 0xFF;
    return;
  }
  g = b = 0;
  constexpr float RED_MUL = 255.0f / RED_COUNT;
  r = val - BLACK2;
  r *= RED_MUL;
}