#include "textures.hpp"
#include <SFML/Graphics/Sprite.hpp>
#include <SFML/Graphics/Texture.hpp>
#include <SFML/Graphics/RenderWindow.hpp>
#include <SFML/System.hpp>
#include <SFML/Audio.hpp>
#include <SFML/Window/Event.hpp>
#include "dbc.hpp"
#include <fmt/core.h>
#include <unistd.h>

bool SHADER_RELOAD = true;

void Handle_events(sf::RenderWindow &window) {
  // is this a main event loop
  while (const auto event = window.pollEvent()) {
    if(event->is<sf::Event::Closed>()) {
      window.close();
    } else if(const auto* key = event->getIf<sf::Event::KeyPressed>()) {
      using KEY = sf::Keyboard::Scan;
      if(key->scancode == KEY::Escape || key->scancode == KEY::Space) {
        SHADER_RELOAD = true;
      }
    }
  }
}

int main(int argc, char *argv[]) {
  int opt = 0;
  bool load_sprite = false;
  std::string sprite_name;
  std::string frag_name;
  sf::Clock clock;
  sf::Shader shader;
  sf::RectangleShape rect;
  textures::SpriteTexture sprite_texture;
  sf::Vector2f u_resolution{720.0, 720.0};
  sf::Vector2u screen_resolution{720, 720};

  textures::init();

  dbc::check(sf::Shader::isAvailable(), "You apparently are a time traveler from the 80s who doesn't have shaders.");

  while((opt = getopt(argc, argv, "hs:f:x:y:")) != -1) {
    switch(opt) {
      case 's':
        sprite_name = optarg;
        load_sprite = true;
        break;
      case 'f':
        frag_name = optarg;
        break;
      case 'x':
        screen_resolution.x = std::atoi(optarg);
        u_resolution.x = screen_resolution.x;
        break;
      case 'y':
        screen_resolution.y = std::atoi(optarg);
        u_resolution.y = screen_resolution.y;
        break;
      case 'h':
        fmt::println(
            "fragviewer is a simple tool to play fragment shaders.\n"
            "USAGE: fragviewer [-x size_x -y size_y] [-s sprite_name] -f shader.frag");
        return 0;
      default:
        fmt::println("USAGE: fragviewer [-x size_x -y size_y] [-s sprite_name] -f shader.frag");
        return -1;
    }
  }

  fmt::println("sprite is", sprite_name);

  dbc::check(frag_name != "", "You must set the -f shader.frag option.");

  sf::RenderWindow window(sf::VideoMode(screen_resolution), "SFML Frag Shader Viewer");
  window.setFramerateLimit(60);
  window.setVerticalSyncEnabled(true);

  if(load_sprite) {
    sprite_texture = textures::get_sprite(sprite_name);
    sprite_texture.sprite->setPosition({0,0});
    auto bounds = sprite_texture.sprite->getLocalBounds();
    sf::Vector2f scale{u_resolution.x / bounds.size.x,
      u_resolution.y / bounds.size.y};
    sprite_texture.sprite->setScale(scale);
    shader.setUniform("source", sf::Shader::CurrentTexture);
  } else {
    rect.setPosition({0,0});
    rect.setSize(u_resolution);
  }


  while(window.isOpen()) {
    Handle_events(window);

    if(SHADER_RELOAD) {
      bool good_shader = shader.loadFromFile(frag_name, sf::Shader::Type::Fragment);

      if(!good_shader) {
        fmt::print("!!!!!! failed to load shader {}\n", frag_name);
      }

      shader.setUniform("u_resolution", u_resolution);
      SHADER_RELOAD = false;
    }

    sf::Time u_time = clock.getElapsedTime();
    sf::Vector2i mouse_at = sf::Mouse::getPosition(window);
    sf::Vector2f u_mouse{float(mouse_at.x), float(mouse_at.y)};

    shader.setUniform("u_mouse", u_mouse);
    shader.setUniform("u_time", u_time.asSeconds());

    window.clear();

    if(load_sprite) {
      window.draw(*sprite_texture.sprite, &shader);
    } else {
      window.draw(rect, &shader);
    }

    window.display();
  }
}
