/* --- logConsoleGUI.cpp ---------------------------------------
   System:   Log system for gui applications
   Status:   Version 1.0 Release 2
   Language: C++

   (c) Copyright SD-Innovation 2016

   Author: Sylvain Schneider
------------------------------------------------------------- */

#include "logConsoleGUI.h"

#include <ctime>
#include <sstream>

#ifdef _WIN32
#  include <Windows.h>
#endif     // defined WIN32

t_logConsoleGUI logConsole;

using namespace std;
using namespace sdiTools;
//--------------------------------------------------------------
logConsoleGUI::logConsoleGUI(const std::string &logFilepath)
{
  // Constructor
  if (!logFilepath.empty())
    openLogFile(logFilepath);
}
//--------------------------------------------------------------
logConsoleGUI::~logConsoleGUI()
{
  // Destructor
  releaseConsole();
}
//--------------------------------------------------------------
void logConsoleGUI::openLogFile(const std::string &logFilepath)
{
  // Open log file
  m_logFile.open(logFilepath.c_str(), std::ofstream::out | std::ofstream::app);
  if (!m_logFile.is_open())
    throw logic_error("Unable to open log file !");
}
//--------------------------------------------------------------
void logConsoleGUI::closeLogFile()
{
  // Close log file
  if (m_logFile.is_open())
    m_logFile.close();
}
//--------------------------------------------------------------
int64_t logConsoleGUI::getLogFileSize()
{
  // Return opened log file size
  if (!m_logFile.is_open())
    return -1;
  return m_logFile.tellp();
}
//--------------------------------------------------------------
void logConsoleGUI::initConsole(bool attachOnly)
{
  // Init application console and attach debug console under MSW
#ifdef _WIN32

  if (hasAttachedConsole())
    return;

  bool hasParentConsole = true;
  if (!AttachConsole(ATTACH_PARENT_PROCESS))     // Attach application to console
  {
    if (attachOnly)
      return;

    // No console was available
    hasParentConsole = false;
    if (!AllocConsole())     // Create console and attach application to it
    {
      // Error during creating console
      throw logic_error("Unable to attach application to debug console");
    }
  }

  int err;
  err = freopen_s(&m_stdoutFile, "CONOUT$", "w", stdout);
  if (err != 0)
  {
    // Error during reopen on stdout
    throw logic_error("Unable to reopen stdout");
  }
  err = freopen_s(&m_stderrFile, "CONOUT$", "w", stderr);
  if (err != 0)
  {
    // Error during reopen on stderr
    throw logic_error("Unable to reopen stderr");
  }

  cout.clear();
  cerr.clear();

  if (hasParentConsole)
  {
    cout << endl;
  }
  // cout << "logConsoleGUI is ready !" << endl;

#endif     // defined WIN32
}
//--------------------------------------------------------------
void logConsoleGUI::releaseConsole()
{
  // Release application console
#ifdef _WIN32

  if (!hasAttachedConsole())
    return;

  cout << "logConsoleGUI is terminated !" << endl;
  cout.clear();
  cerr.clear();

  FreeConsole();

  fclose(m_stdoutFile);
  fclose(m_stderrFile);

#endif     // defined WIN32
}
//--------------------------------------------------------------
bool logConsoleGUI::hasAttachedConsole()
{
  // Returns true if console was attached, false otherwise
#ifdef _WIN32

  if (GetConsoleWindow())
    return true;

  return false;

#endif     // defined WIN32
  return false;
}
//--------------------------------------------------------------
void logConsoleGUI::disable_quickEditMode()
{
#ifdef _WIN32
  HANDLE hInput = GetStdHandle(STD_INPUT_HANDLE);
  if (hInput == INVALID_HANDLE_VALUE || hInput == NULL)
  {
    DWORD errorCode = GetLastError();
    throw runtime_error("Unable to get input console handle");
  }

  BOOL  bRet;
  DWORD consoleMode;

  bRet = GetConsoleMode(hInput, &consoleMode);
  if (bRet == 0)
  {
    DWORD errorCode = GetLastError();
    throw runtime_error("Unable to get console mode");
  }

  consoleMode = ENABLE_EXTENDED_FLAGS | (consoleMode & ~ENABLE_QUICK_EDIT_MODE);

  bRet = SetConsoleMode(hInput, consoleMode);
  if (bRet == 0)
  {
    // DWORD errorCode = GetLastError();
    throw runtime_error("Unable to set console mode");
  }

  // Disable cursor
  HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
  if (hOutput == INVALID_HANDLE_VALUE || hOutput == NULL)
  {
    DWORD errorCode = GetLastError();
    throw runtime_error("Unable to get output console handle");
  }

  CONSOLE_CURSOR_INFO cursorInfo;
  bRet = GetConsoleCursorInfo(hOutput, &cursorInfo);
  if (bRet == 0)
  {
    throw runtime_error("Unable to get console cursor info");
  }

  cursorInfo.bVisible = false;     // set the cursor visibility
  bRet                = SetConsoleCursorInfo(hOutput, &cursorInfo);
  if (bRet == 0)
  {
    throw runtime_error("Unable to set console cursor info");
  }

#endif     // defined WIN32
}
//--------------------------------------------------------------
void logConsoleGUI::alwaysOnTop()
{
#ifdef _WIN32
  HWND hWin = GetConsoleWindow();
  if (!hWin)
    throw runtime_error("Unable to get window handle");

  BOOL bRet;

  bRet = SetWindowPos(
    hWin,             // window handle
    HWND_TOPMOST,     // "handle to the window to precede
                      // the positioned window in the Z order
                      // OR one of the following:"
                      // HWND_BOTTOM or HWND_NOTOPMOST or HWND_TOP or HWND_TOPMOST
    0,
    80,     // X, Y position of the window (in client coordinates)
    0,
    0,                                                                 // cx, cy => width & height of the window in pixels
    SWP_DRAWFRAME | /* SWP_NOMOVE | */ SWP_NOSIZE | SWP_SHOWWINDOW     // The window sizing and positioning flags.
  );
  if (bRet == 0)
  {
    DWORD errorCode = GetLastError();
    throw runtime_error("Unable to change window mode");
  }

#endif     // defined WIN32
}
//--------------------------------------------------------------
void logConsoleGUI::log(const std::string &msg, e_logLevel level)
{
  // Log message into console and log file
  string levelStr = "";
  switch (level)
  {
    case level_DEBUG_COMMUNICATION:
#ifndef DEBUG
      return;
#endif
      levelStr = "[DEBUG_COMM] ";
      break;
    case level_DEBUG:
#ifndef DEBUG
      return;
#endif
      levelStr = "[DEBUG] ";
      break;
    case level_INFORMATIONS:
      break;
    case level_NOTICE:
      levelStr = "[NOTICE] ";
      break;
    case level_WARNING:
      levelStr = "[WARNING] ";
      break;
    case level_ERROR:
      levelStr = "[ERROR] ";
      break;
    case level_CRITICAL:
      levelStr = "[CRITICAL] ";
      break;
    case level_ALERT:
      levelStr = "[ALERT] ";
      break;
    case level_EMERGENCY:
      levelStr = "[EMERGENCY] ";
      break;
  }

  string logMsg = header() + levelStr + msg;

  cout << logMsg << endl;
  if (m_logFile.is_open())
    m_logFile << logMsg << endl;
}
//--------------------------------------------------------------
std::string logConsoleGUI::header()
{
  // Define header to console log
  char      buff[20];
  time_t    now = time(nullptr);
  struct tm tm;

#ifdef _WIN32
  localtime_s(&tm, &now);
#elif __linux__
  tm = *localtime(&now);
#endif

  strftime(buff, 20, "%Y-%m-%d %H:%M:%S", &tm);

  ostringstream oss;
  oss << buff << " - ";

  return oss.str();
}
//--------------------------------------------------------------
