#pragma once

#include <ftxui/screen/screen.hpp>
#include <ftxui/dom/canvas.hpp>
#include <SFML/Window.hpp>
#include <SFML/System.hpp>
#include <SFML/Graphics.hpp>
#include <SFML/Graphics/Rect.hpp>
#include "point.hpp"
#include <codecvt>
#include "ansi_parser.hpp"
#include "panel.hpp"
#include "constants.hpp"
#include <optional>

using ftxui::Canvas, ftxui::Screen;

/*
 * BUG: This could be so much better.
 */
struct RenderConfig {
  unsigned int video_x = VIDEO_WINDOW_X;
  unsigned int video_y = VIDEO_WINDOW_Y;
  int ui_font_size=UI_FONT_SIZE;
  int base_map_font_size=BASE_MAP_FONT_SIZE;
  wchar_t bg_tile = BG_TILE;
  wchar_t ui_base_char = UI_BASE_CHAR;
  int bg_box_offset=BG_BOX_OFFSET;
};

struct SFMLRender {
  int $cells_w = 0;
  int $cells_h = 0;
  RenderConfig $config;
  sf::RenderWindow& $window;
  int $map_font_size;
  float $line_spacing;
  sf::Color $default_fg;
  sf::Color $default_bg;
  sf::Texture $font_texture;
  sf::Sprite $bg_sprite;
  sf::Font $font;
  sf::Text $ui_text;
  ANSIParser $ansi;

  std::unordered_map<wchar_t, sf::Sprite> $sprites;
  sf::Glyph $base_glyph;
  sf::FloatRect $grid_bounds;
  std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> $converter;
  sf::FloatRect $text_bounds;

  SFMLRender(sf::RenderWindow& window);

  // disable copy
  SFMLRender(SFMLRender &other) = delete;

  sf::Sprite &get_text_sprite(wchar_t tile);
  void resize_grid(int new_size, Panel &panel_out);
  void render_grid(const std::wstring &text, sf::Color default_fg, sf::Color default_bg, float x, float y);
  void render_text(const std::wstring &text, sf::Color default_fg, sf::Color default_bg, float x, float y);

  void draw(Panel &panel, float x_offset=0.0f, float y_offset=0.0f);
  void draw_sprite(sf::Sprite &sprite, sf::Shader *shader);
  void center_panel(Panel &panel);

  std::optional<sf::Event> poll_event() {
    return $window.pollEvent();
  }

  void close() { return $window.close(); }

  bool is_open() { return $window.isOpen(); }

  int font_size() { return $map_font_size; }
  void clear() { $window.clear(); }
  void display() { $window.display(); }
  bool mouse_position(Panel &panel, Point &out);
  void clear_cache();
  static void init_terminal();
};
