#pragma once
#include <vector>
#include <queue>
#include <string>
#include <array>
#include <numeric>
#include <algorithm>
#include <fmt/core.h>
#include "point.hpp"
#include "rand.hpp"
#include "dbc.hpp"

/*
 * # What is This Shit?
 *
 * Announcing the Shape Iterators, or "shiterators" for short.  The best shite
 * for C++ for-loops since that [one youtube
 * video](https://www.youtube.com/watch?v=rX0ItVEVjHc) told everyone to
 * recreate SQL databases with structs. You could also say these are Shaw's
 * Iterators, but either way they are the _shite_. Or are they shit? You decide.
 * Maybe they're "shite"?
 *
 * A shiterator is a simple generator that converts 2D shapes into a 1D stream
 * of x/y coordinates.  You give it a matrix, some parameters like start, end,
 * etc. and each time you call `next()` you get the next viable x/y coordinate to
 * complete the shape. This makes them far superior to _any_ existing for-loop
 * technology because shiterators operate _intelligently_ in shapes.  Other
 * [programming pundits](https://www.youtube.com/watch?v=tD5NrevFtbU) will say
 * their 7000 line "easy to maintain" switch statements are better at drawing
 * shapes, but they're wrong.  My way of making a for-loop do stuff is vastly
 * superior because it doesn't use a switch _or_ a virtual function _or_
 * inheritance at all.  That means they have to be the _fastest_. Feel free to run
 * them 1000 times and bask in the glory of 1 nanosecond difference performance.
 *
 * It's science and shite.
 *
 * More importantly, shiterators are simple and easy to use.  They're so easy to
 * use you _don't even use the 3rd part of the for-loop_.  What? You read that right,
 * not only have I managed to eliminate _both_ massive horrible to maintain switches,
 * and also avoided virtual functions, but I've also _eliminated one entire part
 * of the for-loop_. This obviously makes them way faster than other inferior
 * three-clause-loop-trash.  Just look at this comparison:
 *
 * ```cpp
 * for(it = trash.begin(); it != trash.end(); it++) {
 *     std::cout << it << std::endl;
 * }
 * ```
 *
 * ```cpp
 * for(each_cell it{mat}; it.next();) {
 *    std::cout << mat[it.y][it.x] << std::endl;
 * }
 * ```
 *
 * Obviously this will outperform _any_ iterator invented in the last 30 years, but the best
 * thing about shiterators is their composability and ability to work simultaneously across
 * multiple matrices in one loop:
 *
 * ```cpp
 * for(line it{start, end}; it.next();) {
 *    for(compass neighbor{walls, it.x, it.y}; neighbor.next();) {
 *        if(walls[neighbor.y][neighbor.x] == 1) {
 *          wall_update[it.y][it.x] = walls[it.y][it.x] + 10;
 *        }
 *    }
 * }
 * ```
 *
 * This code sample (maybe, because I didn't run it) draws a line from
 * `start` to `end` then looks at each neighbor on a compass (north, south, east, west)
 * at each point to see if it's set to 1.  If it is then it copies that cell over to
 * another matrix with +10.  Why would you need this?  Your Wizard just shot a fireball
 * down a corridor and you need to see if anything in the path is within 1 square of it.
 *
 * You _also_ don't even need to use a for-loop.  Yes, you can harken back to the old
 * days when we did everything RAW inside a Duff's Device between a while-loop for
 * that PERFORMANCE because who cares about maintenance? You're a game developer! Tests?
 * Don't need a test if it runs fine on Sony Playstation only.  Maintenance? You're moving
 * on to the next project in two weeks anyway right?!  Use that while-loop and a shiterator
 * to really help that next guy:
 *
 * ```cpp
 * box it{walls, center_x, center_y, 20};
 * while(it.next()) {
 *     walls[it.y][it.x] = 1;
 * }
 * ```
 *
 * ## Shiterator "Guarantees"
 *
 * Just like Rust [guarantees no memory leaks](https://github.com/pop-os/cosmic-comp/issues/1133),
 * a shiterator tries to ensure a few things, if it can:
 *
 * 1. All x/y values will be within the Matrix you give it. The `line` shiterator doesn't though.
 * 2. They try to not store anything and only calculate the math necessary to linearlize the shape.
 * 3. You can store them and incrementally call next to get the next value.
 * 4. You should be able to compose them together on the same Matrix or different matrices of the same dimensions.
 * 5. Most of them will only require 1 for-loop, the few that require 2 only do this so you can draw the inside of a shape.  `circle` is like this.
 * 6. They don't assume any particular classes or require subclassing.  As long as the type given enables `mat[y][x]` (row major) access then it'll work.
 * 7. The matrix given to a shiterator isn't actually attached to it, so you can use one matrix to setup an iterator, then apply the x/y values to any other matrix of the same dimensions. Great for smart copying and transforming.
 * 8. More importantly, shiterators _do not return any values from the matrix_. They only do the math for coordinates and leave it to you to work your matrix.
 *
 * These shiterators are used all over the game to do map rendering, randomization, drawing, nearly everything that involves a shape.
 *
 * ## Algorithms I Need
 *
 * I'm currently looking for a few algorithms, so if you know how to do these let me know:
 *
 * 1. _Flood fill_  This turns out to be really hard because most algorithms require keeping track of visited cells with a queue, recursion, etc.
 * 2. _Random rectangle fill_  I have something that mostly works but it's really only random across each y-axis, then separate y-axes are randomized.
 * 3. _Dijkstra Map_ I have a Dijkstra algorithm but it's not in this style yet.  Look in `worldbuilder.cpp` for my current implementation.
 * 4. _Viewport_ Currently working on this but I need to have a rectangle I can move around as a viewport.
 *
 *
 * ## Usage
 *
 * Check the `matrix.hpp` for an example if you want to make it more conventient for your own type.
 *
 * ## Thanks
 *
 * Special thanks to Amit and hirdrac for their help with the math and for
 * giving me the initial idea. hirdrac doesn't want to be held responsible for
 * this travesty but he showed me that you can do iteration and _not_ use the
 * weird C++ iterators.  Amit did a lot to show me how to do these calculations
 * without branching.  Thanks to you both--and to everyone else--for helping me while I
 * stream my development.
 *
 * ### SERIOUS DISCLAIMER
 *
 * I am horribly bad at trigonometry and graphics algorithms, so if you've got an idea to improve them
 * or find a bug shoot me an email at help@learncodethehardway.com.
 */
namespace shiterator { using std::vector, std::queue, std::array; using
  std::min, std::max, std::floor;

  template<typename T>
  using BaseRow = vector<T>;

  template<typename T>
  using Base = vector<BaseRow<T>>;

  template<typename T>
  inline Base<T> make(size_t width, size_t height) {
    Base<T> result(height, BaseRow<T>(width));
    return result;
  }

  /*
   * Just a quick thing to reset a matrix to a value.
   */
  template<typename MAT, typename VAL>
  inline void assign(MAT &out, VAL new_value) {
    for(auto &row : out) {
      row.assign(row.size(), new_value);
    }
  }


  /*
   * Tells you if a coordinate is in bounds of the matrix
   * and therefore safe to use.
   */
  template<typename MAT>
  inline bool inbounds(MAT &mat, size_t x, size_t y) {
    // since Point.x and Point.y are size_t any negatives are massive
    return (y < mat.size()) && (x < mat[0].size());
  }

  /*
   * Gives the width of a matrix. Assumes row major (y/x)
   * and vector API .size().
   */
  template<typename MAT>
  inline size_t width(MAT &mat) {
    return mat[0].size();
  }

  /*
   * Same as shiterator::width but just the height.
   */
  template<typename MAT>
  inline size_t height(MAT &mat) {
    return mat.size();
  }

  /*
   * These are internal calculations that help
   * with keeping track of the next x coordinate.
   */
  inline size_t next_x(size_t x, size_t width) {
    return (x + 1) * ((x + 1) < width);
  }

  /*
   * Same as next_x but updates the next y coordinate.
   * It uses the fact that when x==0 you have a new
   * line so increment y.
   */
  inline size_t next_y(size_t x, size_t y) {
    return y + (x == 0);
  }

  /*
   * Figures out if you're at the end of the shape,
   * which is usually when y > height.
   */
  inline bool at_end(size_t y, size_t height) {
    return y < height;
  }

  /*
   * Determines if you're at the end of a row.
   */
  inline bool end_row(size_t x, size_t width) {
    return x == width - 1;
  }

  /*
   * Most basic shiterator. It just goes through
   * every cell in the matrix in linear order
   * with not tracking of anything else.
   */
  template<typename MAT>
  struct each_cell_t {
    size_t x = ~0;
    size_t y = ~0;
    size_t width = 0;
    size_t height = 0;

    each_cell_t(MAT &mat)
    {
      height = shiterator::height(mat);
      width = shiterator::width(mat);
    }

    bool next() {
      x = next_x(x, width);
      y = next_y(x, y);
      return at_end(y, height);
    }
  };

  /*
   * This is just each_cell_t but it sets
   * a boolean value `bool row` so you can
   * tell when you've reached the end of a
   * row.  This is mostly used for printing
   * out a matrix and similar just drawing the
   * whole thing with its boundaries.
   */
  template<typename MAT>
  struct each_row_t {
    size_t x = ~0;
    size_t y = ~0;
    size_t width = 0;
    size_t height = 0;
    bool row = false;

    each_row_t(MAT &mat) {
      height = shiterator::height(mat);
      width = shiterator::width(mat);
    }

    bool next() {
      x = next_x(x, width);
      y = next_y(x, y);
      row = end_row(x, width);
      return at_end(y, height);
    }
  };

  /*
   * This is a CENTERED box, that will create
   * a centered rectangle around a point of a
   * certain dimension. This kind of needs a
   * rewrite but if you want a rectangle from
   * a upper corner then use rectangle_t type.
   *
   * Passing 1 parameter for the size will make
   * a square.
   */
  template<typename MAT>
  struct box_t {
    size_t from_x;
    size_t from_y;
    size_t x = 0; // these are set in constructor
    size_t y = 0; // again, no fancy ~ trick needed
    size_t left = 0;
    size_t top = 0;
    size_t right = 0;
    size_t bottom = 0;

    box_t(MAT &mat, size_t at_x, size_t at_y, size_t size) :
      box_t(mat, at_x, at_y, size, size) {
      }

    box_t(MAT &mat, size_t at_x, size_t at_y, size_t width, size_t height) :
      from_x(at_x), from_y(at_y)
    {
      size_t h = shiterator::height(mat);
      size_t w = shiterator::width(mat);

      // keeps it from going below zero
      // need extra -1 to compensate for the first next()
      left = max(from_x, width) - width;
      x = left - 1;  // must be -1 for next()
      // keeps it from going above width
      right = min(from_x + width + 1, w);

      // same for these two
      top = max(from_y, height) - height;
      y = top - (left == 0);
      bottom = min(from_y + height + 1, h);
    }

    bool next() {
      // calc next but allow to go to 0 for next
      x = next_x(x, right);
      // x will go to 0, which signals new line
      y = next_y(x, y);  // this must go here
      // if x==0 then this moves it to min_x
      x = max(x, left);
      // and done

      return at_end(y, bottom);
    }

    /*
     * This was useful for doing quick lighting
     * calculations, and I might need to implement
     * it in other shiterators. It gives the distance
     * to the center from the current x/y.
     */
    float distance() {
      int dx = from_x - x;
      int dy = from_y - y;

      return sqrt((dx * dx) + (dy * dy));
    }
  };

  /*
   * Stupid simple compass shape North/South/East/West.
   * This comes up a _ton_ when doing searching, flood
   * algorithms, collision, etc.  Probably not the
   * fastest way to do it but good enough.
   */
  template<typename MAT>
  struct compass_t {
    size_t x = 0; // these are set in constructor
    size_t y = 0; // again, no fancy ~ trick needed
    array<int, 4> x_dirs{0, 1, 0, -1};
    array<int, 4> y_dirs{-1, 0, 1, 0};
    size_t max_dirs=0;
    size_t dir = ~0;

    compass_t(MAT &mat, size_t x, size_t y) :
      x(x), y(y)
    {
      array<int, 4> x_in{0, 1, 0, -1};
      array<int, 4> y_in{-1, 0, 1, 0};

      for(size_t i = 0; i < 4; i++) {
        int nx = x + x_in[i];
        int ny = y + y_in[i];
        if(shiterator::inbounds(mat, nx, ny)) {
          x_dirs[max_dirs] = nx;
          y_dirs[max_dirs] = ny;
          max_dirs++;
        }
      }
    }

    bool next() {
      dir++;
      if(dir < max_dirs) {
        x = x_dirs[dir];
        y = y_dirs[dir];
        return true;
      } else {
        return false;
      }
    }
  };

  /*
   * Draws a line from start to end using a algorithm from
   * https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm
   * No idea if the one I picked is best but it's the one
   * that works in the shiterator requirements and produced
   * good results.
   *
   * _WARNING_: This one doesn't check if the start/end are
   * within your Matrix, as it's assumed _you_ did that
   * already.
   */
  struct line {
    int x;
    int y;
    int x1;
    int y1;
    int sx;
    int sy;
    int dx;
    int dy;
    int error;

    line(Point start, Point end) :
        x(start.x), y(start.y),
        x1(end.x), y1(end.y)
    {
      dx = std::abs(x1 - x);
      sx = x < x1 ? 1 : -1;
      dy = std::abs(y1 - y) * -1;
      sy = y < y1 ? 1 : -1;
      error = dx + dy;
    }

    bool next() {
      if(x != x1 || y != y1) {
        int e2 = 2 * error;

        if(e2 >= dy) {
          error = error + dy;
          x = x + sx;
        }

        if(e2 <= dx) {
          error = error + dx;
          y = y + sy;
        }
        return true;
      } else {
        return false;
      }
    }
  };

  /*
   * Draws a simple circle using a fairly naive algorithm
   * but one that actually worked.  So, so, so, so many
   * circle drawing algorithms described online don't work
   * or are flat wrong.  Even the very best I could find
   * did overdrawing of multiple lines or simply got the
   * math wrong.  Keep in mind, _I_ am bad at this trig math
   * so if I'm finding errors in your circle drawing then
   * you got problems.
   *
   * This one is real simple, and works. If you got better
   * then take the challenge but be ready to get it wrong.
   */
  template<typename MAT>
  struct circle_t {
    float center_x;
    float center_y;
    float radius = 0.0f;
    int y = 0;
    int dx = 0;
    int dy = 0;
    int left = 0;
    int right = 0;
    int top = 0;
    int bottom = 0;
    int width = 0;
    int height = 0;

    circle_t(MAT &mat, Point center, float radius) :
      center_x(center.x), center_y(center.y), radius(radius)
    {
      width = shiterator::width(mat);
      height = shiterator::height(mat);
      top = max(int(floor(center_y - radius)), 0);
      bottom = min(int(floor(center_y + radius)), height - 1);

      y = top;
    }

    bool next() {
      y++;
      if(y <= bottom) {
        dy = y - center_y;
        dx = floor(sqrt(radius * radius - dy * dy));
        left = max(0, int(center_x) - dx);
        right = min(width, int(center_x) + dx + 1);
        return true;
      } else {
        return false;
      }
    }
  };

  /*
   * Basic rectangle shiterator, and like box and rando_rect_t you can
   * pass only 1 parameter for size to do a square.
   */
  template<typename MAT>
  struct rectangle_t {
    int x;
    int y;
    int top;
    int left;
    int width;
    int height;
    int right;
    int bottom;

    rectangle_t(MAT &mat, size_t start_x, size_t start_y, size_t size) :
      rectangle_t(mat, start_x, start_y, size, size) {
      }

    rectangle_t(MAT &mat, size_t start_x, size_t start_y, size_t width, size_t height) :
      top(start_y),
      left(start_x),
      width(width),
      height(height)
    {
      size_t h = shiterator::height(mat);
      size_t w = shiterator::width(mat);
      y = start_y - 1;
      x = left - 1;  // must be -1 for next()
      right = min(start_x + width, w);

      y = start_y;
      bottom = min(start_y + height, h);
    }

    bool next() {
      x = next_x(x, right);
      y = next_y(x, y);
      x = max(x, left);
      return at_end(y, bottom);
    }
  };

  /*
   * WIP:  This one is used to place entities randomly but
   * could be used for effects like random destruction of floors.
   * It simply "wraps" the rectangle_t but randomizes the x/y values
   * using a random starting point.  This makes it random across the
   * x-axis but only partially random across the y.
   */
  template<typename MAT>
  struct rando_rect_t {
    int x;
    int y;
    int x_offset;
    int y_offset;
    rectangle_t<MAT> it;

    rando_rect_t(MAT &mat, size_t start_x, size_t start_y, size_t size) :
      rando_rect_t(mat, start_x, start_y, size, size) {
      }

    rando_rect_t(MAT &mat, size_t start_x, size_t start_y, size_t width, size_t height) :
      it{mat, start_x, start_y, width, height}
    {
      x_offset = Random::uniform(0, int(width));
      y_offset = Random::uniform(0, int(height));
    }

    bool next() {
      bool done = it.next();
      x = it.left + ((it.x + x_offset) % it.width);
      y = it.top + ((it.y + y_offset) % it.height);
      return done;
    }
  };

  /*
   * BROKEN: I'm actually not sure what I'm trying to
   * do here yet.
   */
  template<typename MAT>
  struct viewport_t {
    Point start;
    // this is the point in the map
    size_t x;
    size_t y;
    // this is the point inside the box, start at 0
    size_t view_x = ~0;
    size_t view_y = ~0;
    // viewport width/height
    size_t width;
    size_t height;

    viewport_t(MAT &mat, Point start, int max_x, int max_y) :
      start(start),
      x(start.x-1),
      y(start.y-1)
    {
      width = std::min(size_t(max_x), shiterator::width(mat) - start.x);
      height = std::min(size_t(max_y), shiterator::height(mat) - start.y);
      fmt::println("viewport_t max_x, max_y {},{} vs matrix {},{}, x={}, y={}",
          max_x, max_y, shiterator::width(mat), shiterator::height(mat), x, y);
    }

    bool next() {
      y = next_y(x, y);
      x = next_x(x, width);
      view_x = next_x(view_x, width);
      view_y = next_y(view_x, view_y);
      return at_end(y, height);
    }
  };

}
