// File: crn_console.h
// See Copyright Notice and license at the end of inc/crnlib.h
#pragma once
#include "crn_dynamic_string.h"

#ifdef WIN32
#include <tchar.h>
#include <conio.h>
#elif defined(__GNUC__)
#include <termios.h>
#include <unistd.h>
#endif
namespace crnlib {
class dynamic_string;
class data_stream;
class mutex;

enum eConsoleMessageType {
  cDebugConsoleMessage,     // debugging messages
  cProgressConsoleMessage,  // progress messages
  cInfoConsoleMessage,      // ordinary messages
  cConsoleConsoleMessage,   // user console output
  cMessageConsoleMessage,   // high importance messages
  cWarningConsoleMessage,   // warnings
  cErrorConsoleMessage,     // errors

  cCMTTotal
};

typedef bool (*console_output_func)(eConsoleMessageType type, const char* pMsg, void* pData);

class console {
 public:
  static void init();
  static void deinit();

  static bool is_initialized() { return m_pMutex != NULL; }

  static void set_default_category(eConsoleMessageType category);
  static eConsoleMessageType get_default_category();

  static void add_console_output_func(console_output_func pFunc, void* pData);
  static void remove_console_output_func(console_output_func pFunc);

  static void printf(const char* p, ...);

  static void vprintf(eConsoleMessageType type, const char* p, va_list args);
  static void printf(eConsoleMessageType type, const char* p, ...);

  static void cons(const char* p, ...);
  static void debug(const char* p, ...);
  static void progress(const char* p, ...);
  static void info(const char* p, ...);
  static void message(const char* p, ...);
  static void warning(const char* p, ...);
  static void error(const char* p, ...);

  // FIXME: All console state is currently global!
  static void disable_prefixes();
  static void enable_prefixes();
  static bool get_prefixes() { return m_prefixes; }
  static bool get_at_beginning_of_line() { return m_at_beginning_of_line; }

  static void disable_crlf();
  static void enable_crlf();
  static bool get_crlf() { return m_crlf; }

  static void disable_output() { m_output_disabled = true; }
  static void enable_output() { m_output_disabled = false; }
  static bool get_output_disabled() { return m_output_disabled; }

  static void set_log_stream(data_stream* pStream) { m_pLog_stream = pStream; }
  static data_stream* get_log_stream() { return m_pLog_stream; }

  static uint get_num_messages(eConsoleMessageType type) { return m_num_messages[type]; }

 private:
  static eConsoleMessageType m_default_category;

  struct console_func {
    console_func(console_output_func func = NULL, void* pData = NULL)
        : m_func(func), m_pData(pData) {}

    console_output_func m_func;
    void* m_pData;
  };
  static crnlib::vector<console_func> m_output_funcs;

  static bool m_crlf, m_prefixes, m_output_disabled;

  static data_stream* m_pLog_stream;

  static mutex* m_pMutex;

  static uint m_num_messages[cCMTTotal];

  static bool m_at_beginning_of_line;
};

#if defined(WIN32)
inline int crn_getch() {
  return _getch();
}
#elif defined(__GNUC__)
inline int crn_getch() {
  struct termios oldt, newt;
  int ch;
  tcgetattr(STDIN_FILENO, &oldt);
  newt = oldt;
  newt.c_lflag &= ~(ICANON | ECHO);
  tcsetattr(STDIN_FILENO, TCSANOW, &newt);
  ch = getchar();
  tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
  return ch;
}
#else
inline int crn_getch() {
  printf("crn_getch: Unimplemented");
  return 0;
}
#endif
}  // namespace crnlib
