#pragma once

#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>

#include <fmt/ostream.h>

#include <optional>
#include <ostream>
#include <string>
#include <vector>

enum class Alignment {
  START,
  END,
  CENTER,
  LEFT = START,
  TOP = START,
  RIGHT = END,
  BOTTOM = END
};
std::ostream& operator<<(std::ostream& os, Alignment align);
struct PositionInfo {
  PositionInfo();

  int x;
  int y;
  Alignment align_h;
  Alignment align_v;
};

class Widget {
public:
  Widget(SDL_Renderer* renderer);
  virtual ~Widget();

  virtual void set_width(int width, bool preserve_aspect_ratio = true);
  virtual void set_height(int height, bool preserve_aspect_ratio = true);
  virtual void set_position(int x, int y);
  virtual void set_alignment(Alignment align_h, Alignment align_v);

  int get_width();
  int get_height();
  const PositionInfo& get_position();

  virtual void draw() = 0;

protected:
  void recalculate_pos();

  SDL_Renderer* renderer;
  SDL_Rect rect;
  PositionInfo pos;
};

class TextureWidget : public Widget {
public:
  TextureWidget(SDL_Renderer* renderer, std::optional<SDL_Texture*> texture);
  ~TextureWidget();

  virtual void draw() override;

  void update_texture(SDL_Texture* new_texture);

private:
  void update_dimensions_from_texture(SDL_Texture* texture);
  std::optional<SDL_Texture*> texture;
};

class ImageWidget : public TextureWidget {
public:
  ImageWidget(SDL_Renderer* renderer, const std::string& path);
};

class TextWidget : public TextureWidget {
public:
  TextWidget(SDL_Renderer* renderer, TTF_Font* font,
             const std::string& initial_text = "");
  ~TextWidget();

  void update_text(const std::string& new_text);

protected:
  TTF_Font* font;

  std::string text;

  SDL_Texture* generate_text(const std::string& text);
};

class ListWidget : public Widget {
public:
  ListWidget(SDL_Renderer* renderer, int element_height,
             Alignment element_alignment);
  virtual ~ListWidget();

  void add_element(Widget* element);

  virtual void draw() override;

  void select_next();
  void select_prev();
  void select(size_t n);
  size_t get_selection();

protected:
  int element_height;
  Alignment element_alignment;

  size_t selection;

  std::vector<Widget*> elements;

  void place_element(Widget* element, int index);
};