#ifndef SYNTAX_EXTRAS_H
#define SYNTAX_EXTRAS_H

#include "io/knot.h"
#include "syntax/decl.h"
#include "utils/utils.h"

inline static const std::vector<std::pair<std::string, uint32_t>> color_map = {
    {"AliceBlue", 0xF0F8FF},
    {"AntiqueWhite", 0xFAEBD7},
    {"Aqua", 0x00FFFF},
    {"Aquamarine", 0x7FFFD4},
    {"Azure", 0xF0FFFF},
    {"Beige", 0xF5F5DC},
    {"Bisque", 0xFFE4C4},
    {"Black", 0x000000},
    {"BlanchedAlmond", 0xFFEBCD},
    {"Blue", 0x0000FF},
    {"BlueViolet", 0x8A2BE2},
    {"Brown", 0xA52A2A},
    {"BurlyWood", 0xDEB887},
    {"CadetBlue", 0x5F9EA0},
    {"Chartreuse", 0x7FFF00},
    {"Chocolate", 0xD2691E},
    {"Coral", 0xFF7F50},
    {"CornflowerBlue", 0x6495ED},
    {"Cornsilk", 0xFFF8DC},
    {"Crimson", 0xDC143C},
    {"Cyan", 0x00FFFF},
    {"DarkBlue", 0x00008B},
    {"DarkCyan", 0x008B8B},
    {"DarkGoldenRod", 0xB8860B},
    {"DarkGray", 0xA9A9A9},
    {"DarkGrey", 0xA9A9A9},
    {"DarkGreen", 0x006400},
    {"DarkKhaki", 0xBDB76B},
    {"DarkMagenta", 0x8B008B},
    {"DarkOliveGreen", 0x556B2F},
    {"DarkOrange", 0xFF8C00},
    {"DarkOrchid", 0x9932CC},
    {"DarkRed", 0x8B0000},
    {"DarkSalmon", 0xE9967A},
    {"DarkSeaGreen", 0x8FBC8F},
    {"DarkSlateBlue", 0x483D8B},
    {"DarkSlateGray", 0x2F4F4F},
    {"DarkSlateGrey", 0x2F4F4F},
    {"DarkTurquoise", 0x00CED1},
    {"DarkViolet", 0x9400D3},
    {"DeepPink", 0xFF1493},
    {"DeepSkyBlue", 0x00BFFF},
    {"DimGray", 0x696969},
    {"DimGrey", 0x696969},
    {"DodgerBlue", 0x1E90FF},
    {"FireBrick", 0xB22222},
    {"FloralWhite", 0xFFFAF0},
    {"ForestGreen", 0x228B22},
    {"Fuchsia", 0xFF00FF},
    {"Gainsboro", 0xDCDCDC},
    {"GhostWhite", 0xF8F8FF},
    {"Gold", 0xFFD700},
    {"GoldenRod", 0xDAA520},
    {"Gray", 0x808080},
    {"Grey", 0x808080},
    {"Green", 0x008000},
    {"GreenYellow", 0xADFF2F},
    {"HoneyDew", 0xF0FFF0},
    {"HotPink", 0xFF69B4},
    {"IndianRed", 0xCD5C5C},
    {"Indigo", 0x4B0082},
    {"Ivory", 0xFFFFF0},
    {"Khaki", 0xF0E68C},
    {"Lavender", 0xE6E6FA},
    {"LavenderBlush", 0xFFF0F5},
    {"LawnGreen", 0x7CFC00},
    {"LemonChiffon", 0xFFFACD},
    {"LightBlue", 0xADD8E6},
    {"LightCoral", 0xF08080},
    {"LightCyan", 0xE0FFFF},
    {"LightGoldenRodYellow", 0xFAFAD2},
    {"LightGray", 0xD3D3D3},
    {"LightGrey", 0xD3D3D3},
    {"LightGreen", 0x90EE90},
    {"LightPink", 0xFFB6C1},
    {"LightSalmon", 0xFFA07A},
    {"LightSeaGreen", 0x20B2AA},
    {"LightSkyBlue", 0x87CEFA},
    {"LightSlateGray", 0x778899},
    {"LightSlateGrey", 0x778899},
    {"LightSteelBlue", 0xB0C4DE},
    {"LightYellow", 0xFFFFE0},
    {"Lime", 0x00FF00},
    {"LimeGreen", 0x32CD32},
    {"Linen", 0xFAF0E6},
    {"Magenta", 0xFF00FF},
    {"Maroon", 0x800000},
    {"MediumAquaMarine", 0x66CDAA},
    {"MediumBlue", 0x0000CD},
    {"MediumOrchid", 0xBA55D3},
    {"MediumPurple", 0x9370DB},
    {"MediumSeaGreen", 0x3CB371},
    {"MediumSlateBlue", 0x7B68EE},
    {"MediumSpringGreen", 0x00FA9A},
    {"MediumTurquoise", 0x48D1CC},
    {"MediumVioletRed", 0xC71585},
    {"MidnightBlue", 0x191970},
    {"MintCream", 0xF5FFFA},
    {"MistyRose", 0xFFE4E1},
    {"Moccasin", 0xFFE4B5},
    {"NavajoWhite", 0xFFDEAD},
    {"Navy", 0x000080},
    {"OldLace", 0xFDF5E6},
    {"Olive", 0x808000},
    {"OliveDrab", 0x6B8E23},
    {"Orange", 0xFFA500},
    {"OrangeRed", 0xFF4500},
    {"Orchid", 0xDA70D6},
    {"PaleGoldenRod", 0xEEE8AA},
    {"PaleGreen", 0x98FB98},
    {"PaleTurquoise", 0xAFEEEE},
    {"PaleVioletRed", 0xDB7093},
    {"PapayaWhip", 0xFFEFD5},
    {"PeachPuff", 0xFFDAB9},
    {"Peru", 0xCD853F},
    {"Pink", 0xFFC0CB},
    {"Plum", 0xDDA0DD},
    {"PowderBlue", 0xB0E0E6},
    {"Purple", 0x800080},
    {"RebeccaPurple", 0x663399},
    {"Red", 0xFF0000},
    {"RosyBrown", 0xBC8F8F},
    {"RoyalBlue", 0x4169E1},
    {"SaddleBrown", 0x8B4513},
    {"Salmon", 0xFA8072},
    {"SandyBrown", 0xF4A460},
    {"SeaGreen", 0x2E8B57},
    {"SeaShell", 0xFFF5EE},
    {"Sienna", 0xA0522D},
    {"Silver", 0xC0C0C0},
    {"SkyBlue", 0x87CEEB},
    {"SlateBlue", 0x6A5ACD},
    {"SlateGray", 0x708090},
    {"SlateGrey", 0x708090},
    {"Snow", 0xFFFAFA},
    {"SpringGreen", 0x00FF7F},
    {"SteelBlue", 0x4682B4},
    {"Tan", 0xD2B48C},
    {"Teal", 0x008080},
    {"Thistle", 0xD8BFD8},
    {"Tomato", 0xFF6347},
    {"Turquoise", 0x40E0D0},
    {"Violet", 0xEE82EE},
    {"Wheat", 0xF5DEB3},
    {"White", 0xFFFFFF},
    {"WhiteSmoke", 0xF5F5F5},
    {"Yellow", 0xFFFF00},
    {"YellowGreen", 0x9ACD32},
};

// Add word under cursor to this

struct ExtraHighlighter {
  std::vector<uint32_t> colors;
  std::array<std::vector<uint32_t>, 50> lines;
  Trie<uint32_t> css_colors = Trie<uint32_t>();
  uint32_t start = 0;

  ExtraHighlighter() { css_colors.build(color_map, false); }

  void render(Knot *root, uint32_t n_start, std::string word, bool is_css) {
    start = n_start;
    for (auto &line : lines)
      line.clear();
    LineIterator *it = begin_l_iter(root, start);
    if (!it)
      return;
    uint32_t idx = 0;
    uint32_t len;
    char *line;
    while (idx < 50 && (line = next_line(it, &len))) {
      lines[idx].assign(len, UINT32_MAX - 1);
      uint32_t i = 0;
      while (i < len) {
        if (is_css) {
          std::optional<uint32_t> color;
          uint32_t color_len = css_colors.match(
              line, i, len, [](char c) { return isalnum(c) || c == '_'; },
              &color);
          if (color) {
            for (uint32_t j = 0; j < color_len; j++)
              lines[idx][i + j] = *color;
            i += color_len;
            continue;
          } else if (i + 5 < len && (line[i] == 'r' || line[i] == 'R') &&
                     (line[i + 1] == 'g' || line[i + 1] == 'G') &&
                     (line[i + 2] == 'b' || line[i + 2] == 'B')) {
            uint32_t start = i;
            i += 3;
            if (line[i] == 'a' || line[i] == 'A')
              i++;
            if (line[i] == '(') {
              i++;
              bool is_percent = false;
              std::string r = "";
              while (i < len && line[i] >= '0' && line[i] <= '9')
                r += line[i++];
              if (r.empty())
                continue;
              while (i < len &&
                     (line[i] == '.' || (line[i] >= '0' && line[i] <= '9')))
                i++;
              if (line[i] == '%') {
                is_percent = true;
                i++;
              }
              while (i < len && (line[i] == ',' || line[i] == ' '))
                i++;
              std::string g = "";
              while (i < len && line[i] >= '0' && line[i] <= '9')
                g += line[i++];
              if (g.empty())
                continue;
              while (i < len &&
                     (line[i] == '.' || (line[i] >= '0' && line[i] <= '9')))
                i++;
              while (i < len &&
                     (line[i] == ',' || line[i] == ' ' || line[i] == '%'))
                i++;
              std::string b = "";
              while (i < len && line[i] >= '0' && line[i] <= '9')
                b += line[i++];
              if (b.empty())
                continue;
              while (i < len &&
                     (line[i] == ',' || line[i] == ' ' || line[i] == '.' ||
                      line[i] == '/' || line[i] == '%' ||
                      (line[i] >= '0' && line[i] <= '9')))
                i++;
              if (i < len && line[i] == ')')
                i++;
              else
                continue;
              uint32_t rr, gg, bb;
              if (is_percent) {
                rr = std::stoul(r) * 255 / 100;
                gg = std::stoul(g) * 255 / 100;
                bb = std::stoul(b) * 255 / 100;
              } else {
                rr = std::stoul(r);
                gg = std::stoul(g);
                bb = std::stoul(b);
              }
              rr = rr > 255 ? 255 : rr;
              gg = gg > 255 ? 255 : gg;
              bb = bb > 255 ? 255 : bb;
              uint32_t color = (rr << 16) | (gg << 8) | bb;
              for (uint32_t j = start; j < i; j++)
                lines[idx][j] = color;
            }
            continue;
          } else if (i + 5 < len && (line[i] == 'h' || line[i] == 'H') &&
                     (line[i + 1] == 's' || line[i + 1] == 'S') &&
                     (line[i + 2] == 'l' || line[i + 2] == 'L')) {
            uint32_t start = i;
            i += 3;
            if (line[i] == 'a' || line[i] == 'A')
              i++;
            if (line[i] == '(') {
              i++;
              std::string h = "";
              std::string h_unit = "";
              enum unit : uint8_t { deg, grad, rad, turn };
              unit u = deg;
              bool negative = false;
              if (i < len && (line[i] == '-' || line[i] == '+')) {
                negative = line[i] == '-';
                i++;
              }
              while (i < len && line[i] >= '0' && line[i] <= '9')
                h += line[i++];
              if (i < len && line[i] == '.') {
                h += '.';
                while (i < len && line[i] >= '0' && line[i] <= '9')
                  h += line[i++];
              }
              if (h.empty())
                continue;
              while (i < len && ((line[i] >= 'a' && line[i] <= 'z') ||
                                 (line[i] >= 'A' && line[i] <= 'Z')))
                h_unit += line[i++];
              for (size_t x = 0; x < h_unit.size(); x++)
                h_unit[x] = tolower(h_unit[x]);
              if (h_unit.empty())
                u = deg;
              else if (h_unit == "deg")
                u = deg;
              else if (h_unit == "grad")
                u = grad;
              else if (h_unit == "rad")
                u = rad;
              else if (h_unit == "turn")
                u = turn;
              else
                continue;
              double hue = std::stod(h);
              if (negative)
                hue = -hue;
              switch (u) {
              case deg:
                break;
              case grad:
                hue = hue * 360.0 / 400.0;
                break;
              case rad:
                hue = hue * 180.0 / M_PI;
                break;
              case turn:
                hue = hue * 360.0;
                break;
              }
              hue = fmod(hue, 360.0);
              if (hue < 0)
                hue += 360.0;
              double h_final = hue / 360.0;
              while (i < len && (line[i] == ',' || line[i] == ' '))
                i++;
              std::string s = "";
              while (i < len && line[i] >= '0' && line[i] <= '9')
                s += line[i++];
              if (s.empty())
                continue;
              if (i < len && line[i] == '%')
                i++;
              else
                continue;
              while (i < len && (line[i] == ',' || line[i] == ' '))
                i++;
              std::string l = "";
              while (i < len && line[i] >= '0' && line[i] <= '9')
                l += line[i++];
              if (l.empty())
                continue;
              if (i < len && line[i] == '%')
                i++;
              else
                continue;
              while (i < len &&
                     (line[i] == ',' || line[i] == ' ' || line[i] == '.' ||
                      line[i] == '/' || line[i] == '%' ||
                      (line[i] >= '0' && line[i] <= '9')))
                i++;
              if (i < len && line[i] == ')')
                i++;
              double s_val = std::stod(s) / 100.0;
              double l_val = std::stod(l) / 100.0;
              uint32_t color = hslToRgb(h_final, s_val, l_val);
              for (uint32_t j = start; j < i; j++)
                lines[idx][j] = color;
            }
            continue;
          }
        }
        if (i + 4 < len && line[i] == '#') {
          i++;
          uint32_t start = i;
          while (i < len && isxdigit(line[i]))
            i++;
          uint32_t color = 0;
          if (is_css && (i - start == 3 || i - start == 4)) {
            uint32_t r =
                std::stoul(std::string(line + start, 1), nullptr, 16) * 0x11;
            uint32_t g =
                std::stoul(std::string(line + start + 1, 1), nullptr, 16) *
                0x11;
            uint32_t b =
                std::stoul(std::string(line + start + 2, 1), nullptr, 16) *
                0x11;
            color = (r << 16) | (g << 8) | b;
          } else if ((is_css && (i - start == 8)) || i - start == 6) {
            uint32_t r = std::stoul(std::string(line + start, 2), nullptr, 16);
            uint32_t g =
                std::stoul(std::string(line + start + 2, 2), nullptr, 16);
            uint32_t b =
                std::stoul(std::string(line + start + 4, 2), nullptr, 16);
            color = (r << 16) | (g << 8) | b;
          } else {
            continue;
          }
          for (uint32_t j = start - 1; j < i; j++)
            lines[idx][j] = color;
          continue;
        } else if (i + 5 < len && line[i] == '0' && line[i + 1] == 'x') {
          i += 2;
          uint32_t start = i;
          while (i < len && isxdigit(line[i]))
            i++;
          uint32_t color = 0;
          if (i - start == 6) {
            uint32_t r = std::stoul(std::string(line + start, 2), nullptr, 16);
            uint32_t g =
                std::stoul(std::string(line + start + 2, 2), nullptr, 16);
            uint32_t b =
                std::stoul(std::string(line + start + 4, 2), nullptr, 16);
            color = (r << 16) | (g << 8) | b;
          } else {
            continue;
          }
          if (color)
            color--;
          else
            color++;
          for (uint32_t j = start - 2; j < i; j++)
            lines[idx][j] = color;
          continue;
        }
        if (i < len && (isalnum(line[i]) || line[i] == '_')) {
          uint32_t start = i;
          uint32_t x = 0;
          bool found = true;
          while (i < len && (isalnum(line[i]) || line[i] == '_')) {
            if (x < word.size() && line[i] == word[x]) {
              i++;
              x++;
            } else {
              found = false;
              i++;
            }
          }
          if (found && x == word.size())
            for (uint32_t j = start; j < i; j++)
              lines[idx][j] = UINT32_MAX;
        } else {
          i += utf8_codepoint_width(line[i]);
        }
      }
      idx++;
    }
    free(it->buffer);
    free(it);
  }

  std::optional<std::pair<uint32_t, uint32_t>> get(Coord pos) {
    uint32_t val;
    if (pos.row < start || pos.row >= start + 50 ||
        pos.col >= lines[pos.row - start].size() ||
        (val = lines[pos.row - start][pos.col]) == UINT32_MAX - 1)
      return std::nullopt;
    return (std::pair<uint32_t, uint32_t>){fg_for_bg(val), val};
  }

private:
  uint32_t fg_for_bg(uint32_t color) {
    uint8_t r = (color >> 16) & 0xFF;
    uint8_t g = (color >> 8) & 0xFF;
    uint8_t b = color & 0xFF;
    double luminance = 0.299 * r + 0.587 * g + 0.114 * b;
    return (luminance > 128) ? 0x010101 : 0xFFFFFF;
  }

  uint32_t hslToRgb(double h, double s, double l) {
    double r, g, b;
    if (s == 0.0) {
      r = g = b = l;
    } else {
      auto hue2rgb = [](double p, double q, double t) -> double {
        if (t < 0)
          t += 1;
        if (t > 1)
          t -= 1;
        if (t < 1.0 / 6)
          return p + (q - p) * 6 * t;
        if (t < 1.0 / 2)
          return q;
        if (t < 2.0 / 3)
          return p + (q - p) * (2.0 / 3 - t) * 6;
        return p;
      };
      double q = l < 0.5 ? l * (1 + s) : l + s - l * s;
      double p = 2 * l - q;
      r = hue2rgb(p, q, h + 1.0 / 3);
      g = hue2rgb(p, q, h);
      b = hue2rgb(p, q, h - 1.0 / 3);
    }
    uint32_t R = static_cast<uint32_t>(std::clamp(r, 0.0, 1.0) * 255);
    uint32_t G = static_cast<uint32_t>(std::clamp(g, 0.0, 1.0) * 255);
    uint32_t B = static_cast<uint32_t>(std::clamp(b, 0.0, 1.0) * 255);
    return (R << 16) | (G << 8) | B;
  }
};

#endif
