using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
namespace JJServiceManager
{
public enum LogLevel { Info, Success, Warning, Error, Notice }
/// 로그 한 줄
public class LogLine : ObservableObject
{
private bool _isChecked;
private double _fillerHeight;
public int No { get; init; }
public DateTime Time { get; init; }
public string Text { get; init; } = "";
public LogLevel Level { get; init; }
public bool ShowTime { get; init; }
public bool IsFiller { get; init; }
public bool IsChecked { get => _isChecked; set => Set(ref _isChecked, value); }
public double FillerHeight
{
get => _fillerHeight;
set => Set(ref _fillerHeight, Math.Max(0, value));
}
public string NoText => IsFiller ? "" : No.ToString();
public string Display => IsFiller ? "" : (ShowTime ? $"[{Time:HH:mm:ss}] {Text}" : Text);
public Visibility FillerVisibility => IsFiller ? Visibility.Visible : Visibility.Collapsed;
public Visibility LineVisibility => IsFiller ? Visibility.Collapsed : Visibility.Visible;
public Brush Foreground => Level switch
{
LogLevel.Success => Brushes2.Running,
LogLevel.Warning => Brushes2.Pending,
LogLevel.Error => Brushes2.Stopped,
LogLevel.Notice => LogPanel.NoticeBrush,
_ => LogPanel.NormalBrush
};
/// 복사할 때의 텍스트
public string CopyText => ShowTime ? $"[{Time:yyyy-MM-dd HH:mm:ss}] {Text}" : Text;
}
/// 앞쪽 여러 줄을 한 번에 지울 수 있는 컬렉션 (변경 알림 1회)
public sealed class LogCollection : ObservableCollection
{
public void RemoveFirst(int count)
{
if (count <= 0) return;
CheckReentrancy();
var list = (List)Items;
list.RemoveRange(0, Math.Min(count, list.Count));
OnPropertyChanged(new System.ComponentModel.PropertyChangedEventArgs("Count"));
OnPropertyChanged(new System.ComponentModel.PropertyChangedEventArgs("Item[]"));
OnCollectionChanged(new System.Collections.Specialized.NotifyCollectionChangedEventArgs(
System.Collections.Specialized.NotifyCollectionChangedAction.Reset));
}
}
///
/// 번호 + 체크박스가 있는 로그 목록.
/// - 화면클리어: 새 로그를 화면 맨 위부터 출력, 위로 스크롤하면 이전 로그가 보임, 번호는 이어짐
/// - 로그 클리어: 화면의 로그를 모두 지우고 번호를 1번부터 다시 시작
/// - 파일은 절대 지우지 않음
///
public class LogPanel : ContentControl
{
public static readonly SolidColorBrush NormalBrush = Brushes2.Make("#212121");
public static readonly SolidColorBrush NoticeBrush = Brushes2.Make("#1565C0");
private const double LineHeight = 22;
private readonly LogCollection _lines = new LogCollection();
private readonly ListBox _list;
private readonly TextBlock _info;
private readonly DispatcherTimer _infoTimer;
private ScrollViewer _scroll;
private LogLine _filler;
private int _fillerNewLines;
private int _nextNo = 1;
private bool _scrollPending;
/// 줄 앞에 시각 표시 여부 (작업 로그 = true, 파일 로그 = false)
public bool ShowTimestamp { get; set; } = true;
/// 화면에 유지할 최대 줄 수 (오래된 줄부터 화면에서만 빠짐)
public int MaxLines { get; set; } = 20000;
public int Count => _lines.Count - (_filler != null ? 1 : 0);
public LogPanel()
{
var root = XamlLoader.LoadElement("LogPanel.xaml");
Content = root;
_list = root.Find("lstLog");
_info = root.Find("txtInfo");
_list.ItemsSource = _lines;
root.Find