using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Runtime.CompilerServices;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using com.sun.org.glassfish.gmbal;
using FastReport.DataVisualization.Charting;
using Microsoft.Win32;
using NPOI.XWPF.Usermodel;
namespace InABox.Wpf.Console;
public static class ItemsControlExtensions
{
public static void ScrollIntoView(
this ItemsControl control,
object item)
{
var framework =
control.ItemContainerGenerator.ContainerFromItem(item)
as FrameworkElement;
if (framework == null) return;
framework.BringIntoView();
}
public static void ScrollIntoView(this ItemsControl control)
{
var count = control.Items.Count;
if (count == 0) return;
var item = control.Items[count - 1];
control.ScrollIntoView(item);
}
}
///
/// Interaction logic for Console.xaml
///
public partial class ConsoleControl : UserControl, INotifyPropertyChanged
{
public static readonly DependencyProperty EnabledProperty
= DependencyProperty.Register(nameof(Enabled), typeof(bool), typeof(ConsoleControl));
private CollectionViewSource _filtered;
public CollectionViewSource Filtered
{
get => _filtered;
set
{
_filtered = value;
OnPropertyChanged();
}
}
public readonly ObservableCollection LogEntries;
private readonly TimeSpan regexTimeOut = TimeSpan.FromMilliseconds(100);
private Regex? searchRegex;
public event PropertyChangedEventHandler? PropertyChanged;
public event Action? OnLoadLog;
public event Action? OnCloseLog;
public bool _allowLoadLogButton = true;
public bool AllowLoadLogButton
{
get => _allowLoadLogButton;
set
{
_allowLoadLogButton = value;
OnPropertyChanged();
OnPropertyChanged(nameof(ShowLoadLogButton));
OnPropertyChanged(nameof(ShowCloseLogButton));
}
}
private bool _loadedLog = false;
public bool LoadedLog
{
get => _loadedLog;
set
{
_loadedLog = value;
OnPropertyChanged();
OnPropertyChanged(nameof(ShowLoadLogButton));
OnPropertyChanged(nameof(ShowCloseLogButton));
}
}
public bool ShowLoadLogButton => !LoadedLog && AllowLoadLogButton;
public bool ShowCloseLogButton => LoadedLog && AllowLoadLogButton;
public bool Enabled
{
get => (bool)GetValue(EnabledProperty);
set => SetValue(EnabledProperty, value);
}
public ConsoleControl()
{
InitializeComponent();
Filtered = new CollectionViewSource();
LogEntries = new ObservableCollection();
Filtered.Source = LogEntries;
Filtered.Filter += (sender, args) =>
{
var logEntry = (LogEntry)args.Item;
if (ShowImportant.IsChecked == true && !IsImportant(logEntry))
{
args.Accepted = false;
return;
}
if (UseRegEx.IsChecked == true && searchRegex != null)
args.Accepted = string.IsNullOrWhiteSpace(Search.Text)
|| searchRegex.IsMatch(logEntry.DateTime)
|| searchRegex.IsMatch(logEntry.Type)
|| searchRegex.IsMatch(logEntry.User)
|| searchRegex.IsMatch(logEntry.Message);
else
args.Accepted = string.IsNullOrWhiteSpace(Search.Text)
|| logEntry.DateTime.Contains(Search.Text)
|| logEntry.Type.Contains(Search.Text)
|| logEntry.User.Contains(Search.Text)
|| logEntry.Message.Contains(Search.Text);
};
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private static bool IsImportant(LogEntry entry)
{
return entry.Type == "IMPTNT";
}
// DATE TRANSACTION TYPE USERID MESSAGE
[GeneratedRegex("(\\d\\d:\\d\\d:\\d\\d\\.\\d\\d\\d)\\s*([0-9a-zA-Z]{8}(?:\\-[0-9a-zA-Z]{4}){3}\\-[0-9a-zA-Z]{12})?\\s*(\\S+) (.+)", RegexOptions.Singleline)]
internal static partial Regex LogEntryParseRegex();
public static LogEntry? ParseLogMessage(string logLine)
{
var match = LogEntryParseRegex().Match(logLine);
if (match.Success)
{
var date = match.Groups[1].Value;
if (!Guid.TryParse(match.Groups[2].Value, out var transaction))
{
transaction = Guid.Empty;
}
var type = match.Groups[3].Value;
var remaining = match.Groups[4].Value;
var user = remaining.Length >= 12 ? remaining[..12] : "";
var msg = remaining.Length >= 12 ? remaining[13..] : "";
return new LogEntry
{
DateTime = date,
Type = type,
User = user,
Message = msg,
Transaction = transaction
};
}
else
{
return null;
}
}
public void LoadLogEntries(IEnumerable lines)
{
var logEntries = new List();
var numberSkipped = 0;
foreach (var line in lines)
{
var logEntry = ParseLogMessage(line ?? "");
if(logEntry is not null)
{
var logType = logEntry.Type;
if (logType == "ERROR" || logType == "INFO" || logType == "IMPTNT")
logEntries.Add(logEntry);
else if (string.IsNullOrWhiteSpace(logType)) numberSkipped++;
}
}
if (numberSkipped > 0)
{
if (logEntries.Count == 0)
SetErrorMessage("File does not contain valid log information!");
else
SetErrorMessage(string.Format("Skipped {0} lines that did not contain valid log information", numberSkipped));
}
Filtered.Source = logEntries;
}
public void LoadLogEntry(string line)
{
var logEntry = ParseLogMessage(line);
if(logEntry is not null)
{
var logType = logEntry.Type;
if (logType == "INFO" || logType == "ERROR" || logType == "IMPTNT")
{
LogEntries.Insert(0, logEntry);
}
}
}
private void Search_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter && UseRegEx.IsChecked == true)
{
try
{
searchRegex = new Regex(Search.Text, RegexOptions.Compiled, regexTimeOut);
}
catch (ArgumentException)
{
searchRegex = null;
}
Filtered.View.Refresh();
SetSearchStyleNormal();
}
}
private void SetSearchStyleChanged()
{
Search.Background = Brushes.White;
}
private void SetSearchStyleNormal()
{
Search.Background = Brushes.LightYellow;
}
private void Search_TextChanged(object sender, TextChangedEventArgs e)
{
if (UseRegEx.IsChecked != true)
{
Filtered.View.Refresh();
}
else
{
if (string.IsNullOrWhiteSpace(Search.Text))
{
searchRegex = null;
Filtered.View.Refresh();
SetSearchStyleNormal();
}
else
{
SetSearchStyleChanged();
}
}
}
private void UseRegEx_Checked(object sender, RoutedEventArgs e)
{
try
{
searchRegex = new Regex(Search.Text, RegexOptions.Compiled, regexTimeOut);
}
catch (ArgumentException ex)
{
searchRegex = null;
}
Filtered.View.Refresh();
}
private void UseRegEx_Unchecked(object sender, RoutedEventArgs e)
{
searchRegex = null;
Filtered.View.Refresh();
SetSearchStyleNormal();
}
public void SetErrorMessage(string? error)
{
if (string.IsNullOrWhiteSpace(error))
{
Error.Content = "";
Error.Visibility = Visibility.Collapsed;
}
else
{
Error.Content = error;
Error.Visibility = Visibility.Visible;
}
}
private void CloseLog_Click(object sender, RoutedEventArgs e)
{
Filtered.Source = LogEntries;
OnCloseLog?.Invoke();
}
private void ShowImportant_Checked(object sender, RoutedEventArgs e)
{
Filtered.View.Refresh();
}
private void ShowImportant_Unchecked(object sender, RoutedEventArgs e)
{
Filtered.View.Refresh();
}
private void LoadLog_Click(object sender, RoutedEventArgs e)
{
OnLoadLog?.Invoke();
}
}
public abstract class Console : Window
{
public ConsoleControl ConsoleControl { get; set; }
private readonly string Description;
public Console(string description)
{
ConsoleControl = new ConsoleControl();
ConsoleControl.OnLoadLog += ConsoleControl_OnLoadLog;
ConsoleControl.OnCloseLog += ConsoleControl_OnCloseLog;
Content = ConsoleControl;
Height = 800;
Width = 1200;
Loaded += Console_Loaded;
Closing += Console_Closing;
Title = description;
Description = description;
}
private void ConsoleControl_OnCloseLog()
{
Title = Description;
ConsoleControl.LoadedLog = false;
}
private void ConsoleControl_OnLoadLog()
{
var dialog = new OpenFileDialog
{
InitialDirectory = GetLogDirectory()
};
if (dialog.ShowDialog() == true)
{
var lines = File.ReadLines(dialog.FileName);
ConsoleControl.LoadLogEntries(lines);
ConsoleControl.LoadedLog = true;
Title = dialog.FileName;
}
}
protected virtual void OnLoaded()
{
}
protected virtual void OnClosing()
{
}
private void Console_Closing(object? sender, CancelEventArgs e)
{
OnClosing();
}
private void Console_Loaded(object sender, RoutedEventArgs e)
{
OnLoaded();
}
protected abstract string GetLogDirectory();
}