using Dinah.Core.Threading; using LibationWinForms.BookLiberation; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using System.Windows.Forms; namespace LibationWinForms.ProcessQueue { internal partial class ProcessQueueControl : UserControl, ILogForm { private TrackedQueue Queue = new(); private readonly LogMe Logger; private int QueuedCount { set { queueNumberLbl.Text = value.ToString(); queueNumberLbl.Visible = value > 0; } } private int ErrorCount { set { errorNumberLbl.Text = value.ToString(); errorNumberLbl.Visible = value > 0; } } private int CompletedCount { set { completedNumberLbl.Text = value.ToString(); completedNumberLbl.Visible = value > 0; } } public Task QueueRunner { get; private set; } public bool Running => !QueueRunner?.IsCompleted ?? false; public ToolStripButton popoutBtn = new(); public ProcessQueueControl() { InitializeComponent(); Logger = LogMe.RegisterForm(this); runningTimeLbl.Text = string.Empty; popoutBtn.DisplayStyle = ToolStripItemDisplayStyle.Text; popoutBtn.Name = "popoutBtn"; popoutBtn.Text = "Pop Out"; popoutBtn.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; popoutBtn.Alignment = ToolStripItemAlignment.Right; popoutBtn.Anchor = AnchorStyles.Bottom | AnchorStyles.Right; statusStrip1.Items.Add(popoutBtn); virtualFlowControl2.RequestData += VirtualFlowControl1_RequestData; virtualFlowControl2.ButtonClicked += VirtualFlowControl2_ButtonClicked; Queue.QueuededCountChanged += Queue_QueuededCountChanged; Queue.CompletedCountChanged += Queue_CompletedCountChanged; QueuedCount = 0; ErrorCount = 0; CompletedCount = 0; } public void AddDownloadDecrypt(IEnumerable entries) { foreach (var entry in entries) AddDownloadDecrypt(entry); } public void AddConvertMp3(IEnumerable entries) { foreach (var entry in entries) AddConvertMp3(entry); } public void AddDownloadDecrypt(GridEntry gridEntry) { if (Queue.Any(b => b?.LibraryBook?.Book?.AudibleProductId == gridEntry.AudibleProductId)) return; ProcessBook pbook = new(gridEntry.LibraryBook, gridEntry.Cover, Logger); pbook.DataAvailable += Pbook_DataAvailable; pbook.AddDownloadDecryptBook(); pbook.AddDownloadPdf(); Queue.Enqueue(pbook); if (!Running) { QueueRunner = QueueLoop(); } } public void AddConvertMp3(GridEntry gridEntry) { if (Queue.Any(b => b?.LibraryBook?.Book?.AudibleProductId == gridEntry.AudibleProductId)) return; ProcessBook pbook = new(gridEntry.LibraryBook, gridEntry.Cover, Logger); pbook.DataAvailable += Pbook_DataAvailable; pbook.AddConvertToMp3(); Queue.Enqueue(pbook); if (!Running) { QueueRunner = QueueLoop(); } } DateTime StartintTime; private async Task QueueLoop() { StartintTime = DateTime.Now; counterTimer.Start(); while (Queue.MoveNext()) { var nextBook = Queue.Current; var result = await nextBook.ProcessOneAsync(); if (result == ProcessBookResult.FailedRetry) Queue.Enqueue(nextBook); else if (result == ProcessBookResult.FailedAbort) return; } Queue_CompletedCountChanged(this, 0); counterTimer.Stop(); } public void WriteLine(string text) { if (!IsDisposed) logMeTbox.UIThreadAsync(() => logMeTbox.AppendText($"{DateTime.Now} {text}{Environment.NewLine}")); } #region Control event handlers private void Queue_CompletedCountChanged(object sender, int e) { int errCount = Queue.Completed.Count(p => p.Result is ProcessBookResult.FailedAbort or ProcessBookResult.FailedSkip or ProcessBookResult.ValidationFail); int completeCount = Queue.Completed.Count(p => p.Result is ProcessBookResult.Success); ErrorCount = errCount; CompletedCount = completeCount; UpdateProgressBar(); } private void Queue_QueuededCountChanged(object sender, int cueCount) { QueuedCount = cueCount; virtualFlowControl2.VirtualControlCount = Queue.Count; UpdateProgressBar(); } private void UpdateProgressBar() { toolStripProgressBar1.Maximum = Queue.Count; toolStripProgressBar1.Value = Queue.Completed.Count; } private void cancelAllBtn_Click(object sender, EventArgs e) { Queue.ClearQueue(); Queue.Current?.Cancel(); virtualFlowControl2.VirtualControlCount = Queue.Count; UpdateAllControls(); } private void btnClearFinished_Click(object sender, EventArgs e) { Queue.ClearCompleted(); virtualFlowControl2.VirtualControlCount = Queue.Count; UpdateAllControls(); if (!Running) runningTimeLbl.Text = string.Empty; } private void CounterTimer_Tick(object sender, EventArgs e) { string timeToStr(TimeSpan time) { string minsSecs = $"{time:mm\\:ss}"; if (time.TotalHours >= 1) return $"{time.TotalHours:F0}:{minsSecs}"; return minsSecs; } if (Running) runningTimeLbl.Text = timeToStr(DateTime.Now - StartintTime); } private void clearLogBtn_Click(object sender, EventArgs e) { logMeTbox.Clear(); } #endregion #region View-Model update event handling /// /// Index of the first visible in the /// private int FirstVisible = 0; /// /// Number of visible in the /// private int NumVisible = 0; /// /// Controls displaying the state, starting with /// private IReadOnlyList Panels; /// /// Updates the display of a single at within /// /// index of the within the private void UpdateControl(int queueIndex) { int i = queueIndex - FirstVisible; if (i > NumVisible || i < 0) return; var proc = Queue[queueIndex]; Panels[i].Invoke(() => { Panels[i].SuspendLayout(); Panels[i].SetCover(proc.Cover); Panels[i].SetBookInfo(proc.BookText); if (proc.Result != ProcessBookResult.None) { Panels[i].SetResult(proc.Result); return; } Panels[i].SetStatus(proc.Status); Panels[i].SetProgrss(proc.Progress); Panels[i].SetRemainingTime(proc.TimeRemaining); Panels[i].ResumeLayout(); }); } private void UpdateAllControls() { int numToShow = Math.Min(NumVisible, Queue.Count - FirstVisible); for (int i = 0; i < numToShow; i++) UpdateControl(FirstVisible + i); } /// /// View notified the model that a botton was clicked /// /// index of the within /// The clicked control to update private void VirtualFlowControl2_ButtonClicked(int queueIndex, string buttonName, ProcessBookControl panelClicked) { ProcessBook item = Queue[queueIndex]; if (buttonName == "cancelBtn") { item.Cancel(); Queue.RemoveQueued(item); virtualFlowControl2.VirtualControlCount = Queue.Count; UpdateControl(queueIndex); } else if (buttonName == "moveFirstBtn") { Queue.MoveQueuePosition(item, QueuePosition.Fisrt); UpdateAllControls(); } else if (buttonName == "moveUpBtn") { Queue.MoveQueuePosition(item, QueuePosition.OneUp); UpdateControl(queueIndex - 1); UpdateControl(queueIndex); } else if (buttonName == "moveDownBtn") { Queue.MoveQueuePosition(item, QueuePosition.OneDown); UpdateControl(queueIndex + 1); UpdateControl(queueIndex); } else if (buttonName == "moveLastBtn") { Queue.MoveQueuePosition(item, QueuePosition.Last); UpdateAllControls(); } } /// /// View needs updating /// private void VirtualFlowControl1_RequestData(int firstIndex, int numVisible, IReadOnlyList panelsToFill) { FirstVisible = firstIndex; NumVisible = numVisible; Panels = panelsToFill; UpdateAllControls(); } /// /// Model updates the view /// private void Pbook_DataAvailable(object sender, EventArgs e) { int index = Queue.IndexOf((ProcessBook)sender); UpdateControl(index); } #endregion } }