using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
using MPF.Core.Data;
using MPF.UI.Core.UserControls;
namespace MPF.UI.Core.ViewModels
{
public class LogViewModel
{
#region Fields
///
/// Parent OptionsWindow object
///
public LogOutput Parent { get; private set; }
#endregion
#region Private State Variables
///
/// Paragraph backing the log
///
private readonly Paragraph _paragraph;
///
/// Cached value of the last line written
///
private Run lastLine = null;
///
/// Queue of items that need to be logged
///
private readonly ProcessingQueue logQueue;
#endregion
///
/// Constructor
///
public LogViewModel(LogOutput parent)
{
Parent = parent;
// Add handlers
Parent.OutputViewer.SizeChanged += OutputViewerSizeChanged;
Parent.Output.TextChanged += OnTextChanged;
Parent.ClearButton.Click += OnClearButton;
Parent.SaveButton.Click += OnSaveButton;
// Update the internal state
var document = new FlowDocument()
{
Background = new SolidColorBrush(Color.FromArgb(0xFF, 0x20, 0x20, 0x20))
};
_paragraph = new Paragraph();
document.Blocks.Add(_paragraph);
Parent.Output.Document = document;
logQueue = new ProcessingQueue(ProcessLogLine);
}
#region Logging
///
/// Log level for output
///
public enum LogLevel
{
USER,
VERBOSE,
ERROR,
SECRET,
}
///
/// Log line wrapper
///
private struct LogLine
{
public readonly string Text;
public readonly LogLevel LogLevel;
public LogLine(string text, LogLevel logLevel)
{
this.Text = text;
this.LogLevel = logLevel;
}
///
/// Get the foreground Brush for the current LogLevel
///
/// Brush representing the color
public Brush GetForegroundColor()
{
switch (this.LogLevel)
{
case LogLevel.SECRET:
return Brushes.Blue;
case LogLevel.ERROR:
return Brushes.Red;
case LogLevel.VERBOSE:
return Brushes.Yellow;
case LogLevel.USER:
default:
return Brushes.White;
}
}
///
/// Generate a Run object from the current LogLine
///
/// Run object based on internal values
public Run GenerateRun()
{
return new Run { Text = this.Text, Foreground = GetForegroundColor() };
}
}
///
/// Enqueue text to the log
///
/// Text to write to the log
public void Log(string text) => LogInternal(text, LogLevel.USER);
///
/// Enqueue text with a newline to the log
///
/// Text to write to the log
public void LogLn(string text) => Log(text + "\n");
///
/// Enqueue error text to the log
///
/// Text to write to the log
public void ErrorLog(string text) => LogInternal(text, LogLevel.ERROR);
///
/// Enqueue error text with a newline to the log
///
/// Text to write to the log
public void ErrorLogLn(string text) => ErrorLog(text + "\n");
///
/// Enqueue secret text to the log
///
/// Text to write to the log
public void SecretLog(string text) => LogInternal(text, LogLevel.SECRET);
///
/// Enqueue secret text with a newline to the log
///
/// Text to write to the log
public void SecretLogLn(string text) => SecretLog(text + "\n");
///
/// Enqueue verbose text to the log
///
/// Text to write to the log
public void VerboseLog(string text) => LogInternal(text, LogLevel.VERBOSE);
///
/// Enqueue verbose text with a newline to the log
///
/// Text to write to the log
public void VerboseLogLn(string text) => VerboseLog(text + "\n");
///
/// Reset the progress bar state
///
public void ResetProgressBar()
{
Parent.Dispatcher.Invoke(() =>
{
Parent.ProgressBar.Value = 0;
Parent.ProgressLabel.Text = string.Empty;
});
}
///
/// Enqueue text to the log with formatting
///
/// Text to write to the log
/// LogLevel for the log
private void LogInternal(string text, LogLevel logLevel)
{
// Null text gets ignored
if (text == null)
return;
// Enqueue the text
logQueue.Enqueue(new LogLine(text, logLevel));
}
///
/// Process the log lines in the queue
///
/// LogLine item to process
private void ProcessLogLine(LogLine nextLogLine)
{
// Null text gets ignored
string nextText = Parent.Dispatcher.Invoke(() => nextLogLine.Text);
if (nextText == null)
return;
try
{
if (nextText.StartsWith("\r"))
ReplaceLastLine(nextLogLine);
else
AppendToTextBox(nextLogLine);
}
catch (Exception ex)
{
// In the event that something fails horribly, we want to log
AppendToTextBox(new LogLine(ex.ToString(), LogLevel.ERROR));
}
}
///
/// Append log line to the log text box
///
/// LogLine value to append
private void AppendToTextBox(LogLine logLine)
{
Parent.Dispatcher.Invoke(() =>
{
var run = logLine.GenerateRun();
_paragraph.Inlines.Add(run);
lastLine = run;
});
}
///
/// Replace the last line written to the log text box
///
/// LogLine value to append
private void ReplaceLastLine(LogLine logLine)
{
Parent.Dispatcher.Invoke(() =>
{
lastLine.Text = logLine.Text;
lastLine.Foreground = logLine.GetForegroundColor();
});
}
#endregion
#region Helpers
///
/// Scroll the current view to the bottom
///
public void ScrollToBottom()
{
Parent.OutputViewer.ScrollToBottom();
}
#endregion
#region EventHandlers
private void OnClearButton(object sender, EventArgs e)
{
_paragraph.Inlines.Clear();
ResetProgressBar();
}
private void OnSaveButton(object sender, EventArgs e)
{
using (StreamWriter tw = new StreamWriter(File.OpenWrite("console.log")))
{
foreach (var inline in _paragraph.Inlines)
{
if (inline is Run run)
tw.Write(run.Text);
}
}
}
private void OnTextChanged(object sender, TextChangedEventArgs e) =>
ScrollToBottom();
private void OutputViewerSizeChanged(object sender, SizeChangedEventArgs e) =>
ScrollToBottom();
#endregion
}
}