/*
{{copyright}} 
*/

/*
{{version}} 
*/

/*
{{license}} 
*/

#pragma once

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

#include <iostream>
#include <memory>

namespace sdi_toolBox::logs
{
//--------------------------------------------------------------
class Win32Console
{
  public:
  static void initConsole();        // Init application console and attach debug console under MSW
  static void releaseConsole();     // Release application console

  public:
  virtual ~Win32Console();                                           // Default destructor
  Win32Console(const Win32Console &obj)                = delete;     // Copy constructor
  Win32Console(Win32Console &&obj) noexcept            = delete;     // Move constructor
  Win32Console &operator=(const Win32Console &obj)     = delete;     // Copy assignment operator
  Win32Console &operator=(Win32Console &&obj) noexcept = delete;     // Move assignment operator

  static bool hasAttachedConsole();     // Returns true if console is attached, false otherwise

  protected:
  static inline std::unique_ptr<Win32Console> m_singleton;
  Win32Console();     // Default constructor

  FILE *m_stdoutFile;     // Reopened stdout file pointer
  FILE *m_stderrFile;     // Reopened stderr file pointer
};
//--------------------------------------------------------------
/* Init application console and attach debug console under MSW */
inline void Win32Console::initConsole()
{
#ifndef _WIN32
#  error This calss is only available under Windows systems
#endif

  if (!m_singleton)
    m_singleton = std::unique_ptr<Win32Console>(new Win32Console());
}
//--------------------------------------------------------------
/* Release application console */
inline void Win32Console::releaseConsole()
{
  if (m_singleton)
    m_singleton.reset();
}
//--------------------------------------------------------------
/* Default constructor */
inline Win32Console::Win32Console()
{
#ifdef _WIN32
  bool consoleIsCreated = false;

  // Try to attach application to the current console
  AttachConsole(ATTACH_PARENT_PROCESS);

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

  // Reopen stdout and stderr streams to console window
  if (freopen_s(&m_stdoutFile, "CONOUT$", "w", stdout) != 0)
    throw std::logic_error("Unable to reopen stdout");     // Error during reopen on stdout

  if (freopen_s(&m_stderrFile, "CONOUT$", "w", stderr) != 0)
    throw std::logic_error("Unable to reopen stderr");     // Error during reopen on stderr

  std::cout.clear();
  std::cerr.clear();

  if (!consoleIsCreated)
    std::cout << std::endl;     // Add a new line if console was already existing
#endif
}
//--------------------------------------------------------------
/* Default destructor */
inline Win32Console::~Win32Console()
{
#ifdef _WIN32
  std::cout.clear();
  std::cerr.clear();

  // Free console
  FreeConsole();

  // Close reopened stdout and stderr streams
  (void)fclose(m_stdoutFile);
  (void)fclose(m_stderrFile);
#endif
}
//--------------------------------------------------------------
/* Returns true if console is attached, false otherwise */
inline bool Win32Console::hasAttachedConsole()
{
#ifdef _WIN32
  if (GetConsoleWindow())
    return true;
#endif
  return false;
}
//--------------------------------------------------------------
}     // namespace sdi_toolBox::logs
