#ifndef SESSION_H
#define SESSION_H

#include <boost/asio.hpp>
#include <boost/asio/ip/basic_resolver.hpp>
#include <boost/asio/signal_set.hpp>
#include <map>
#include <string>

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

// don't want this here!
#include "dispatchers.h"

#define MAX_BUFFER 256

/*
The Session:

 */

class Session : public std::enable_shared_from_this<Session> {
 public:
  Session(boost::asio::ip::tcp::socket socket,
          boost::asio::io_service &io_service, std::string hostname,
          std::string port, bool server_telnet_);

  ~Session();

  void start(void);

  const std::string &get_prompt(void);
  void set_prompt(const std::string &prompt);
  void to_client(const std::string &message);
  void to_server(const std::string &message);

  /*
    DispatchSettings save_settings(void);
    void restore_settings(const DispatchSettings &ss);
  */

  // The session line parsing needs show_client

  // these move to Director
  // bool show_client = true;
  bool talk_direct = true;
  bool server_telnet;

  void post(notifyFunc nf);

 private:
  Director director;

  void on_server_line(const std::string &line, const std::string &raw_line);
  void on_server_prompt(const std::string &prompt,
                        const std::string &raw_prompt);

  void parse_auth(void);
  void on_connect(const boost::system::error_code error);

  void server_read(void);
  void on_resolve(const boost::system::error_code error,
                  const boost::asio::ip::tcp::resolver::results_type results);
  void client_read(void);

  void client_input(const std::string &input);

  void on_shutdown(boost::system::error_code ec);

  void split_lines(std::string line);
  void process_lines(std::string &received);

 private:
  /*
    Move to director  -?
    StringFunc SL_parser;
    void SL_cimline(const std::string &line);
    void SL_thiefline(const std::string &line);
    void SL_sectorline(const std::string &line);
    void SL_portline(const std::string &line);
    void SL_warpline(const std::string &line);
  */

  void set_prompt_timer(void);
  void reset_prompt_timer(void);
  void on_prompt_timeout(const boost::system::error_code error);
  void stayin_alive(const boost::system::error_code error);
  void start_keepin_alive(void);
  int time_ms;
  int keepalive_secs;

  // Move this to director.  This controls if the "keep alive" fires or not.
  bool active = false;
  /*  Move to director.
    MainDispatch main;
    void proxy_activate(void);
    void proxy_deactivate(void);

    // std::stack<Dispatch *> director;

    bool active = false;

  */

  /**
   * The client's socket
   */
  boost::asio::ip::tcp::socket socket_;
  boost::asio::io_service &io_service_;
  boost::asio::ip::tcp::resolver resolver_;
  /**
   * The server's socket
   */
  boost::asio::ip::tcp::socket server_;

  /**
   * The time that we'll use to fire off the "prompt" event.
   *
   * The idea being that we'd receive chars, processing lines.
   * And if we have something in server_prompt start the timer.
   * If we receive more, process lines, if !server_prompt.empty()
   * reset the timer.
   *
   * If the timer fires (and isn't aborted), fire off the prompt.
   *
   * I'm not so sure about this -- because this adds delay to the
   * proxy.  [It might be better to just fire off "some" text, and
   * have it ignored, rather then adding a delay.]  Or, this might
   * not matter at all, I'm thinking milliseconds here!
   */
  boost::asio::high_resolution_timer prompt_timer_;
  /**
   * Keep connection alive, don't timeout.
   *
   * This gets set by to_server config[keepalive], and sends a
   * space ' ' if we haven't sent anything to the server in that
   * many seconds.
   */
  boost::asio::high_resolution_timer keep_alive_;
  /**
   * What characters have been received from the server,
   * that weren't \n terminated?
   *
   * This needs to be reset/cleared if there's a \r (carriage return).
   */
  std::string server_prompt;

  /**
   * The client read buffer.
   *
   * This is too big, we don't get that many characters from the client.
   *
   */
  char read_buffer[MAX_BUFFER];
  /**
   * The server read buffer.
   */
  char server_buffer[MAX_BUFFER];

  /**
   * The rlogin information received from the client.
   *
   * We check this, and if it isn't valid, we spoof some rlogin
   * connection.
   */
  std::string rlogin_auth;
  /**
   * The username passed in via rlogin.  We need this so we know what
   * name we need to store the data under.
   */
  std::string rlogin_name;
  std::string host;
  std::string port;
  char game = 0;

  /**
   * Are we connected to the server?
   *
   * Don't shutdown the server socket if we aren't connected.
   */
  bool connected = false;
};

/*
maybe move the resolver part to the server, so I don't need io_service?

I'm not sure what the socket connection part is going to need just yet,
so I probably won't move that just yet.  [NNY!]

*/

class Server {
 public:
  Server(boost::asio::io_service &io_service,
         const boost::asio::ip::tcp::endpoint &endpoint,
         const std::string &host, const std::string &port, bool server_telnet_);
  ~Server();

 private:
  void do_accept(void);
  void on_signal(const boost::system::error_code &ec, int signal);

  bool server_telnet;
  boost::asio::io_service &io_service_;
  boost::asio::ip::tcp::acceptor acceptor_;
  boost::asio::signal_set signal_;

  bool keep_accepting;

  /**
   * The host to connect to (from config)
   */
  std::string host_;
  /**
   * The port to connect to (from config)
   *
   */
  std::string port_;
};

#endif