//
// client.cpp
// ~~~~~~~~~~
//
// Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//

#include <boost/asio.hpp>
#include <boost/log/attributes.hpp>
#include <boost/log/core.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/log/utility/setup/console.hpp>
// #include <boost/log/sinks/text_file_backend.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/utility/setup/file.hpp>
#include <cstdlib>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <map>
#include <string>

#include "config.h"
#include "logging.h"
#include "session.h"

// #define BOOST_ASIO_ENABLE_HANDLER_TRACKING
// std::map<std::string, std::string> CONFIG;

// #include <boost/date_time/posix_time/posix_time_types.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/support/date_time.hpp>

/*
boost log linking -
undefined reference to `void boost::log::v2_mt_posix::init_from_stream
https://github.com/boostorg/log/issues/46
 */

void init_logging(void) {
  // because TimeStamp is missing by default.
  boost::log::add_common_attributes();

  // "proxy-%Y-%m-%d.log"
  std::string log_filename = "proxy.log";
  if (CONFIG.contains("log_file")) log_filename = json_str(CONFIG["log_file"]);

  // "%I:%M:%S.%f %p"
  std::string log_timeformat = "%H:%M:%S.%f";
  if (CONFIG.contains("log_timeformat"))
    log_timeformat = json_str(CONFIG["log_timeformat"]);
  // from_config("log_timeformat", "%H:%M:%S.%f");

  bool log_autoflush = false;
  if (CONFIG.contains("log_autoflush")) {
    log_autoflush = json_bool(CONFIG["log_autoflush"]);
  }

  int log_level = 2;
  if (CONFIG.contains("log_level")) {
    log_level = json_int(CONFIG["log_level"]);
  }

  bool console = false;
  if (CONFIG.contains("log_console")) {
    console = json_bool(CONFIG["log_console"]);
  }

  std::cout << "Logging to: ";
  if (console) std::cout << "console + ";
  std::cout << log_filename << " level: " << log_level
            << " flush: " << log_autoflush << " format: " << log_timeformat
            << std::endl;

  if (console)
    boost::log::add_console_log(
        std::clog, boost::log::keywords::auto_flush = log_autoflush,
        boost::log::keywords::format =
            (boost::log::expressions::stream
             << boost::log::expressions::format_date_time<
                    boost::posix_time::ptime>("TimeStamp", log_timeformat)
             << " " << std::setw(8) << boost::log::trivial::severity << " "
             << boost::log::expressions::smessage));

  boost::log::add_file_log(
      boost::log::keywords::file_name = log_filename,
      // This appends to the logfile (instead of overwrite)
      boost::log::keywords::open_mode = std::ios_base::out | std::ios_base::app,
      boost::log::keywords::auto_flush = log_autoflush,
      // boost::log::keywords::format = "[%TimeStamp%] %Severity%  : %Message%"
      boost::log::keywords::format =
          (boost::log::expressions::stream
           << boost::log::expressions::format_date_time<
                  boost::posix_time::ptime>("TimeStamp", log_timeformat)
           << " " << std::setw(8) << boost::log::trivial::severity << " "
           << boost::log::expressions::smessage));

  auto core = boost::log::core::get();
  core->set_filter(boost::log::trivial::severity >= log_level);
}

int main(int argc, char *argv[]) {
  if (argc != 2) {
    std::cerr << "Usage: twproxy <filename>" << std::endl;
    return EXIT_FAILURE;
  }

  {
    std::ifstream fin(argv[1]);
    CONFIG = json::parse(fin);  // YAML::LoadFile(argv[1]);
  }

  init_logging();

  bool config_ok = true;

  // for (const char *key : {"server", "host", "port"}) {
  for (auto key : {"server", "host", "port", "basename"}) {
    if (!CONFIG.contains(key)) {
      config_ok = false;
      std::cout << "Config file missing: " << key << std::endl;
      BUGZ_LOG(fatal) << "Config file missing: " << key;
    }

    // The leaks are reported here, because this is the first usage of the
    // BOOST_LOG_TRIVIAL()! Comment these out, and the leaks are reported at the
    // next logging usage instance.

    std::string value = json_str(CONFIG[key]);
    BUGZ_LOG(info) << "Config: " << key << " : " << value;
  }

  if (!config_ok) return EXIT_FAILURE;

  int listen_port = json_int(CONFIG["server"]);

#define BURN

#ifndef BURN
  try {
#endif
    bool telnet = false;

    if (CONFIG.contains("server_telnet")) {
      telnet = true;
      BUGZ_LOG(fatal) << "Connect to server via TELNET";
    }

    boost::asio::io_service io_service;

    boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::tcp::v4(),
                                            listen_port);
    // connect to the game server
    std::string host = json_str(CONFIG["host"]);
    std::string port = json_str(CONFIG["port"]);
    BUGZ_LOG(fatal) << "host: " << host << " port: " << port;

    Server serve(io_service, endpoint, host, port, telnet);

    io_service.run();
#ifndef BURN
  } catch (std::exception &e) {
    BUGZ_LOG(fatal) << "Exception: " << e.what();
    std::cerr << "Exception: " << e.what() << "\n";
  }
#endif

  return EXIT_SUCCESS;
}