#include <fmt/core.h>
#include <string_view>
#include "dbc.hpp"
#include <SFML/Graphics.hpp>
#include "ansi_parser.hpp"
#include <iostream>

using namespace fmt;

%%{
  machine ansi_parser;
  alphtype int;

  action tstart {
    start = fpc;
  }

  action number {
    value = 0;
    size_t len = fpc - start;
    dbc::check(start[0] != '-', "negative numbers not supported");

    switch(len) {
      case 10:    value += (start[len-10] - '0') * 1000000000;
      case  9:    value += (start[len- 9] - '0') * 100000000;
      case  8:    value += (start[len- 8] - '0') * 10000000;
      case  7:    value += (start[len- 7] - '0') * 1000000;
      case  6:    value += (start[len- 6] - '0') * 100000;
      case  5:    value += (start[len- 5] - '0') * 10000;
      case  4:    value += (start[len- 4] - '0') * 1000;
      case  3:    value += (start[len- 3] - '0') * 100;
      case  2:    value += (start[len- 2] - '0') * 10;
      case  1:    value += (start[len- 1] - '0');
        break;
      default:
        dbc::sentinel("can't process > 10 digits");
    }
  }

  action color_out {
    color_cb(color, bgcolor);
  }
  action is_fg {
    target = &color;
  }
  action is_bg {
    target = &bgcolor;
  }

  action out {
    write_cb(fc);
  }

  action reset_fg {
    color = $default_fg;
    color_cb(color, bgcolor);
  }
  action reset_bg {
    bgcolor = $default_bg;
    color_cb(color, bgcolor);
  }
  action invert {
    color = $default_bg;
    bgcolor = $default_fg;
    color_cb(color, bgcolor);
  }
  action reset_invert {
    color = $default_fg;
    bgcolor = $default_bg;
    color_cb(color, bgcolor);
  }
  action half_bright {
    color = sf::Color(100,100,100);
    color_cb(color, bgcolor);
  }
  action red_text {
    color = sf::Color::Red;
    color_cb(color, bgcolor);
  }

  action red { target->r = value; }
  action blue { target->g = value; }
  action green { target->b = value; }
  action start { value = 0; }
  action end {}
  action log { println("command {}", (char)fc); }

  ESC = 0x1B;
  start = ESC "[";
  fg = "38;" %is_fg;
  bg = "48;" %is_bg;
  reset = ("39" %reset_fg | "49" %reset_bg);
  num = digit+ >tstart %number;
  color256 = "5;";
  color24b = "2;";

  ansi = (
      start %start
      (
        reset |
        "0" %reset_fg %reset_bg |
        "1"  |
        "2" %half_bright |
        "3"  |
        "4"  |
        "5"  |
        "6"  |
        "7" %invert |
        "31" %red_text |
        "22" |
        "24" |
        "27" %reset_invert |
        "9" ["0"-"7"] |
        "10" ["0"-"7"] |
        (fg|bg) (color24b num %red ";" num %blue ";" num %green ) %color_out
      ) "m" %end
  );

  other = (any+ @out -- ESC)*;

  main := (other :> ansi)**;
}%%

%% write data;

#include <ftxui/screen/terminal.hpp>

ANSIParser::ANSIParser(sf::Color default_fg, sf::Color default_bg) :
  $default_fg(default_fg),
  $default_bg(default_bg)
{
}

bool ANSIParser::parse(std::wstring_view codes, ColorCB color_cb, WriteCB write_cb) {
  const wchar_t *start = NULL;
  int cs = 0;
  unsigned int value = 0;
  const wchar_t *p = codes.data();
  const wchar_t *pe = p + codes.size();
  const wchar_t *eof = pe;
  sf::Color bgcolor($default_bg);
  sf::Color color($default_fg);
  sf::Color* target = &color;

  %% write init;
  %% write exec;

  bool good = pe - p == 0;

  if(!good) {
    p -= 10;
    // dear cthuhlu, save me from the pain that is wstring
    for(int i = 0; i < 100; i++) {
      try {
        print("{}", p[i] == 0x1B ? '^' : char(p[i]));
      } catch(...) {
        print("?=", int(p[i]));
      }
    }
  }

  return good;
}