using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using MmdPdf.Services;
namespace MmdPdf.Features.Scan
{
///
/// Asks the copier for pages. The person picks the machine, the feed, how much ink and how
/// much detail; everything else is the copier's own default. The pages come back as image
/// files which MainWindow turns into one document.
///
/// A run cannot be interrupted mid-sheet - that is the copier's hardware, not a decision made
/// here - so Cancel stops after the sheet in progress and keeps the pages already read.
///
public partial class ScanDialog : Window
{
private readonly List _scanners;
private CancellationTokenSource? _cancel;
private bool _running;
/// Image files, one per page, in the order the copier read them.
internal string[] Pages { get; private set; } = Array.Empty();
/// The person asked for the words to be made searchable afterwards.
internal bool MakeSearchable { get; private set; }
/// The person asked for crooked pages to be straightened afterwards.
internal bool Straighten { get; private set; }
public ScanDialog()
{
InitializeComponent();
_scanners = ScanService.IsAvailable ? ScanService.ListScanners() : new List();
foreach (var s in _scanners) CopierBox.Items.Add(s.Name);
if (_scanners.Count > 0)
{
CopierBox.SelectedIndex = 0;
}
else
{
CopierBox.Items.Add(ScanService.IsAvailable
? "No copier found on this machine"
: "This machine has no imaging service");
CopierBox.SelectedIndex = 0;
CopierBox.IsEnabled = false;
ScanBtn.IsEnabled = false;
FootNote.Text = "Switch the copier on, or connect it, then open this again";
}
FeedBox.Items.Add("Feeder, one side");
FeedBox.Items.Add("Feeder, both sides");
FeedBox.Items.Add("Glass, one sheet");
FeedBox.SelectedIndex = 0;
ColourBox.Items.Add("Black and white");
ColourBox.Items.Add("Shades of grey");
ColourBox.Items.Add("Colour");
ColourBox.SelectedIndex = 0;
DetailBox.Items.Add("200 dpi, quick");
DetailBox.Items.Add("300 dpi, good for text");
DetailBox.Items.Add("600 dpi, fine detail");
DetailBox.SelectedIndex = 1;
foreach (var p in ScanService.PaperSizes) PaperBox.Items.Add(p);
PaperBox.SelectedIndex = 0;
}
private void TitleBar_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (e.ButtonState == MouseButtonState.Pressed) DragMove();
}
private void Cancel_Click(object sender, RoutedEventArgs e)
{
if (_running)
{
_cancel?.Cancel();
FootNote.Text = "Stopping after this sheet…";
return;
}
DialogResult = false;
Close();
}
private async void Scan_Click(object sender, RoutedEventArgs e)
{
if (_running || _scanners.Count == 0) return;
var device = _scanners[Math.Max(0, CopierBox.SelectedIndex)];
var feed = FeedBox.SelectedIndex switch
{
1 => ScanFeed.FeederBothSides,
2 => ScanFeed.Flatbed,
_ => ScanFeed.FeederOneSide,
};
var colour = ColourBox.SelectedIndex switch
{
1 => ScanColour.Greyscale,
2 => ScanColour.Colour,
_ => ScanColour.BlackAndWhite,
};
int dpi = DetailBox.SelectedIndex switch
{
0 => 200,
2 => 600,
_ => 300,
};
string paper = PaperBox.SelectedItem as string ?? "A4 portrait";
MakeSearchable = SearchableBox.IsChecked == true;
Straighten = StraightenBox.IsChecked == true;
_running = true;
_cancel = new CancellationTokenSource();
ScanBtn.IsEnabled = false;
CancelBtn.Content = "Stop";
SetInputsEnabled(false);
FootNote.Text = feed == ScanFeed.Flatbed ? "Reading the sheet on the glass…" : "Reading…";
var progress = new Progress(n =>
FootNote.Text = n == 1 ? "1 page read" : n.ToString(CultureInfo.InvariantCulture) + " pages read");
try
{
var token = _cancel.Token;
var reporter = (IProgress)progress;
string[] pages = await Task.Run(() =>
ScanService.Acquire(device.DeviceId, feed, colour, dpi, paper,
maxPages: feed == ScanFeed.Flatbed ? 1 : 200,
cancel: token,
onPage: reporter.Report).ToArray(), token);
Pages = pages;
if (pages.Length == 0)
{
FootNote.Text = "The copier returned no pages";
ResetAfterRun();
return;
}
DialogResult = true;
Close();
}
catch (OperationCanceledException)
{
FootNote.Text = "Stopped";
ResetAfterRun();
}
catch (Exception ex)
{
FootNote.Text = "The copier could not be used: " + ex.Message;
ResetAfterRun();
}
}
private void ResetAfterRun()
{
_running = false;
_cancel?.Dispose();
_cancel = null;
ScanBtn.IsEnabled = _scanners.Count > 0;
CancelBtn.Content = "Cancel";
SetInputsEnabled(true);
}
private void SetInputsEnabled(bool on)
{
CopierBox.IsEnabled = on && _scanners.Count > 0;
FeedBox.IsEnabled = on;
ColourBox.IsEnabled = on;
DetailBox.IsEnabled = on;
PaperBox.IsEnabled = on;
SearchableBox.IsEnabled = on;
StraightenBox.IsEnabled = on;
}
}
}