
#include "image.hpp"
#include "a83/log.hpp"
#include "a83/macro.hpp"
//! изменение индексов с учётом границ картинки
bool Image::index_bound(int& x, int& y, bound_e mode) const {
  switch (mode) {
    default:
    case bound_e::NONE: // fast ver of x >= 0 && x < X && y >= 0...:
      return uint(x) < uint(X) && uint(y) < uint(Y);
      break;
    case bound_e::MIRROR:
      if (x < 0)
        x = x % X * -1;
      if (x >= X)
        x = X - (x % X) - 1;
      if (y < 0)
        y = y % Y * -1;
      if (y >= Y)
        y = Y - (y % Y) - 1;
      break;
    case bound_e::DIV:
      if (x < 0)
        x *= -1;
      if (x >= X)
        x %= X;
      if (y < 0)
        y *= -1;
      if (y >= Y)
        y %= Y;
      break;
    case bound_e::COPY:
      if (x < 0)
        x = 0;
      if (x >= X)
        x = X-1;
      if (y < 0)  
        y = 0;
      if (y >= Y)
        y = Y-1;
      break;
  }
  return true;
}
bool Image::index_bound(int& i, bound_e mode) const {
  if (X < 1 || Y < 1)
    return false;
  switch (mode) {
    case bound_e::MIRROR: {
      if (i < 0)
        i = i % size() * -1;
      if (i >= size())
        i = size() - (i % size()) - 1;
      break;
    }
    case bound_e::DIV: {
      if (i < 0)
        i *= -1;
      if (i >= size())
        i %= size();
      break;
    }
    case bound_e::COPY: {
      if (i < 0)
        i = 0;
      if (i >= size())
        i = size()-1;
      break;
    }
    case bound_e::NONE:
    default: { return i >= 0 && i < SIZE; }
  }
  return true;
}
// выделение памяти под картинку
void Image::init(int nx, int ny) {
  free();
  #ifdef DEBUG
  IFERROR(nx >= 32'000, "incorrect x res: " + a83::n2s(nx));
  IFERROR(ny >= 32'000, "incorrect y res: " + a83::n2s(ny));
  #endif
  if ( !nx or !ny) // можно создать пустую пикчу
    return;
  // можно не перевыделять память, если картинка была большой
  if (nx * ny <= X * Y)
    return;
  X = nx;
  Y = ny;
  SIZE = X * Y;
  data = new Color[SIZE];
}
void Image::fill(CN(Color) col) {
  if ( !check())
    return;
  memset(rcast(void*, data), col.val, SIZE * sizeof(Color));
}
void Image::clear() {
  if (check())
    memset(reinterpret_cast<void*>(data), 0, SIZE * sizeof(Color));
}
void Image::invert() {
  FOR (i, size())
    data[i].inv();
}
// копирование
void Image::init(CN(Image) img) {
  if ( !img.check())
    return;
  if (img.data == this->data)
    return;
  init(img.X, img.Y);
  memcpy(this->data, img.data, SIZE * sizeof(Color));
}
void Image::add(int val, bool white_decrease) {
  if ( !check())
    return;
  if (white_decrease) { // WHITE to WHITE2
    FOR (i, SIZE) {
      auto ptr = fast_get_ptr(i);
      if (ptr->val == Color::WHITE)
        *ptr = Color::WHITE2;
    }
  }
  cauto val_ = Color::get_gray(std::abs(val));
  if (val < 0) { // -
    FOR (i, SIZE)
      blend_sub_safe(val_, *fast_get_ptr(i));
  } else { // +
    FOR (i, SIZE)
      blend_add_safe(val_, *fast_get_ptr(i));
  }
}
void Image::mul(int val) {
  if ( !check())
    return;
  FOR (i, SIZE)
    blend_mul_safe(std::abs(val), *fast_get_ptr(i));
}
CN(Color) Image::get(int i, bound_e mode) const {
  if (check() && index_bound(i, mode))
    return fast_get(i);
  return NULL_COLOR;
}
CN(Color) Image::get(int x, int y, bound_e mode) const {
  if (check() && index_bound(x, y, mode))
    return fast_get(x, y);
  return NULL_COLOR;
}
Color* Image::get_ptr(int i, bound_e mode) {
  if (check() && index_bound(i, mode))
    return fast_get_ptr(i);
  return nullptr;
}
Color* Image::get_ptr(int x, int y, bound_e mode) {
  if (check() && index_bound(x, y, mode))
    return fast_get_ptr(x, y);
  return nullptr;
}
const Color* Image::get_ptr(int i, bound_e mode) const {
  if (index_bound(i, mode))
    return fast_get_ptr(i);
  return nullptr;
}
const Color* Image::get_ptr(int x, int y, bound_e mode) const {
  if (index_bound(x, y, mode))
    return fast_get_ptr(x, y);
  return nullptr;
}
const Color* Image::get_ptr2(int x, int y) const {
  if (uint(x) < uint(X) and uint(y) < uint(Y))
    return fast_get_ptr(x, y);
  return nullptr;
}
void Image::set(int i, CN(Color) col, bound_e mode) {
  if (index_bound(i, mode))
    fast_set(i, col);
}
void Image::set(int x, int y, CN(Color) col, bound_e mode) {
  if (index_bound(x, y, mode))
    fast_set(x, y, col);
}
void Image::set_add(int x, int y, int val, bound_e mode) {
  if (index_bound(x, y, mode))
    blend_add_safe(Color::get_gray(val), *fast_get_ptr(x, y));
}