#pragma once
#include "a83/error.hpp"
#include "a83/string-util.hpp"
#include "image-color-blend.hpp"
#include <cstring>
#include "dither-mtx.hpp"
#include "common/Vec.hpp"
#include "common/archive.hpp"
namespace { const Color NULL_COLOR = Color(Color::BLACK); }
//! режимы получения пикселя вне пределов изображения
enum class bound_e {NONE = 0, MIRROR, DIV, COPY};
//! класс для игровых изображений
class Image {
protected:
  bool index_bound(int& x, int& y, bound_e mode = {}) const;
  bool index_bound(int& i, bound_e mode = {}) const;
public:
  int X = 0, Y = 0, SIZE = 0;
  Color* data = nullptr;
  Image() = default;
  Image(int nx, int ny) { init(nx, ny); }
  Image(int nx, int ny, CN(Color) col) { init(nx, ny, col); }
  Image(CN(Image) img) { init(img); }
  ~Image() { free(); }
  Image& operator =(CN(Image) img) { init(img); return *this; }
  Color* get_data() { return data; }
  const Color* get_data() const { return data; }
  CN(int) size() const { return SIZE; }
  CN(int) getX() const { return X; }
  CN(int) getY() const { return Y; }
  void init(int nx = 0, int ny = 0);	///< выделение памяти под картинку
  void init(int nx, int ny, const Color& col) { init(nx, ny); fill(col); }
  void init(CN(Image) img);	///< копирование
  void free() {
    zdelete_arr(data);
    X = Y = SIZE = 0;
  }
  constexpr bool check() const { return data; }
  void fill(CN(Color) col);
  void clear();
  void invert();
  // get:
  CN(Color) fast_get(int i) const { return data[i]; }
  CN(Color) fast_get(int x, int y) const { return data[y * X + x]; }
  Color* fast_get_ptr(int i) { return &data[i]; }
  Color* fast_get_ptr(int x, int y) { return &data[y * X + x]; }
  const Color* fast_get_ptr(int i) const { return &data[i]; }
  const Color* fast_get_ptr(int x, int y) const { return &data[y * X + x]; }
  // функции с проверкой:
  CN(Color) get(int i, bound_e mode = {}) const;
  CN(Color) get(int x, int y, bound_e mode = {}) const;
  Color* get_ptr(int i, bound_e mode = {});
  Color* get_ptr(int x, int y, bound_e mode = {});
  const Color* get_ptr(int i, bound_e mode = {}) const;
  const Color* get_ptr(int x, int y, bound_e mode = {}) const;
  // варик побыстрее:
  const Color* get_ptr2(int x, int y) const;
  // set:
  void fast_set(int i, CN(Color) col) { data[i] = col; }
  void fast_set(int x, int y, CN(Color) col) { data[y * X + x] = col; }
  void set(int i, CN(Color) col, bound_e mode = {});
  void set(int x, int y, CN(Color) col, bound_e mode = {});
  void set_add(int x, int y, int val, bound_e mode = {});
  //!@param white_decrease if = 1, учитывает WHITE как WHITE2
  void add(int val, bool white_decrease = false);
  void mul(int val);
// image-effects.cpp:
  template<int size = 2> void fast_blur();
  void rotate_90(Image &dst) const; //!< поворот картинки на 90 градусов
  void rotate_180(Image &dst) const; //!< поворот картинки на 180 градусов
  void rotate_270(Image &dst) const; //!< поворот картинки на 270 градусов
  void mirror_v(Image &dst) const; //!< отзеркаливание картинки по вертикали
  void mirror_h(Image& dst) const; //!< отзеркаливание картинки по горизонтали
  void mirror_hv(Image &dst) const; //!< оба отзеркаливания
  void contrast2();
  /** Эффект воздушного искажения
  *@details достигается копированием областей
  *@param pos центр эффекта
  *@param sz размер копируемого квадратика
  *@param len растояние смещения области
  *@param cnt число копирований
  *@param r радиус эффекта */
  void haze(CN(Vec) pos, int sz = 8, int len = 3, int cnt = 4, float r = 30.0f);
// image-utils.cpp:
  //! рисует изображение мигающее шахматной сеткой
  template<blend_pf f>
  void make_print_blink_dot2(Image& dst, CN(Vec) pos, int timer) const;
  //! растекание пикселей по краям в разные стороны
  void pixel_expand(CN(Color) alpha = Color::BLACK, bool use_8way = false);
  //! вырезать часть картинки и вставить в другую
  void cut2img(int sx, int sy, int mx, int my, Image& dst,
    bound_e mode = bound_e::MIRROR) const;
  //! быстрое вырезание части картинки
  void fast_cut(int sx, int sy, int mx, int my, Image& dst) const;
  void noise(bool USE_RED = false); //!< функция заполняет картинку случайными цветами
  //! быстрое копирование двух одинаковых по размеру картинок
  void fast_print(Image& dst) const;
  //! src вставляется на dst с позиции posx/y
  void print_mask(int posx, int posy, Image& dst, CN(Image) mask) const;
  /** рисует контур прямоугольника
  sx,sy - кординаты левой верхней части прям-а
  mx,my - размеры прям-а */
  void draw_rect(int sx, int sy, int mx, int my, Color col);
  /** рисует залитый прямоугольник
  sx,sy - кординаты левой верхней части прям-а
  mx,my - размеры прям-а */
  void draw_rect_filled(int sx, int sy, int mx, int my, Color col) { make_draw_rect_filled<blend_past>(sx,sy, mx,my, col); }
  void draw_rect_filled_diff(int sx, int sy, int mx, int my, Color col) { make_draw_rect_filled<blend_diff>(sx,sy, mx,my, col); }
  void draw_rect_filled_blend_158(int sx, int sy, int mx, int my, Color col) { make_draw_rect_filled<blend_158>(sx,sy, mx,my, col); }
  template <blend_pf f> void make_draw_rect_filled(int sx, int sy, int mx, int my, Color col);
  //! EFLA Variation E (Addition Fixed Point PreCalc)
  void draw_line(int x, int y, int x2, int y2, Color color);
  //! нарисовать закрашенный круг
  void circle_fill(int x0, int y0, CN(Color) col, int radius);
  //! нарисовать круг без заливки
  void draw_circle(Color col, int x0, int y0, int radius);
  /*рисует на dst изображение повёрнутое на угол deg вокруг 
  точки center_x/y с позиции pos_x/y */
  void print_rotated(Image& dst, float pos_x, float pos_y,
  float center_x, float center_y, float deg) const;
// отпимизированные по размерам видимой картинки функции вставки:
  //! оптимизированная вставка с учётом границ
  template <blend_pf f> auto make_print(int posx, int posy, Image& dst) const -> void;
  using print_f = void (Image::*)(int posx, int posy, Image& dst) const;
  void print    (int posx, int posy, Image& dst) const;
  void print_min(int posx, int posy, Image& dst) const;
  void print_max(int posx, int posy, Image& dst) const;
  void print_avr(int posx, int posy, Image& dst) const;
  void print_add(int posx, int posy, Image& dst) const;
  void print_add_safe(int posx, int posy, Image& dst) const;
  void print_or (int posx, int posy, Image& dst) const;
  void print_diff(int posx, int posy, Image& dst) const;
  void print_and(int posx, int posy, Image& dst) const;
  void print_blend_158(int posx, int posy, Image& dst) const;
  template <blend_pf f> void make_fast_print(Image& dst) const;
  void fast_print_max(Image& dst) const { make_fast_print<blend_max>(dst); }
  void fast_print_add(Image& dst) const { make_fast_print<blend_add>(dst); }
  void fast_print_add_safe(Image& dst) const { make_fast_print<blend_add_safe>(dst); }
  void fast_print_sub_safe(Image& dst) const { make_fast_print<blend_sub_safe>(dst); }
  void fast_print_diff(Image& dst) const { make_fast_print<blend_diff>(dst); }
  void fast_print_or(Image& dst) const { make_fast_print<blend_or>(dst); }
  void fast_print_and(Image& dst) const { make_fast_print<blend_and>(dst); }
  void fast_print_mul(Image& dst) const { make_fast_print<blend_mul>(dst); }
  void binary_safe(); ///< threshold 50%
  //! восьмиконечная звезда
  void draw_star(int posx, int posy, float size = 16.0, float power = 1.0);
  //! вспыщка полоской
  void draw_star2(int posx, int posy, float size = 16.0, float power = 1.0);
  void fast_print_mask(Image& dst, CN(Image) mask,
    CN(Image) mask_light, bool use_mask_light = false) const;
  /** эффект линий движения
  @param len длинна линий
  @param dir_x/y нормализованный вектор направления
  @param freg частота появления [0..1] */
  void motion_lines(float len, float dir_x, float dir_y, float freg = 0.5f);
// image-io.cpp:
  void save(a83::Str name);
  void load(CN(data_t) mem);
  void load_file(a83::Str name);
  Image(CN(a83::Str) name) { load_file(name); }
  void load_arch(CN(Archive) arch, a83::Str name);
  Image(CN(Archive) arch, CN(a83::Str) name) { load_arch(arch, name); }
// dither.cpp:
  //! дизер Байера
  template <uint color_num, uint mtx_size, const float mtx[]>
  void make_dither_bayer();
  void dither_bayer16_c2() { this->make_dither_bayer<2, 16, mtx_16x16>(); }
  void dither_bayer16_c3() { this->make_dither_bayer<3, 16, mtx_16x16>(); }
  void dither_bayer16_c8() { this->make_dither_bayer<8, 16, mtx_16x16>(); }
};
namespace { Image zero_image = {}; }
#include "image-utils-templ.hpp"
#include "image.cpp"
#include "image-io.cpp"
#include "image-utils.cpp"
#include "image-effects.cpp"
#include "dither.cpp"