#ifndef DISPATCHERS_H
#define DISPATCHERS_H

#include <map>
#include <memory>
#include <string>

#include "director.h"
#include "session_common.h"

/*
Item of the day:
class Result {
    // holds unique_ptr or shared_ptr to the "dispatcher"
    // when I'm done -- delete the result, cleaning up
    // the dispatcher

}

 How does this call another?
 How does it return a result?

possibly:  io_service::post( DONE );  !

 */

class Dispatch {
 protected:
  Director &director;
  notifyFunc notify_;
  std::shared_ptr<Dispatch> chain;

 public:
  Dispatch(Director &);
  virtual ~Dispatch();

  void setNotify(notifyFunc nf);
  void notify(void);

  virtual void activate(void) = 0;
  virtual void deactivate(void) = 0;

  const std::string &get_prompt(void);
  void to_server(const std::string &send);
  void to_client(const std::string &send);

  // default to chain calls
  void chain_client_input(const std::string &input);
  void chain_server_line(const std::string &line, const std::string &raw_line);
  void chain_server_prompt(const std::string &prompt);
  virtual void client_input(const std::string &input);
  virtual void server_line(const std::string &line, const std::string &raw_line);
  virtual void server_prompt(const std::string &prompt);
};

/*
 * Some options for input:
 *
 * numeric only
 *
 */
class InputDispatch : public Dispatch {
 private:
 public:
  InputDispatch(Director &);
  ~InputDispatch();

  std::string prompt;
  size_t max_length;
  std::string input;

  void activate(void) override;
  void deactivate(void) override;

  // optional here
  void server_line(const std::string &line,
                   const std::string &raw_line) override;
  // void server_prompt(const std::string &prompt);
  void client_input(const std::string &cinput) override;
};

class MenuDispatch : public Dispatch {
 private:
  void help(void);
  void menubox(void);
  size_t max_width;
  size_t max_option_width;
  bool instant = false;
  void calculate_widths(void);
  std::string centered(int length, const std::string &);

 public:
  MenuDispatch(Director &);
  ~MenuDispatch();

  std::string menu_box_color;
  std::string menu_text_color;
  std::string menu_title;
  std::string menu_options_color;
  std::string menu_prompt;
  bool lazy = true;
  std::map<std::string, std::string> menu;
  bool case_sensitive = false;
  std::string input;
  int choice;

  void activate(void) override;
  void deactivate(void) override;

  // optional here
  void server_line(const std::string &line, const std::string &raw_line) override;
  // void server_prompt(const std::string &prompt);
  void client_input(const std::string &cinput) override;
};

// This was the original idea, but we've lost our "Main Dispatch" at this point
// I might bring this back, as a way to test the Input and Menu parts.

/**
 * The main/first proxy Dispatcher.
 *
 * Don't follow this as an example.  On disable,
 * it resets everything back to nothing active.
 * (Which is likely not what you want.)
 *
 */

class MainDispatch : public Dispatch {
 private:
  InputDispatch id;
  MenuDispatch md;

 public:
  MainDispatch(Director &);
  ~MainDispatch();

  void activate(void) override;
  void deactivate(void) override;

  void have_input(void);
  void menu_choice(void);

  void server_line(const std::string &line, const std::string &raw_line) override;
  void server_prompt(const std::string &prompt) override;
  // void client_input(const std::string &input);

 private:
  int count;
  std::string old_prompt;
};

class CoreDispatch : public Dispatch {
 public:
  CoreDispatch(Director &);

  void activate(void) override;
  void deactivate(void) override;

  // optional here
  void server_line(const std::string &line, const std::string &raw_line) override;
  void server_prompt(const std::string &prompt) override;
  void client_input(const std::string &input) override;
};

#endif