#include <chrono>                   // for operator""s, chrono_literals
#include <thread>                   // for sleep_for
#include <fmt/core.h>
#include <filesystem>
#include "panel.hpp"
#include "color.hpp"
#include "render.hpp"
#include "dbc.hpp"
#include <SFML/Graphics/Image.hpp>
#include <ftxui/screen/color.hpp>
#include <ftxui/screen/terminal.hpp>
#include <iostream>
#include <fcntl.h>
#include <io.h>
#include <vector>

using namespace std::chrono_literals;
using namespace fmt;
using std::string, std::cout, std::vector;

struct HSVColor {
  unsigned long h = 0;
  unsigned long s = 0;
  unsigned long v = 0;
};

struct RGBColor {
  unsigned long r = 0;
  unsigned long g = 0;
  unsigned long b = 0;
};

// taken from https://github.com/python/cpython/blob/3.9/Lib/colorsys.py#L140
HSVColor rgb_to_hsv(sf::Color rgb) {
  float r = rgb.r / 255.0f;
  float g = rgb.g / 255.0f;
  float b = rgb.b / 255.0f;

  float maxc = std::max({rgb.r, rgb.g, rgb.b});
  float minc = std::min({rgb.r, rgb.g, rgb.b});
  float v = maxc;

  //  if minc == maxc:
  if(minc == maxc) {
    // no hue no sat, so gray with value
    return {0, 0, uint8_t(v * 255.0)};
  }

  float s = (maxc - minc) / maxc;
  float rc = (maxc - r) / (maxc - minc);
  float gc = (maxc - g) / (maxc - minc);
  float bc = (maxc - b) / (maxc - minc);
  float h = 0.0f;

  if(r == maxc) {
    h = bc-gc;
  } else if(g == maxc) {
    h = 2.0 + rc - bc;
  } else {
    h = 4.0 + gc - rc;
    h = std::fmod((h/6.0), 1.0);
  }

  return {uint8_t(h * 255.0f), uint8_t(s * 255.0f), uint8_t(v * 255.0f)};
}

int main(int argc, char *argv[]) {
  ftxui::Terminal::SetColorSupport(ftxui::Terminal::Color::TrueColor);

  _setmode(_fileno(stdout), _O_U16TEXT);
  dbc::check(argc == 2, "USAGE: img2ansi <image_file>");

  string image_file(argv[1]);

  println("LOADING IMAGE: {}", image_file);

  // load the image from argv
  sf::Image image;
  image.loadFromFile(image_file);

  // divide the image into cells
  auto size = image.getSize();

  const Point cell = {3, 6};

  println("IMAGE SIZE {},{}", size.x, size.y);
  RGBColor avg{0,0,0};

  typedef vector<RGBColor> ColorRow;
  vector<ColorRow> colors(size.x / cell.x, ColorRow(size.y / cell.y));

  // LOL, so bad but just the start
  for(unsigned int i = 0; i < size.x / cell.x; i++) {
    for(unsigned int j = 0; j < size.y / cell.y; j++) {

      // sum the cell
      for(unsigned int x = 0; x < cell.x ; x++) {
        for(unsigned int y = 0; y < cell.y ; y++) {
          auto pixel = image.getPixel((i*cell.x) + x, (j * cell.y) + y);

          avg.r += pixel.r;
          avg.g += pixel.g;
          avg.b += pixel.b;
        }
      }

      // average it for the cell size
      RGBColor color = {
        avg.r / int(cell.x * cell.y),
        avg.g / int(cell.x * cell.y),
        avg.b / int(cell.x * cell.y),
      };

      // add it
      colors[i][j] = color;

      // reset
      avg = {0,0,0};
    }
  }

  Canvas drawing;
  // create a grid panel to hold the cells
  Panel panel(0, 0, 0, 0, true);
  SFMLRender renderer;
  renderer.resize_grid(26, panel);

  drawing = Canvas(panel.width * 2, panel.height * 4);
  panel.resize(240,240);

  panel.set_renderer(Renderer([&]{
    for(size_t x = 0; x < colors.size(); x++) {
      for(size_t y = 0; y < colors[0].size(); y++) {
        auto color = colors[x][y];
        ftxui::Color block = ftxui::Color::RGB(color.r, color.g, color.b);
        drawing.DrawText(x * 2, y * 4, "█", block);
      }
    }
    return ftxui::canvas(drawing);
  }));

  sf::Event event;

  int start_x = 0;
  int start_y = 0;
  int end_x = 600;
  int end_y = 300;
  int cur_x = start_x;
  int cur_y = start_y;

  sf::Texture texture;
  texture.loadFromFile(image_file);
  sf::Sprite sprite(texture);

  panel.render();

  while(renderer.is_open()) {
    cur_x = cur_x < end_x ? cur_x + 1 : start_x;
    cur_y = cur_y < end_y ? cur_y + 1 : start_y;

    // sprite.setPosition(cur_x, cur_y);
    // renderer.$window.draw(sprite);
    // renderer.display();

    renderer.draw(panel, cur_x, cur_y);
    renderer.display();

    while(renderer.poll_event(event)) {
      if(event.type == sf::Event::Closed) {
        renderer.close();
      }
    }
  }

  return 0;
}
