#pragma once
#include "level.hpp"
#include "Layer.hpp"
#include "enity/enity.hpp"
#include "game/game-archive.hpp"
/** уровень обычной структуры
* @details есть несколько фонов, карта спавнов и зацикливаний */
class Level_sky: public Level {
protected:
  BankTex btex = {}; ///< банк текстур
  BankSprite bspr = {}; ///< банк спрайтов
  vp_layer_t down_layers = {}; ///< нижние слои
  Layer* main_layer = nullptr; ///< слой, на котором летает игрок
  vp_layer_t up_layers = {}; ///< верхние слои
  int index_btex = 1;
  int index_bspr = 1;
  Color bgr_col = {}; ///< цвет фона
  Layer* load_layer(CN(a83::Str) fname, int brightness = 0,
    bool is_sprite = false); ///! грузит слой из картинки
private:
  void _init(CN(a83::Str) fname);
  void load(CN(a83::Str) fname);
  Image screen_buf = {};
  Image screen_down = {}; ///< полотно для копии нижних фонов
  Image screen_up = {}; ///< полотно для копии верхних фонов
public:
  Level_sky() = default;
  explicit Level_sky(CN(a83::Str) fname)
    { _init(fname); }
  ~Level_sky() { free(); }
  void init(CN(a83::Str) fname) override
    { _init(fname); }
  void free() {
    btex.free();
    bspr.free();
    vp_delete(down_layers);
    zdelete(main_layer);
    vp_delete(up_layers);
    bgr_col = {};
    index_btex = index_bspr = 1;
    screen_up.free();
    screen_buf.free();
    screen_down.free();
  } // free
  void print_d(Image& dst) override {
    if ((Game::status & GF_BLINK_OPTMZ)
    and (Game::status & GF_VLAGS)
    and (Game::timer_anim & 1) ) {
      screen_down.fast_print(dst);
      return;
    }
  // отрисовка фонов:
  // -3 lr
    down_layers[0]->print(screen_down, btex, bspr, Game::timer_anim);
  // -2..-1 lr
    if (Game::status & GF_HIGH_DETAILED)
      for (int i = 1; i < 3; ++i)
        if (down_layers[i]) {
          screen_buf.clear();
          down_layers[i]->print(screen_buf, btex, bspr, Game::timer_anim);
          screen_buf.fast_print_max(screen_down);
        }
  // main lr
    screen_buf.clear();
    main_layer->print(screen_buf, btex, bspr, Game::timer_anim);
    screen_buf.fast_print_max(screen_down);
    screen_down.fast_print(dst);
  } // print_d
  //! отрисовка верхнего слоя
  void print_u(Image& dst) override {
    if ( !(Game::status & GF_HIGH_DETAILED))
      return;
    if ( !up_layers[0])
      return;
    if ((Game::status & GF_BLINK_OPTMZ)
    and (Game::status & GF_VLAGS)
    and (Game::timer_anim & 1) ) {
      screen_up.fast_print_add_safe(dst);
      return;
    }
    screen_up.clear();
    up_layers[0]->print(screen_up, btex, bspr, Game::timer_anim);
    screen_up.fast_print_add_safe(dst);
  } // print_upper
  void update(CN(Phys) global_motion, float dt) override {
    Level::update(global_motion, dt);
    Phys motion_offset(global_motion);
    motion_offset.move(dt);
    for (auto layer: down_layers)
      if (layer)
        layer->move(motion_offset.pos);
    if (main_layer)
      main_layer->move(motion_offset.pos);
    if (Game::status & GF_HIGH_DETAILED)
      for (auto layer: up_layers)
        if (layer)
          layer->move(motion_offset.pos);
  } // update
private:
  nocopy(Level_sky);
  void test_spawn() {
    FOR (i, 50) {
      if (auto p = add_ene(new Fighter()); p) {
        p->phys.pos = {4000.0f + i * 100.0f, 2000.0f};
      }
      if (auto p = add_ene(new Fighter()); p) {
        p->phys.pos = {-8000.0f + i * 100.0f, -6000.0f};
      }
    }
  } // test_spawn
}; // Level_sky
void Level_sky::_init(CN(a83::Str) fname) {
  free();
  screen_buf.init(Game::X, Game::Y);
  screen_up.init(Game::X, Game::Y);
  screen_down.init(Game::X, Game::Y);
  Level::init(fname);
  btex.init(4096);
  btex[0].free(); // нулевая картинка
  bspr.init(4096);
  bspr[0].mask.free(); bspr[0].tex.free();
  load(file_name);
  Game::status |= GF_INAIR; // включеие тряски игрока в воздухе
  test_spawn();
}
void Level_sky::load(CN(a83::Str) fname) {
  up_layers.resize(1, nullptr);
  down_layers.resize(3, nullptr);
  bgr_col = Color::WHITE;
  file_name = fname;
  const a83::Str ANIMDIR = "level/1/";
  #define INIT_LAYER(INDEX, VAL, NUM, SPD, BRGHT) { \
    VAL[INDEX] = load_layer(ANIMDIR + #NUM ".png", BRGHT); \
    IFERROR( !VAL[INDEX], "level_str -> layer " #NUM " is not created"); \
    VAL[INDEX]->speed = SPD; \
    VAL[INDEX]->flags = LAYER_XlOOP | LAYER_YlOOP; \
  }
  // main layer:
  main_layer = load_layer(ANIMDIR + "0.png");
  IFERROR( !main_layer, "level_str -> main_layer is not created");
  main_layer->flags = LAYER_XlOOP | LAYER_YlOOP;
  INIT_LAYER(0, down_layers, -3, 0.5, -96);
  // на упрощённой графике некоторые слои не грузятся:
  if (Game::status & GF_HIGH_DETAILED) {
    INIT_LAYER(1, down_layers, -2, 2.0 / 3.0, -72);
    INIT_LAYER(2, down_layers, -1, 0.8,       -8);
    INIT_LAYER(0, up_layers,    1, 3.0,       -128);
  }
  #undef INIT_LAYER
}
Layer* Level_sky::load_layer(CN(a83::Str) fname, int brightness, bool is_sprite) {
  Sprite source;
  if (is_sprite)
    source.load(Game::archive, fname);
  else
    source.tex.load_arch(Game::archive, fname);
// характеристики слоя:
  int layer_x = std::floor(float(source.tex.getX()) / float(Sector::XP)); 
  int layer_y = std::floor(float(source.tex.getY()) / float(Sector::YP));
  int bank_max = layer_x * layer_y + 384; 
  Layer* ret = new Layer(layer_x, layer_y, bank_max);
  IFERROR( !ret, "Level_sky.load_layer -> layer nomem");
  ret->bank_set(0, nullptr); // пустой сектор
  int index = 1;
// уровень заполняется секторами из одного монолитного тайла:
  for (int y = 0; y < layer_y; ++y)
  for (int x = 0; x < layer_x; ++x) {
    Sprite tile;
    source.cut(x * Sector::XP, y * Sector::YP, Sector::XP, Sector::YP, tile);
    tile.tex.add(brightness, true);
    #ifdef MARK_SECTORS
      Game::font->print(tile.tex, 1, 1,
        sconv<utf32>(a83::n2s(x) + "," + a83::n2s(y)), true);
    #endif
    Sector* sector;
    if (is_sprite) {
      bspr[index_bspr] = tile;
      sector = new SectorMono(index_bspr, is_sprite);
      ++index_bspr;
    } else {
      btex[index_btex] = tile.tex;
      sector = new SectorMono(index_btex, is_sprite);
      ++index_btex;
    }
    ret->bank_set(index, sector); // добавить сектор в банк
    ret->map_set(x, y, index); // ...сразу же указать на какой он позиции
    ++index;
  }
  return ret;
} // Level