#include "textures.hpp"
#include <SFML/Graphics/Image.hpp>
#include "dbc.hpp"
#include <fmt/core.h>
#include "config.hpp"
#include "constants.hpp"
#include <memory>

namespace textures {
  using std::shared_ptr, std::make_shared;

  static TextureManager TMGR;
  static bool initialized = false;

  void load_sprites() {
    Config assets("assets/config.json");

    for(auto& [name, settings] : assets["sprites"].items()) {
      auto texture = make_shared<sf::Texture>(settings["path"]);

      texture->setSmooth(assets["graphics"]["smooth_textures"]);
      auto sprite = make_shared<sf::Sprite>(*texture);

      int width = settings["frame_width"];
      int height = settings["frame_height"];
      sprite->setTextureRect({{0,0}, {width, height}});

      TMGR.sprite_textures.try_emplace(name, name, sprite, texture);
    }

    TMGR.floor = load_image(assets["sprites"]["floor"]["path"]);
    TMGR.ceiling = load_image(assets["sprites"]["ceiling"]["path"]);
  }

  void load_tiles() {
    Config assets("assets/tiles.json");
    auto &tiles = assets.json();

    for(auto &el : tiles.items()) {
      auto &config = el.value();
      TMGR.surfaces.emplace_back(load_image(config["texture"]));
      wchar_t tid = config["display"];
      int surface_i = TMGR.surfaces.size() - 1;
      TMGR.char_to_texture[tid] = surface_i;
    }
  }

  void init() {
    if(!initialized) {
      load_tiles();
      load_sprites();
      initialized = true;
    }
  }

  SpriteTexture get(std::string name) {
    dbc::check(initialized, "you forgot to call textures::init()");
    dbc::check(TMGR.sprite_textures.contains(name),
        fmt::format("!!!!! texture pack does not contain {} sprite", name));

    auto result = TMGR.sprite_textures.at(name);

    dbc::check(result.sprite != nullptr,
        fmt::format("bad sprite from textures::get named {}", name));
    dbc::check(result.texture != nullptr,
        fmt::format("bad texture from textures::get named {}", name));

    return result;
  }

  sf::Image load_image(std::string filename) {
    sf::Image texture;
    bool good = texture.loadFromFile(filename);
    dbc::check(good, fmt::format("failed to load {}", filename));
    return texture;
  }

  const uint32_t* get_surface(size_t num) {
    return (const uint32_t *)TMGR.surfaces[num].getPixelsPtr();
  }

  matrix::Matrix convert_char_to_texture(matrix::Matrix &tile_ids) {
    auto result = matrix::make(matrix::width(tile_ids), matrix::height(tile_ids));

    for(matrix::each_cell it(tile_ids); it.next();) {
      wchar_t tid = tile_ids[it.y][it.x];
      result[it.y][it.x] = TMGR.char_to_texture.at(tid);
    }

    return result;
  }

  const uint32_t* get_floor() {
    return (const uint32_t *)TMGR.floor.getPixelsPtr();
  }

  const uint32_t* get_ceiling() {
    return (const uint32_t *)TMGR.ceiling.getPixelsPtr();
  }
};