#pragma once

#include <memory>
#include <unordered_map>
#include "graphics/textures.hpp"
#include <SFML/Graphics/RenderWindow.hpp>
#include <guecs/ui.hpp>
#include "graphics/camera.hpp"
#include <functional>
#include "graphics/animation.hpp"
#include "game/components.hpp"

namespace scene {
  using std::shared_ptr;
  using namespace textures;

  struct Element {
    std::string name;
    textures::SpriteTexture st;
    std::optional<animation::Animation> anim;
    std::string cell;
    sf::Vector2f scale{1.0f, 1.0f};
    sf::Vector2f pos{0.0f, 0.0f};
    bool at_mid=false;
    bool flipped=false;
    std::shared_ptr<sf::Shader> effect = nullptr;
    sf::Text text;
  };

  struct Engine {
    sf::Clock $clock;
    guecs::UI $ui;
    components::AnimatedScene& $scene;
    std::string $layout;
    std::unordered_map<std::string, int> $actor_name_ids;
    std::vector<Element> $fixtures;
    std::vector<Element> $actors;
    cinematic::Camera $camera{{SCENE_VIEW_WIDTH, SCENE_VIEW_HEIGHT}, "scene"};

    Engine(components::AnimatedScene& scene);

    void init();
    void render(sf::RenderTexture& view);
    void update();
    bool mouse(float x, float y, guecs::Modifiers mods);
    void attach_text(const std::string& actor, const std::string& text);

    nlohmann::json& buttons() { return $scene.buttons; };
    lel::Cell& button_cell() { return $ui.cell_for("buttons"); }

    Element config_scene_element(nlohmann::json& config, bool duped);

    sf::Vector2f position_sprite(textures::SpriteTexture& st, const std::string& cell_name, sf::Vector2f scale, bool at_mid, float x_diff=0.0f, float y_diff=0.0f);

    void move_actor(const std::string& actor, const std::string& cell_name);
    void animate_actor(const std::string& actor, const std::string& form);
    void apply_effect(const std::string& actor, const std::string& shader);
    Element& actor_config(const std::string& actor);
    void zoom(const std::string& actor, const std::string& style, float scale=0.9f);
    void zoom(float mid_x, float mid_y, const std::string& style, float scale);
    void reset(sf::RenderTexture& view);
    void set_end_cb(std::function<void()> cb);
  };
}
