#pragma once
#include <array>
#include "Counter.hpp"
class Image;
//! для пререндера
class Light_mipmap {
public:
  constx float flash_size = 330.0f; ///< максимальный размер вспышки
  constx int mipmap_count = 30; ///< количество мипмапов
  std::array<Image, mipmap_count> mipmap = {}; ///< пререндер вспышки
  constx float step = flash_size / mipmap_count;
private:
  //! делает картинку - градиентную точку со звёздочкой
  void make_flash(Image& dst, float radius) {
    if (radius <= 0.0f)
      return;
    const int size = std::ceil(radius * 2.0f);
    dst.init(size, size, Color::BLACK);
    // рисование градиентного кружка
    const int i_radius = std::floor(radius);
    int x = i_radius;
    int y = 0;
    int xChange = 1 - (i_radius << 1);
    int yChange = 0;
    int radiusError = 0;
    const int half_size = size / 2;
    #define x0 half_size
    #define y0 half_size
    while (x >= y) {
      for (int i = x0 - x; i <= x0 + x; ++i) {
        auto col = (distance(Vec(x0, y0), Vec(i, y0 + y)) / radius);
        col = (1.0f - col) * (Color::WHITE2);
        col = std::max(col, 0.0f);
        dst.set(i, y0 + y, col);
        dst.set(i, y0 - y, col);
      }
      for (int i = x0 - y; i <= x0 + y; ++i) {
        auto col = (distance(Vec(x0, y0), Vec(i, y0 + x)) / radius);
        col = (1.0f - col) * (Color::WHITE2);
        col = std::max(col, 0.0f);
        dst.set(i, y0 + x, col);
        dst.set(i, y0 - x, col);
      }
      ++y;
      radiusError += yChange;
      yChange += 2;
      if ((radiusError << 1) + xChange > 0) {
        --x;
        radiusError += xChange;
        xChange += 2;
      }
    } // while (x >= y)
    #undef x0
    #undef y0
  }
  //! генерация пререндера
  void init() {
    for (index_t i = 0; i < mipmap.size(); ++i)
      make_flash(mipmap[i], ((i + 1) * step) * 0.75f);
  }
public:
  Light_mipmap() { init(); }
  Light_mipmap(CN(Light_mipmap)) { init(); }
  Light_mipmap& operator = (CN(Light_mipmap)) { init(); return *this; }
}; // Light_mipmap
namespace { Light_mipmap light_mipmap = {}; }
constexpr uint LF_RAND = 1 << 0; ///< свечение со случайным миганием
constexpr uint LF_INFINITY = 1 << 1; ///< огонёк не гаснет
constexpr uint LF_STAR = 1 << 2; ///< вспышка звёздочка
constexpr uint LF_NOSTAR = 1 << 3; ///< отображение без звёздочки
constexpr uint LF_RSIZE = 1 << 4; ///< случайный размер в пределах value
//! Light source for particles
struct Light { 
  Counter delay = {};
  float value = 0;
  float radius = 0;
  uint flags = 0;
  Vec offset = {};
  Light() = default;
  ~Light() = default;
  Light(float val, float delay_, float radius_, uint flags_ = 0,
  CN(Vec) offset_ = {}):
    delay(delay_), value(val), radius(radius_), flags(flags_),
    offset(offset_) {}
  void clear() { *this = {}; }
  bool check() const {
    if (radius <= 0)
      return false;
    if (flags & LF_INFINITY)
      return true;
    if (value <= 0 or delay())
      return false;
    return true;
  }
  void update(float dt) {
    if ( !check())
      return;
    delay.update(dt);
  }
  //! ret 1 if light is valid (for light count optimization)
  bool print(Image& dst, Vec pos, float deg) const {
    if ( !check())
      return false;
    pos += offset.get_rotate_deg({}, 90.0f - deg);
    auto tmp_val = value;
    if ( !(flags & LF_INFINITY))
      tmp_val *= delay.get_ratio();
    if (flags & LF_RAND)
      tmp_val *= a83::frand_fast();
    if (flags & LF_RSIZE) // случайного размера вспышка
      tmp_val *= a83::frand();
    // отрисовка звёздочек
    if ( !(flags & LF_NOSTAR)) {
      if (flags & LF_STAR)
        dst.draw_star(pos.x, pos.y, radius, tmp_val);
      else
        dst.draw_star2(pos.x, pos.y, radius * 2.0f,
          tmp_val * float(1. + (1./3.)));
    } // if !LF_NOSTAR
    const index_t i_radius = radius / Light_mipmap::step; // and index for mipmap
    if (i_radius < light_mipmap.mipmap.size()) {
      cnauto flash = light_mipmap.mipmap[i_radius];
      // оптмизированная по размерам вставка вспышки
      int posx = pos.x - flash.getX() / 2;
      int posy = pos.y - flash.getY() / 2;
      if (posx >= dst.getX()) return true;
      if (posy >= dst.getY()) return true;
      auto ey = posy + flash.getY();
      auto ex = posx + flash.getX();
      if (ex <= 0) return true;
      if (ey <= 0) return true;
      if (ey > dst.getY()) ey = dst.getY();
      if (ex > dst.getX()) ex = dst.getX();
      cauto old_posy = posy;
      cauto old_posx = posx;
      if (posx < 0) posx = 0;
      if (posy < 0) posy = 0;
      for (int y = posy; y < ey; ++y)
      for (int x = posx; x < ex; ++x) {
        cnauto pix_src = flash.fast_get(x - old_posx, y - old_posy);
        blend_add_safe(Color::get_gray(std::min(pix_src.val * tmp_val, 255.0f)),
          *dst.fast_get_ptr(x, y));
      }
    } else
      dst.add(255.0f * tmp_val);
    return true;
  } // print
};