#include "lel.hpp"
#include <fmt/core.h>
#include "dbc.hpp"
#include <numeric>

#include "lel_parser.cpp"

namespace lel {

  Parser::Parser(int x, int y, int width, int height) :
    grid_x(x),
    grid_y(y),
    grid_w(width),
    grid_h(height),
    cur(0, 0)
  {
  }

  Parser::Parser() : cur(0, 0) { }

  void Parser::position(int x, int y, int width, int height) {
    grid_x = x;
    grid_y = y;
    grid_w = width;
    grid_h = height;
  }

  void Parser::id(std::string name) {
    if(name != "_") {
      dbc::check(!cells.contains(name),
          fmt::format("duplicate cell name {}", name));
      cells.insert_or_assign(name, cur);
    }

    cur = {cur.col + 1, cur.row};
    auto& row = grid.back();
    row.push_back(name);
  }

  void Parser::finalize() {
    size_t rows = grid.size();
    int cell_height = grid_h / rows;

    for(auto& row : grid) {
      size_t columns = row.size();
      int cell_width = grid_w / columns;
      dbc::check(cell_width > 0, "invalid cell width calc");
      dbc::check(cell_height > 0, "invalid cell height calc");

      for(auto& name : row) {
        if(name == "_") continue;
        auto& cell = cells.at(name);

        // ZED: getting a bit hairy but this should work
        if(cell.percent) {
          // when percent mode we have to take unset to 100%
          if(cell.max_w == 0) cell.max_w = 100;
          if(cell.max_h == 0) cell.max_h = 100;
          cell.max_w *= cell_width * 0.01;
          cell.max_h *= cell_height * 0.01;
        } else {
          if(cell.max_w == 0) cell.max_w = cell_width;
          if(cell.max_h == 0) cell.max_h = cell_height;
        }

        cell.w = cell.expand ? std::min(cell.max_w, grid_w) : std::min(cell_width, cell.max_w);
        cell.h = cell.expand ? std::min(cell.max_h, grid_h) : std::min(cell_height, cell.max_h);

        dbc::check(cell.h > 0, fmt::format("invalid height cell {}", name));
        dbc::check(cell.w > 0, fmt::format("invalid width cell {}", name));

        cell.x = grid_x + (cell.col * cell_width);
        cell.y = grid_y + (cell.row * cell_height);

        // keep the midpoint since it is used a lot
        cell.mid_x = std::midpoint(cell.x, cell.x + cell.w);
        cell.mid_y = std::midpoint(cell.y, cell.y + cell.h);

        // perform alignments
        if(cell.right) cell.x += cell_width - cell.w;
        if(cell.bottom) cell.y += cell_height - cell.h;
        if(cell.center) {
          cell.x = cell.mid_x - cell.w / 2;
          cell.y = cell.mid_y - cell.h / 2;
        }
      }
    }
  }

  void Parser::reset() {
    cur = {0, 0};
    grid.clear();
    cells.clear();
  }

  std::optional<std::string> Parser::hit(int x, int y) {
    for(auto& [name, cell] : cells) {
      if((x >= cell.x && x <= cell.x + cell.w) &&
          (y >= cell.y && y <= cell.y + cell.h)) {
        return name;
      }
    }

    return std::nullopt;
  }

  Cell center(int width, int height, Cell &parent) {
    Cell copy = parent;

    copy.x = parent.mid_x - width / 2;
    copy.y = parent.mid_y - height / 2;
    copy.w = width;
    copy.h = height;

    return copy;
  }
}