#pragma once
class Sector;
struct Cord;
#ifdef DBG_LAYER
  class Image;
#endif
constx uint LAYER_XlOOP = 1 << 0; ///< tile print looped by x axis
constx uint LAYER_YlOOP = 1 << 1; ///< tile print looped by y axis
//! один слой уровня
class Layer {
public:
  Cord offset = {}; ///< смещение кордов уровня
  Cord camera = {}; ///< собственная камера сектора
  float speed = 1.f; ///< ускоритель движения фона
  uint flags = 0;
protected:
  int BANK_MAX = 0; ///< размер банка секторов
  Sector** sector_bank = nullptr; ///< банк секторов
  int sector_map_x = 0, sector_map_y = 0; ///< размеры карты секторов
  int sector_map_size = 0;
  int* sector_map = nullptr; ///< карта секторов
  bool is_bad_map_cord(int xs, int ys) const {
    return
      uint(xs) >= uint(sector_map_x) || 
      uint(ys) >= uint(sector_map_y);
  }
  bool is_bad_bank_id(int id) const { return id < 0 || id >= BANK_MAX; }
private:
  nocopy(Layer);
public:
  Layer() = default;
  Layer(int map_x, int map_y, int bank_max) { init(map_x, map_y, bank_max); }
  ~Layer() { free(); };
  void init(int map_x, int map_y, int bank_max) {
    free();
    BANK_MAX = bank_max;
    sector_bank = new Sector*[BANK_MAX];
    memset(sector_bank, 0, sizeof(Sector*) * BANK_MAX);
    sector_map_x = map_x;
    sector_map_y = map_y;
    sector_map_size = map_x * map_y;
    sector_map = new int[sector_map_size];
    memset(sector_map, 0, sizeof(int) * sector_map_size);
  } // init
  void free() {
    if (sector_bank) {
      for (int i = 0; i < BANK_MAX; ++i)
        if (sector_bank[i])
          delete sector_bank[i];
      delete[] sector_bank;
      sector_bank = nullptr;
    }
    zdelete_arr(sector_map);
    sector_map_x = 0;
    sector_map_y = 0;
    sector_map_size = 0;
    BANK_MAX = 0;
    camera = {};
    offset = {};
    speed = 1;
    flags = 0;
  } // free
  CN(int) getX() const { return sector_map_x; }
  CN(int) getY() const { return sector_map_y; }
  //! отрисовка секторов
  void print(Image& dst, CN(BankTex) btex, CN(BankSprite) bspr,
  int timer) const {
    #ifdef DEBUG
    IFERROR( !sector_bank, "Layer::print -> bad sector_bank mem");
    IFERROR( !sector_map, "Layer::print -> bad sector_map mem");
    #endif
    // число отображаемых секторов на экране
    cauto CX = dst.getX() / Sector::XP + 2;
    cauto CY = dst.getY() / Sector::YP + 2;
    for (int sy = 0; sy < CY; ++sy)
    for (int sx = 0; sx < CX; ++sx) {
      int MAP_POS_X = sx + std::floor(camera.xs); // 4 index 2 sector ptrs
      if (flags & LAYER_XlOOP) {
        MAP_POS_X %= sector_map_x;
        if (MAP_POS_X < 0)
          MAP_POS_X += sector_map_x;
      }
      if (uint(MAP_POS_X) >= uint(sector_map_x))
        continue;
      int MAP_POS_Y = sy + std::floor(camera.ys); // 4 index 2 sector ptrs
      if (flags & LAYER_YlOOP) {
        MAP_POS_Y %= sector_map_y;
        if (MAP_POS_Y < 0)
          MAP_POS_Y += sector_map_y;
      }
      if (uint(MAP_POS_Y) >= uint(sector_map_y))
        continue;
      int MAP_INDEX = MAP_POS_Y * sector_map_x + MAP_POS_X; // ptr 2 sectors
      cnauto ID = sector_map[MAP_INDEX];
      auto* pSect = sector_bank[ID];
      if (pSect)
        pSect->print(int(-camera.x) + sx * Sector::XP, int(-camera.y) + sy * Sector::YP,
          dst, btex, bspr, timer);
    } // for CX CY
    // отрисовка видимых границ сектора при дебаге:
    #ifdef DBG_LAYER
    for (int sy = 0; sy < CY; ++sy)
    for (int sx = 0; sx < CX; ++sx) {
      auto xPos = int(-camera.x) + sx*Sector::XP;
      auto yPos = int(-camera.y) + sy*Sector::YP;
      dst.draw_rect(xPos, yPos, Sector::XP, Sector::YP, Color(Color::RED));
    }
    #endif
  } // print
  //! добавить сектор в банк
  void bank_set(int id, Sector* p_sect) {
    #ifdef DEBUG
    IFERROR(!sector_bank, "Layer::bank_sett -> bad sector_bank mem");
    IFERROR(is_bad_bank_id(id), "Layer::bank_set -> bad id");
    #endif
    sector_bank[id] = p_sect;
  }
  //! получить сектор из банка
  const Sector* bank_get(int id) const {
    #ifdef DEBUG
    IFERROR( !sector_bank, "Layer::bank_get -> bad sector_bank mem");
    IFERROR(is_bad_bank_id(id), "Layer::bank_get -> bad id");
    #endif
    return sector_bank[id];
  }
  //! указать сектор на карте
  void map_set(int x, int y, int id) {
    #ifdef DEBUG
    IFERROR( !sector_map, "Layer::map_set -> bad sector_map mem");
    IFERROR(is_bad_map_cord(x, y), "Layer::map_set -> bad id");
    #endif
    sector_map[y * sector_map_x + x] = id;
  }
  //! двигает камеру слоя
  void move(Vec motion) {
    camera += motion * speed;
    camera.fix();
  }
};
using vp_layer_t = std::vector<Layer*>;