using Tofvesson.Common; using System; using System.IO; using System.Collections.Generic; using System.Text; using System.Threading; namespace Server { public static class Output { // Fancy timestamped output private static readonly object writeLock = new object(); private static readonly TextWriter stampedError = new TimeStampWriter(Console.Error, "HH:mm:ss.fff"); private static readonly TextWriter stampedOutput = new TimeStampWriter(Console.Out, "HH:mm:ss.fff"); private static bool overwrite = false; private static short readStart_x = 0, readStart_y = 0; private static bool reading = false; private static List peek = new List(); public static bool ReadingLine { get => reading; } public static Action OnNewLine { get; set; } public static void WriteLine(object message, bool error = false, bool timeStamp = true) { if (error) Error(message, true, timeStamp); else Info(message, true, timeStamp); } public static void Write(object message, bool error = false, bool timeStamp = true) { if (error) Error(message, false, timeStamp); else Info(message, false, timeStamp); } public static void WriteOverwritable(string message) { Info(message, false, false); overwrite = true; } public static void Raw(object message, bool newline = true) => Info(message, newline, false); public static void RawErr(object message, bool newline = true) => Error(message, newline, false); public static void Positive(object message, bool newline = true, bool timeStamp = true) => Write(message == null ? "null" : message.ToString(), ConsoleColor.DarkGreen, ConsoleColor.Black, newline, timeStamp ? stampedOutput : Console.Out); public static void Info(object message, bool newline = true, bool timeStamp = true) => Write(message == null ? "null" : message.ToString(), ConsoleColor.Gray, ConsoleColor.Black, newline, timeStamp ? stampedOutput : Console.Out); public static void Error(object message, bool newline = true, bool timeStamp = true) => Write(message == null ? "null" : message.ToString(), ConsoleColor.Red, ConsoleColor.Black, newline, timeStamp ? stampedOutput : Console.Out); public static void Fatal(object message, bool newline = true, bool timeStamp = true) => Write(message == null ? "null" : message.ToString(), ConsoleColor.Red, ConsoleColor.White, newline, timeStamp ? stampedError : Console.Error); private static void Write(string message, ConsoleColor f, ConsoleColor b, bool newline, TextWriter writer) { lock (writeLock) { string read = null; if (reading && newline) read = PeekLine(); if (overwrite) ClearLine(); overwrite = false; ConsoleColor f1 = Console.ForegroundColor, b1 = Console.BackgroundColor; Console.ForegroundColor = f; Console.BackgroundColor = b; writer.Write(message); readStart_y = (short)Console.CursorTop; if (newline) { writer.WriteLine(); Console.ForegroundColor = f1; Console.BackgroundColor = b1; readStart_x = 0; ++readStart_y; OnNewLine?.Invoke(); readStart_y = (short)Console.CursorTop; readStart_x = (short)Console.CursorLeft; if (reading) Console.Out.Write(read); } else { readStart_x = (short)Console.CursorLeft; Console.ForegroundColor = f1; Console.BackgroundColor = b1; } } } // Read currently entered keyboard input (even if enter hasn't been pressed) public static string PeekLine() { IEnumerator e = ConsoleReader.ReadFromBuffer(readStart_x, readStart_y, (short)((short)Console.BufferWidth - readStart_x), 1).GetEnumerator(); e.MoveNext(); Console.SetCursorPosition(readStart_x, readStart_y); StringBuilder builder = new StringBuilder(e.Current); while (builder.Length > 0 && builder[builder.Length - 1] == ' ') --builder.Length; return builder.ToString(); } public static void Clear() { string peek = PeekLine(); Console.Clear(); readStart_x = 0; readStart_y = 0; OnNewLine?.Invoke(); Console.Out.Write(peek); } public static string ReadLine() { if (reading) throw new SystemException("Cannot concurrently read line!"); reading = true; readStart_x = (short)Console.CursorLeft; readStart_y = (short)Console.CursorTop; string s = Console.ReadLine(); reading = false; overwrite = false; OnNewLine?.Invoke(); return s; } private static void ClearLine(int from = 0) { from = Math.Min(from, Console.WindowWidth); int y = Console.CursorTop; Console.SetCursorPosition(from, y); char[] msg = new char[Console.WindowWidth - from]; for (int i = 0; i < msg.Length; ++i) msg[i] = ' '; Console.Write(new string(msg)); Console.SetCursorPosition(from, y); } } }