using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.IO;
using System.Runtime.CompilerServices;
using Futonizer.Services;
namespace Futonizer.Models;
///
/// The four states shown as an icon in the file queue list.
///
public enum FileLoadState
{
/// Currently being scanned with mkvmerge -J.
Loading,
/// Scan finished (or failed) but track selection isn't evaluated as correct/incorrect yet.
Loaded,
/// Exactly one audio track and one subtitle track are selected to be kept.
Correct,
/// Anything other than exactly one audio + one subtitle selected.
Incorrect,
}
///
/// Represents one MKV file in the processing queue.
///
public class QueuedFileItem : INotifyPropertyChanged
{
private string _status = string.Empty;
private bool _isScanning;
private bool _hasError;
public string FilePath { get; }
public string FileName => Path.GetFileName(FilePath);
///
/// The filename as it will appear after rename adjustments (release-tag prefix
/// and/or show-name injection). Equals when no
/// changes would be applied.
///
public string DisplayFileName => FileNameHelper.ComputeOutputFileName(FilePath);
public string FileSizeDisplay { get; }
public ObservableCollection Tracks { get; } = new();
public string Status
{
get => _status;
set { _status = value; OnPropertyChanged(); }
}
public bool IsScanning
{
get => _isScanning;
set { _isScanning = value; OnPropertyChanged(); NotifyLoadState(); }
}
public bool HasError
{
get => _hasError;
set { _hasError = value; OnPropertyChanged(); NotifyLoadState(); }
}
public FileLoadState LoadState
{
get
{
if (_isScanning) return FileLoadState.Loading;
if (_hasError || Tracks.Count == 0) return FileLoadState.Loaded;
int audioSelected = Tracks.Count(t => t.Type == "audio" && t.Copy);
int subtitleSelected = Tracks.Count(t => t.Type == "subtitles" && t.Copy);
return audioSelected == 1 && subtitleSelected == 1
? FileLoadState.Correct
: FileLoadState.Incorrect;
}
}
public bool IsLoading => LoadState == FileLoadState.Loading;
public bool IsLoadedNeutral => LoadState == FileLoadState.Loaded;
public bool IsCorrect => LoadState == FileLoadState.Correct;
public bool IsIncorrect => LoadState == FileLoadState.Incorrect;
public QueuedFileItem(string filePath)
{
FilePath = filePath;
FileSizeDisplay = FormatFileSize(filePath);
Tracks.CollectionChanged += OnTracksChanged;
}
private static string FormatFileSize(string filePath)
{
try
{
long bytes = new FileInfo(filePath).Length;
string[] units = { "B", "KB", "MB", "GB", "TB" };
double size = bytes;
int unitIndex = 0;
while (size >= 1024 && unitIndex < units.Length - 1)
{
size /= 1024;
unitIndex++;
}
return unitIndex == 0
? $"{size:0} {units[unitIndex]}"
: $"{size:0.#} {units[unitIndex]}";
}
catch
{
return string.Empty;
}
}
private void OnTracksChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null)
{
foreach (TrackItem t in e.NewItems)
t.PropertyChanged += TrackPropertyChanged;
}
if (e.OldItems != null)
{
foreach (TrackItem t in e.OldItems)
t.PropertyChanged -= TrackPropertyChanged;
}
NotifyLoadState();
}
private void TrackPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(TrackItem.Copy))
NotifyLoadState();
}
private void NotifyLoadState()
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(LoadState)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsLoading)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsLoadedNeutral)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsCorrect)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsIncorrect)));
}
public event PropertyChangedEventHandler? PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string? name = null)
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}