#pragma once
#include "gui.hpp"
#include "game_engine.hpp"
#include <stdio.h>
#include "fsm.hpp"
#include <efsw/efsw.hpp>
#include <future>
#include <stdio.h>
#include "watcher.hpp"

using std::string;

struct MatchResult {
  bool match = false;
  string file_name = "";
  string lnumber = "";
  string col = "";
  string type = "";
  string message = "";
};

enum class BuildState {
  START, WAITING, BUILDING, DONE, FORKING, READING,
  EXIT, ERROR
};

enum BuildEvent {
  GO, QUIT, CRASH
};

class Builder : DeadSimpleFSM<BuildState, BuildEvent> {
  GUI &gui;
  GameEngine &game;
  string git_path = "NOT SET";
  string build_cmd = "NOT SET";
  efsw::FileWatcher* fileWatcher = NULL;
  UpdateListener* listener = NULL;
  efsw::WatchID wid;
  FILE *build_out = NULL;
  bool build_done = false;
  string line = "";
  std::future<FILE *> build_fut;
  std::future<string> read_fut;
  std::mutex fsm_mutex;
  git_repository* repo = nullptr;
  std::unordered_map<string, int> $hit_line_stats;

  public:

  Builder(GUI &g, GameEngine &engine);

  MatchResult parse_line(const string &line);
  string read_line(FILE *build_out, bool &done_out);
  FILE *start_command(string &build_cmd);

  void event(BuildEvent ev) {
    try {
      if(ev == QUIT) {
        EXIT(ev);
      }

      switch(_state) {
        FSM_STATE(BuildState, BUILDING, ev);
        FSM_STATE(BuildState, START, ev);
        FSM_STATE(BuildState, WAITING, ev);
        FSM_STATE(BuildState, DONE, ev);
        FSM_STATE(BuildState, FORKING, ev);
        FSM_STATE(BuildState, READING, ev);
        FSM_STATE(BuildState, EXIT, ev);
        FSM_STATE(BuildState, ERROR, ev);
      }
    } catch(...) {
      fmt::println("ERROR: unhandled state: {}", int(_state));
      ERROR(ev);
    }
  }

  void BUILDING(BuildEvent ev);
  void START(BuildEvent ev);
  void WAITING(BuildEvent ev);
  void DONE(BuildEvent ev);
  void FORKING(BuildEvent ev);
  void READING(BuildEvent ev);
  void ERROR(BuildEvent ev);
  void EXIT(BuildEvent ev);
};