#include "pch.h"
#include "System/Console.h"
#include "System/IO/TextWriter.h"
#include "System/IO/TextReader.h"
#include <mutex>
#include <iostream>
#include <string>
#include <vector>
#if defined(_WIN32)
#include <windows.h>
#include "Win32Internal.h"
using namespace DotNetDupe::System::Internal;
#else
#include <unistd.h>
#endif
using namespace DotNetDupe::System;
namespace {
static std::vector<String> s_outputs;
static std::vector<String> s_inputs;
static String s_accumulator = String("");
static std::recursive_mutex s_mutex;
static ConsoleColor s_defaultFore = ConsoleColor::Gray;
static ConsoleColor s_defaultBack = ConsoleColor::Black;
static ConsoleColor s_currentFore = ConsoleColor::Gray;
static ConsoleColor s_currentBack = ConsoleColor::Black;
static String s_consoleTitle = String("");
static bool s_colorsInitialized = false;
void EnsureColorsInitialized() {
if (!s_colorsInitialized) {
#if defined(_WIN32)
CONSOLE_SCREEN_BUFFER_INFO csbi;
if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)) {
s_defaultFore = (ConsoleColor)(csbi.wAttributes & 0x0F);
s_defaultBack = (ConsoleColor)((csbi.wAttributes & 0xF0) >> 4);
s_currentFore = s_defaultFore;
s_currentBack = s_defaultBack;
}
#endif
s_colorsInitialized = true;
}
}
static SmartPointer<IO::TextWriter> s_pOutWriter = nullptr;
static SmartPointer<IO::TextWriter> s_pErrorWriter = nullptr;
static SmartPointer<IO::TextReader> s_pInReader = nullptr;
}
void InternalWrite(const String& sValue) {
SmartPointer<IO::TextWriter> pWriter = nullptr;
{
std::lock_guard<std::recursive_mutex> lk(s_mutex);
s_accumulator = s_accumulator + sValue;
pWriter = s_pOutWriter;
if (!pWriter) {
std::cout << sValue.GetRawString() << std::flush;
return;
}
}
pWriter->Write(sValue);
}
void InternalWriteLine(const String& sValue) {
SmartPointer<IO::TextWriter> pWriter = nullptr;
{
std::lock_guard<std::recursive_mutex> lk(s_mutex);
s_accumulator = s_accumulator + sValue;
s_outputs.push_back(s_accumulator);
pWriter = s_pOutWriter;
if (!pWriter) {
std::cout << sValue.GetRawString() << std::endl;
s_accumulator = "";
return;
}
s_accumulator = "";
}
pWriter->WriteLine(sValue);
}
void Console::Write(bool value) { InternalWrite(value ? "True" : "False"); }
void Console::Write(Char value) { char buf[2] = { static_cast<char>(value.GetChar()), 0 }; InternalWrite(buf); }
void Console::Write(double value) { InternalWrite(String(std::to_string(value).c_str())); }
void Console::Write(int value) { InternalWrite(String(std::to_string(value).c_str())); }
void Console::Write(long value) { InternalWrite(String(std::to_string(value).c_str())); }
void Console::Write(long long value) { InternalWrite(String(std::to_string(value).c_str())); }
void Console::Write(float value) { InternalWrite(String(std::to_string(value).c_str())); }
void Console::Write(const String& value) { InternalWrite(value); }
void Console::Write(const char* value) { InternalWrite(value); }
void Console::WriteLine() { InternalWriteLine(""); }
void Console::WriteLine(bool value) { InternalWriteLine(value ? "True" : "False"); }
void Console::WriteLine(Char value) { char buf[2] = { static_cast<char>(value.GetChar()), 0 }; InternalWriteLine(String(buf)); }
void Console::WriteLine(double value) { InternalWriteLine(String(std::to_string(value).c_str())); }
void Console::WriteLine(int value) { InternalWriteLine(String(std::to_string(value).c_str())); }
void Console::WriteLine(long value) { InternalWriteLine(String(std::to_string(value).c_str())); }
void Console::WriteLine(long long value) { InternalWriteLine(String(std::to_string(value).c_str())); }
void Console::WriteLine(float value) { InternalWriteLine(String(std::to_string(value).c_str())); }
void Console::WriteLine(const String& value) { InternalWriteLine(value); }
void Console::WriteLine(const char* value) { InternalWriteLine(String(value)); }
int Console::Read() {
std::lock_guard<std::recursive_mutex> lk(s_mutex);
if (s_pInReader) {
return s_pInReader->Read();
}
return std::cin.get();
}
String Console::ReadLine() {
std::lock_guard<std::recursive_mutex> lk(s_mutex);
if (s_pInReader) {
return s_pInReader->ReadLine();
}
if (!s_inputs.empty()) {
String v = s_inputs.front();
s_inputs.erase(s_inputs.begin());
return v;
}
std::string line;
std::getline(std::cin, line);
return String(line.c_str());
}
int Console::GetCursorLeft() {
#if defined(_WIN32)
CONSOLE_SCREEN_BUFFER_INFO csbi;
if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)) {
return csbi.dwCursorPosition.X;
}
#endif
return 0;
}
void Console::SetCursorLeft(int left) {
SetCursorPosition(left, GetCursorTop());
}
int Console::GetCursorTop() {
#if defined(_WIN32)
CONSOLE_SCREEN_BUFFER_INFO csbi;
if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)) {
return csbi.dwCursorPosition.Y;
}
#endif
return 0;
}
void Console::SetCursorTop(int top) {
SetCursorPosition(GetCursorLeft(), top);
}
void Console::SetCursorPosition(int left, int top) {
#if defined(_WIN32)
COORD coord = { (SHORT)left, (SHORT)top };
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
#else
// ANSI escape: \033[{row};{col}H (1-indexed)
std::cout << "\033[" << (top + 1) << ";" << (left + 1) << "H" << std::flush;
#endif
}
bool Console::GetCursorVisible() {
#if defined(_WIN32)
CONSOLE_CURSOR_INFO cci;
if (GetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cci)) {
return cci.bVisible != FALSE;
}
#endif
return true;
}
void Console::SetCursorVisible(bool visible) {
#if defined(_WIN32)
CONSOLE_CURSOR_INFO cci;
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
if (GetConsoleCursorInfo(hConsole, &cci)) {
cci.bVisible = visible;
SetConsoleCursorInfo(hConsole, &cci);
}
#else
if (visible) std::cout << "\033[?25h" << std::flush;
else std::cout << "\033[?25l" << std::flush;
#endif
}
int Console::GetWindowHeight() {
#if defined(_WIN32)
CONSOLE_SCREEN_BUFFER_INFO csbi;
if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)) {
return csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
}
#endif
return 25; // Standard fallback
}
void Console::SetWindowHeight(int height) {
#if defined(_WIN32)
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO csbi;
if (GetConsoleScreenBufferInfo(hConsole, &csbi)) {
SMALL_RECT rect = csbi.srWindow;
rect.Bottom = rect.Top + (SHORT)height - 1;
SetConsoleWindowInfo(hConsole, TRUE, &rect);
}
#endif
}
int Console::GetWindowWidth() {
#if defined(_WIN32)
CONSOLE_SCREEN_BUFFER_INFO csbi;
if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)) {
return csbi.srWindow.Right - csbi.srWindow.Left + 1;
}
#endif
return 80; // Standard fallback
}
void Console::SetWindowWidth(int width) {
#if defined(_WIN32)
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO csbi;
if (GetConsoleScreenBufferInfo(hConsole, &csbi)) {
SMALL_RECT rect = csbi.srWindow;
rect.Right = rect.Left + (SHORT)width - 1;
SetConsoleWindowInfo(hConsole, TRUE, &rect);
}
#endif
}
bool Console::GetCapsLock() {
#if defined(_WIN32)
return (GetKeyState(VK_CAPITAL) & 0x0001) != 0;
#endif
return false;
}
bool Console::GetNumberLock() {
#if defined(_WIN32)
return (GetKeyState(VK_NUMLOCK) & 0x0001) != 0;
#endif
return false;
}
ConsoleColor Console::GetBackgroundColor() {
EnsureColorsInitialized();
#if defined(_WIN32)
CONSOLE_SCREEN_BUFFER_INFO csbi;
if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)) {
return (ConsoleColor)((csbi.wAttributes & 0xF0) >> 4);
}
#endif
return s_currentBack;
}
void Console::SetBackgroundColor(ConsoleColor color) {
EnsureColorsInitialized();
s_currentBack = color;
#if defined(_WIN32)
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO csbi;
if (GetConsoleScreenBufferInfo(hConsole, &csbi)) {
WORD attributes = (csbi.wAttributes & 0xFF0F) | (((WORD)color << 4) & 0x00F0);
SetConsoleTextAttribute(hConsole, attributes);
}
#else
static const int ansiBack[] = { 40, 44, 42, 46, 41, 45, 43, 47, 100, 104, 102, 106, 101, 105, 103, 107 };
int idx = static_cast<int>(color);
if (idx >= 0 && idx < 16) {
std::cout << "\033[" << ansiBack[idx] << "m" << std::flush;
}
#endif
}
ConsoleColor Console::GetForegroundColor() {
EnsureColorsInitialized();
#if defined(_WIN32)
CONSOLE_SCREEN_BUFFER_INFO csbi;
if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)) {
return (ConsoleColor)(csbi.wAttributes & 0x0F);
}
#endif
return s_currentFore;
}
void Console::SetForegroundColor(ConsoleColor color) {
EnsureColorsInitialized();
s_currentFore = color;
#if defined(_WIN32)
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO csbi;
if (GetConsoleScreenBufferInfo(hConsole, &csbi)) {
WORD attributes = (csbi.wAttributes & 0xFFF0) | ((WORD)color & 0x000F);
SetConsoleTextAttribute(hConsole, attributes);
}
#else
static const int ansiFore[] = { 30, 34, 32, 36, 31, 35, 33, 37, 90, 94, 92, 96, 91, 95, 93, 97 };
int idx = static_cast<int>(color);
if (idx >= 0 && idx < 16) {
std::cout << "\033[" << ansiFore[idx] << "m" << std::flush;
}
#endif
}
void Console::ResetColor() {
EnsureColorsInitialized();
SetForegroundColor(s_defaultFore);
SetBackgroundColor(s_defaultBack);
#if !defined(_WIN32)
std::cout << "\033[0m" << std::flush;
#endif
}
String Console::GetTitle() {
#if defined(_WIN32)
wchar_t title[MAX_PATH];
if (GetConsoleTitleW(title, MAX_PATH)) {
return String(WCharToUtf8(title).c_str());
}
#endif
return s_consoleTitle;
}
void Console::SetTitle(const String& title) {
s_consoleTitle = title;
#if defined(_WIN32)
::SetConsoleTitleW(Utf8ToWChar(title.GetRawString()).c_str());
#else
std::cout << "\033]0;" << title.GetRawString() << "\007" << std::flush;
#endif
}
void Console::Beep() {
#if defined(_WIN32)
::Beep(800, 200);
#else
std::cout << "\a" << std::flush;
#endif
}
static void ClearConsoleScreenNative() {
#if defined(_WIN32)
HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO csbi;
DWORD count, cellCount;
COORD home = { 0, 0 };
if (GetConsoleScreenBufferInfo(h, &csbi)) {
cellCount = csbi.dwSize.X * csbi.dwSize.Y;
FillConsoleOutputCharacterW(h, (wchar_t)' ', cellCount, home, &count);
FillConsoleOutputAttribute(h, csbi.wAttributes, cellCount, home, &count);
SetConsoleCursorPosition(h, home);
}
#else
std::cout << "\033[2J\033[1;1H" << std::flush;
#endif
}
void Console::Clear() {
std::lock_guard<std::recursive_mutex> lk(s_mutex);
s_outputs.clear();
s_inputs.clear();
s_accumulator = String("");
s_pOutWriter = nullptr;
s_pErrorWriter = nullptr;
s_pInReader = nullptr;
ClearConsoleScreenNative();
}
void Console::SetOut(const SmartPointer<IO::TextWriter>& pOutWriter) {
std::lock_guard<std::recursive_mutex> lk(s_mutex);
s_pOutWriter = pOutWriter;
}
void Console::SetError(const SmartPointer<IO::TextWriter>& pErrorWriter) {
std::lock_guard<std::recursive_mutex> lk(s_mutex);
s_pErrorWriter = pErrorWriter;
}
void Console::SetIn(const SmartPointer<IO::TextReader>& pInReader) {
if (!pInReader) {
throw ArgumentException("pInReader cannot be null.");
}
std::lock_guard<std::recursive_mutex> lk(s_mutex);
s_pInReader = pInReader;
}
SmartPointer<IO::TextWriter> Console::Out() {
std::lock_guard<std::recursive_mutex> lk(s_mutex);
return s_pOutWriter;
}
SmartPointer<IO::TextWriter> Console::Error() {
std::lock_guard<std::recursive_mutex> lk(s_mutex);
return s_pErrorWriter;
}
SmartPointer<IO::TextReader> Console::In() {
std::lock_guard<std::recursive_mutex> lk(s_mutex);
return s_pInReader;
}
void Console::SetIn(const String& value) {
std::lock_guard<std::recursive_mutex> lk(s_mutex);
s_inputs.push_back(value);
}
void Console::ClearInputs() {
std::lock_guard<std::recursive_mutex> lk(s_mutex);
s_inputs.clear();
}
Array<String> Console::GetOutputs() {
std::lock_guard<std::recursive_mutex> lock(s_mutex);
Array<String> result((int)s_outputs.size());
for (int i = 0; i < (int)s_outputs.size(); i++) result[i] = s_outputs[i];
return result;
}