using Elwig.Documents;
using Elwig.Helpers;
using Elwig.Helpers.Export;
using Elwig.Helpers.Weighing;
using Elwig.Models.Entities;
using Elwig.Services;
using Elwig.ViewModels;
using Microsoft.EntityFrameworkCore;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Threading;

namespace Elwig.Windows {
    public partial class DeliveryAdminWindow : AdministrationWindow {

        public DeliveryAdminViewModel ViewModel => (DeliveryAdminViewModel)DataContext;

        private bool IsUpdatingGradation = false;
        private readonly DispatcherTimer SecondsTimer;

        private readonly RoutedCommand CtrlF = new("CtrlF", typeof(DeliveryAdminWindow), [new KeyGesture(Key.F, ModifierKeys.Control)]);
        private readonly RoutedCommand CtrlL = new("CtrlL", typeof(DeliveryAdminWindow), [new KeyGesture(Key.L, ModifierKeys.Control)]);
        private readonly RoutedCommand CtrlP = new("CtrlP", typeof(DeliveryAdminWindow), [new KeyGesture(Key.P, ModifierKeys.Control)]);
        private readonly RoutedCommand CtrlO = new("CtrlO", typeof(DeliveryAdminWindow), [new KeyGesture(Key.O, ModifierKeys.Control)]);
        private readonly RoutedCommand CtrlJ = new("CtrlJ", typeof(DeliveryAdminWindow), [new KeyGesture(Key.J, ModifierKeys.Control)]);
        private readonly RoutedCommand CtrlQ = new("CtrlQ", typeof(DeliveryAdminWindow), [new KeyGesture(Key.Q, ModifierKeys.Control)]);
        private readonly RoutedCommand CtrlShiftP = new("CtrlShiftP", typeof(DeliveryAdminWindow), [new KeyGesture(Key.P, ModifierKeys.Control | ModifierKeys.Shift)]);
        private readonly RoutedCommand CtrlShiftO = new("CtrlShiftO", typeof(DeliveryAdminWindow), [new KeyGesture(Key.O, ModifierKeys.Control | ModifierKeys.Shift)]);

        private readonly Button[] WeighingButtons;

        private List<WineQualLevel> WineQualityLevels = [];

        public DeliveryAdminWindow(bool receipt = false) {
            InitializeComponent();
            CommandBindings.Add(new CommandBinding(CtrlF, FocusSearchInput));
            CommandBindings.Add(new CommandBinding(CtrlL, Menu_DeliveryJournal_SaveFilters_Click));
            CommandBindings.Add(new CommandBinding(CtrlP, Menu_DeliveryNote_Show_Click));
            CommandBindings.Add(new CommandBinding(CtrlO, Menu_DeliveryJournal_ShowFilters_Click));
            CommandBindings.Add(new CommandBinding(CtrlJ, Menu_DeliveryJournal_PrintToday_Click));
            CommandBindings.Add(new CommandBinding(CtrlQ, Menu_Statistics_WineQuality_PrintToday_Click));
            CommandBindings.Add(new CommandBinding(CtrlShiftP, Menu_DeliveryNote_Print_Click));
            CommandBindings.Add(new CommandBinding(CtrlShiftO, Menu_DeliveryJournal_PrintFilters_Click));
            RequiredInputs = [
                MgNrInput, MemberInput,
                LsNrInput, DateInput, BranchInput,
                SortIdInput, WineVarietyInput,
                GradationOeInput, GradationKmwInput, WineQualityLevelInput,
                WineOriginInput, WineKgInput,
                WeightInput, GerebeltGewogenInput,
            ];
            ExemptInputs = [
                SearchInput, SeasonInput, TodayOnlyInput, AllSeasonsInput,
                DeliveryList, DeliveryPartList,
                MemberAddressField,
            ];
            WeighingButtons = [
                WeighingAButton, WeighingBButton, WeighingCButton, WeighingDButton,
            ];
            ViewModel.IsReceipt = receipt;

            SecondsTimer = new DispatcherTimer();
            SecondsTimer.Tick += new EventHandler(OnSecondPassed);
            SecondsTimer.Interval = new TimeSpan(0, 0, 1);

            ControlUtils.InitializeDelayTimer(SearchInput, SearchInput_TextChanged);
            SearchInput.TextChanged -= SearchInput_TextChanged;
            ViewModel.FilterSeason = Utils.CurrentLastSeason;

            DoShowWarningWindows = false;

            if (ViewModel.IsReceipt) {
                ViewModel.Title = $"Übernahme - {App.BranchName} - Elwig";
                ViewModel.FilterTodayOnly = true;
                var n = App.CommandScales.Count;
                if (n < 1) WeighingAButton.Visibility = Visibility.Hidden;
                if (n < 2) WeighingBButton.Visibility = Visibility.Hidden;
                if (n < 3) WeighingCButton.Visibility = Visibility.Hidden;
                if (n < 4) WeighingDButton.Visibility = Visibility.Hidden;
                if (n == 1) WeighingAButton.Content = "Wiegen";
                if (n > 1) WeighingAButton.Content = $"Wiegen {App.CommandScales[0].ScaleId}";
                if (n >= 2) WeighingBButton.Content = $"Wiegen {App.CommandScales[1].ScaleId}";
                if (n >= 3) WeighingCButton.Content = $"Wiegen {App.CommandScales[2].ScaleId}";
                if (n >= 4) WeighingDButton.Content = $"Wiegen {App.CommandScales[3].ScaleId}";
                WeighingManualButton.Margin = new Thickness(10, 10 + n * 32, 10, 10);
                foreach (var s in App.EventScales) {
                    s.WeighingEvent += Scale_Weighing;
                }
                if (App.Client.IsMatzen) {
                    RequiredInputs = [.. RequiredInputs, ModifiersInput];
                }
                ModifiersInput.ItemTemplate = (DataTemplate)ModifiersInput.FindResource("PublicModifierTemplate");
            } else {
                WeighingManualButton.Visibility = Visibility.Hidden;
                WeighingAButton.Visibility = Visibility.Hidden;
                WeighingBButton.Visibility = Visibility.Hidden;
                WeighingCButton.Visibility = Visibility.Hidden;
                WeighingDButton.Visibility = Visibility.Hidden;
            }

            Menu_Statistics_WineQuality_ModeOe.IsChecked = false;
            Menu_Statistics_WineQuality_ModeKmw1.IsChecked = false;
            Menu_Statistics_WineQuality_ModeKmw2.IsChecked = false;
            Menu_Statistics_WineQuality_ModeKmw5.IsChecked = false;
            Menu_Statistics_WineQuality_ModeKmw10.IsChecked = false;
            switch (App.Client.OrderingMemberList) {
                case 0: Menu_Statistics_WineQuality_ModeOe.IsChecked = true; break;
                case 1: Menu_Statistics_WineQuality_ModeKmw1.IsChecked = true; break;
                case 2: Menu_Statistics_WineQuality_ModeKmw2.IsChecked = true; break;
                case 3: Menu_Statistics_WineQuality_ModeKmw5.IsChecked = true; break;
                case 4: Menu_Statistics_WineQuality_ModeKmw10.IsChecked = true; break;
            }

            Menu_Export_UploadFilters.IsEnabled = App.Config.SyncUrl != null;
            Menu_Export_UploadSeason.IsEnabled = App.Config.SyncUrl != null;
        }

        public DeliveryAdminWindow(int mgnr) : this() {
            ViewModel.FilterMember = DeliveryService.GetMember(mgnr) ?? throw new ArgumentException("MgNr argument has invalid value");
            ViewModel.Title = $"Lieferungen - {ViewModel.FilterMember.AdministrativeName} - Elwig";
            ViewModel.EnableAllSeasons = true;
        }

        private void Window_Loaded(object sender, RoutedEventArgs evt) {
            OnSecondPassed(null, null);
            SecondsTimer.Start();
            LockInputs();
            if (ViewModel.IsReceipt) {
                NewDeliveryButton_Click(null, null);
                using var ctx = new AppDbContext();
                if (ctx.Seasons.Find(Utils.CurrentYear) == null) {
                    MessageBox.Show("Die Saison für das aktuelle Jahr wurde noch nicht erstellt. Neue Lieferungen können nicht abgespeichert werden.\n\n(Stammdaten -> Saisons -> Neu anlegen...)",
                        "Saison noch nicht erstellt", MessageBoxButton.OK, MessageBoxImage.Warning);
                }
            }
        }

        private async void Menu_DeliveryNote_Show_Click(object sender, RoutedEventArgs evt) {
            if (DeliveryList.SelectedItem is not Delivery d)
                return;
            await DeliveryService.GenerateDeliveryNote(d.Year, d.DId, ExportMode.Show);
        }

        private async void Menu_DeliveryNote_SavePdf_Click(object sender, RoutedEventArgs evt) {
            if (DeliveryList.SelectedItem is not Delivery d)
                return;
            await DeliveryService.GenerateDeliveryNote(d.Year, d.DId, ExportMode.SavePdf);
        }

        private async void Menu_DeliveryNote_Print_Click(object sender, RoutedEventArgs evt) {
            if (DeliveryList.SelectedItem is not Delivery d)
                return;
            await DeliveryService.GenerateDeliveryNote(d.Year, d.DId, ExportMode.Print);
        }

        private async void Menu_DeliveryNote_Email_Click(object sender, RoutedEventArgs evt) {
            if (DeliveryList.SelectedItem is not Delivery d)
                return;
            var res = MessageBox.Show("Soll eine E-Mail verschickt werden?", "Lieferschein verschicken",
                MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.Yes);
            if (res != MessageBoxResult.Yes)
                return;
            await DeliveryService.GenerateDeliveryNote(d.Year, d.DId, ExportMode.Email);
        }

        private async void Menu_Bki_SaveList_Click(object sender, RoutedEventArgs evt) {
            if (sender is not MenuItem m) return;
            var year = int.Parse(m.Header.ToString()?.Split(" ")[^1] ?? Utils.CurrentLastSeason.ToString());
            var d = new SaveFileDialog() {
                FileName = $"{App.Client.NameToken}-Traubentransportscheinliste-{year}.{Bki.FileExtension}",
                DefaultExt = Bki.FileExtension,
                Filter = "CSV-Datei (*.csv)|*.csv",
                Title = $"Traubentransportscheinliste (BKI) speichern unter - Elwig"
            };
            if (d.ShowDialog() == true) {
                Mouse.OverrideCursor = Cursors.Wait;
                await Task.Run(async () => {
                    using var file = new Bki(d.FileName);
                    await file.ExportAsync(year);
                });
                Mouse.OverrideCursor = null;
            }
        }

        private async void Menu_DeliveryJournal_SaveToday_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateDeliveryJournal(DeliveryService.ExportSubject.FromToday, ExportMode.SaveList);
        private async void Menu_DeliveryJournal_SavePdfToday_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateDeliveryJournal(DeliveryService.ExportSubject.FromToday, ExportMode.SavePdf);
        private async void Menu_DeliveryJournal_ShowToday_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateDeliveryJournal(DeliveryService.ExportSubject.FromToday, ExportMode.Show);
        private async void Menu_DeliveryJournal_PrintToday_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateDeliveryJournal(DeliveryService.ExportSubject.FromToday, ExportMode.Print);
        private async void Menu_DeliveryJournal_SaveFilters_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateDeliveryJournal(DeliveryService.ExportSubject.FromFilters, ExportMode.SaveList);
        private async void Menu_DeliveryJournal_SavePdfFilters_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateDeliveryJournal(DeliveryService.ExportSubject.FromFilters, ExportMode.SavePdf);
        private async void Menu_DeliveryJournal_ShowFilters_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateDeliveryJournal(DeliveryService.ExportSubject.FromFilters, ExportMode.Show);
        private async void Menu_DeliveryJournal_PrintFilters_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateDeliveryJournal(DeliveryService.ExportSubject.FromFilters, ExportMode.Print);

        private async void Menu_Export_ExportSeason_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateDeliveryJournal(DeliveryService.ExportSubject.FromSeasonAndBranch, ExportMode.Export);
        private async void Menu_Export_ExportFilters_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateDeliveryJournal(DeliveryService.ExportSubject.FromFilters, ExportMode.Export);
        private async void Menu_Export_ExportSelected_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateDeliveryJournal(DeliveryService.ExportSubject.Selected, ExportMode.Export);

        private async void Menu_Export_UploadSeason_Click(object sender, RoutedEventArgs evt) {
            if (App.Config.SyncUrl == null) return;
            await ViewModel.GenerateDeliveryJournal(DeliveryService.ExportSubject.FromSeasonAndBranch, ExportMode.Upload);
        }
        private async void Menu_Export_UploadFilters_Click(object sender, RoutedEventArgs evt) {
            if (App.Config.SyncUrl == null) return;
            await ViewModel.GenerateDeliveryJournal(DeliveryService.ExportSubject.FromFilters, ExportMode.Upload);
        }
        private async void Menu_Export_UploadSelected_Click(object sender, RoutedEventArgs evt) {
            if (App.Config.SyncUrl == null) return;
            await ViewModel.GenerateDeliveryJournal(DeliveryService.ExportSubject.FromSeasonAndBranch, ExportMode.Upload);
        }

        private async void Menu_Statistics_WineQuality_ShowToday_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateWineQualityStatistics(DeliveryService.ExportSubject.FromToday, ExportMode.Show);
        private async void Menu_Statistics_WineQuality_SavePdfToday_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateWineQualityStatistics(DeliveryService.ExportSubject.FromToday, ExportMode.SavePdf);
        private async void Menu_Statistics_WineQuality_PrintToday_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateWineQualityStatistics(DeliveryService.ExportSubject.FromToday, ExportMode.Print);
        private async void Menu_Statistics_WineQuality_ShowFilters_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateWineQualityStatistics(DeliveryService.ExportSubject.FromFilters, ExportMode.Show);
        private async void Menu_Statistics_WineQuality_SavePdfFilters_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateWineQualityStatistics(DeliveryService.ExportSubject.FromFilters, ExportMode.SavePdf);
        private async void Menu_Statistics_WineQuality_PrintFilters_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateWineQualityStatistics(DeliveryService.ExportSubject.FromFilters, ExportMode.Print);

        private async void Menu_Statistics_WineQuality_Mode_Click(object sender, RoutedEventArgs evt) {
            Menu_Statistics.IsSubmenuOpen = true;
            Menu_Statistics_WineQuality.IsSubmenuOpen = true;
            if (sender == Menu_Statistics_WineQuality_ModeOe) {
                App.Client.OrderingMemberList = 0;
            } else if (sender == Menu_Statistics_WineQuality_ModeKmw1) {
                App.Client.OrderingMemberList = 1;
            } else if (sender == Menu_Statistics_WineQuality_ModeKmw2) {
                App.Client.OrderingMemberList = 2;
            } else if (sender == Menu_Statistics_WineQuality_ModeKmw5) {
                App.Client.OrderingMemberList = 3;
            } else if (sender == Menu_Statistics_WineQuality_ModeKmw10) {
                App.Client.OrderingMemberList = 4;
            }
            Menu_Statistics_WineQuality_ModeOe.IsChecked = App.Client.OrderingMemberList == 0;
            Menu_Statistics_WineQuality_ModeKmw1.IsChecked = App.Client.OrderingMemberList == 1;
            Menu_Statistics_WineQuality_ModeKmw2.IsChecked = App.Client.OrderingMemberList == 2;
            Menu_Statistics_WineQuality_ModeKmw5.IsChecked = App.Client.OrderingMemberList == 3;
            Menu_Statistics_WineQuality_ModeKmw10.IsChecked = App.Client.OrderingMemberList == 4;
            await App.Client.UpdateValues();
        }

        private async void Menu_Statistic_Locality_SaveFilters_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateLocalityStatistics(DeliveryService.ExportSubject.FromFilters);

        private async void Menu_DeliveryDepreciationList_SaveFilters_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateDeliveryDepreciationList(DeliveryService.ExportSubject.FromFilters, ExportMode.SaveList);
        private async void Menu_DeliveryDepreciationList_ShowFilters_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateDeliveryDepreciationList(DeliveryService.ExportSubject.FromFilters, ExportMode.Show);
        private async void Menu_DeliveryDepreciationList_SavePdfFilters_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateDeliveryDepreciationList(DeliveryService.ExportSubject.FromFilters, ExportMode.SavePdf);
        private async void Menu_DeliveryDepreciationList_PrintFilters_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateDeliveryDepreciationList(DeliveryService.ExportSubject.FromFilters, ExportMode.Print);
        private async void Menu_DeliveryDepreciationList_SaveSeason_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateDeliveryDepreciationList(DeliveryService.ExportSubject.FromSeason, ExportMode.SaveList);
        private async void Menu_DeliveryDepreciationList_ShowSeason_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateDeliveryDepreciationList(DeliveryService.ExportSubject.FromSeason, ExportMode.Show);
        private async void Menu_DeliveryDepreciationList_SavePdfSeason_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateDeliveryDepreciationList(DeliveryService.ExportSubject.FromSeason, ExportMode.SavePdf);
        private async void Menu_DeliveryDepreciationList_PrintSeason_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateDeliveryDepreciationList(DeliveryService.ExportSubject.FromSeason, ExportMode.Print);

        private async void Menu_DeliveryDataList_SaveFilters_Click(object sender, RoutedEventArgs evt) =>
            await ViewModel.GenerateDeliveryDataList(DeliveryService.ExportSubject.FromFilters, ExportMode.SaveList);

        private void Menu_Settings_EnableFreeEditing_Checked(object sender, RoutedEventArgs evt) {
            if (IsEditing || IsCreating) {
                DateInput.IsReadOnly = false;
                TimeInput.IsReadOnly = false;
                BranchInput.IsEnabled = true;
                GerebeltGewogenInput.IsEnabled = true;
                if (IsCreating) ViewModel.Time = "";
                OnSecondPassed(null, null);
            }
        }

        private void Menu_Settings_EnableFreeEditing_Unchecked(object sender, RoutedEventArgs evt) {
            DateInput.IsReadOnly = true;
            TimeInput.IsReadOnly = true;
            BranchInput.IsEnabled = false;
            GerebeltGewogenInput.IsEnabled = App.Config.WeighingMode != WeighingMode.Net;
            OnSecondPassed(null, null);
        }

        private void OnSecondPassed(object? sender, EventArgs? evt) {
            if (ViewModel.IsReceipt && IsCreating && !Menu_Settings_EnableFreeEditing.IsChecked) {
                var now = DateTime.Now;
                ViewModel.Time = now.ToString("HH:mm");
                ViewModel.Date = now.ToString("dd.MM.yyyy");
                SetDefaultValue(TimeInput);
                SetDefaultValue(DateInput);
            }
        }

        private void InitialDefaultInputs() {
            if (App.Config.WeighingMode == WeighingMode.Net) {
                GerebeltGewogenInput.IsEnabled = false;
                SetDefaultValue(GerebeltGewogenInput, true);
            } else {
                GerebeltGewogenInput.IsEnabled = true;
                GerebeltGewogenInput.IsChecked = null;
                UnsetDefaultValue(GerebeltGewogenInput);
            }

            if (App.Config.WeighingMode == WeighingMode.Box) {
                SetDefaultValue(UnloadingDumperInput, false);
                SetDefaultValue(UnloadingPumpedInput, false);
                SetDefaultValue(UnloadingBoxInput, true);
                SetDefaultValue(UnloadingOtherInput, false);
                UnloadingBoxInput.IsChecked = true;
            } else {
                UnsetDefaultValue(UnloadingDumperInput);
                UnsetDefaultValue(UnloadingPumpedInput);
                UnsetDefaultValue(UnloadingBoxInput);
                UnsetDefaultValue(UnloadingOtherInput);
            }

            if (App.Config.WeighingMode != WeighingMode.Net) {
                HandPickedInput.IsThreeState = false;
                UnsetDefaultValue(HandPickedInput);
            } else {
                HandPickedInput.IsThreeState = true;
                SetDefaultValue(HandPickedInput, null);
            }

            if (App.Client.IsMatzen || App.Client.IsWinzerkeller || App.Client.IsBaden || App.Client.IsWeinland) {
                GebundenInput.IsEnabled = false;
                SetDefaultValue(GebundenInput, null);
            } else {
                GebundenInput.IsEnabled = true;
                UnsetDefaultValue(GebundenInput);
            }
        }

        private void InitialInputs() {
            ViewModel.LastScaleError = null;
            ViewModel.WeighingData = null;
            ViewModel.ScaleId = null;
            ViewModel.ManualWeighingReason = null;

            ClearOriginalValues();
            ClearDefaultValues();

            ViewModel.IsNetWeight = App.Config.WeighingMode == WeighingMode.Net;
            ViewModel.IsHandPicked = App.Config.WeighingMode != WeighingMode.Net ? true : null;
            ViewModel.Unloading = null;
            ViewModel.IsGebunden = null;
            InitialDefaultInputs();

            //WineQualityLevelInput.IsEnabled = false;  // disable wine quality level input in Übernahme
            ValidateRequiredInputs();
        }

        private void InitInputs() {
            ControlUtils.SelectItemWithPk(BranchInput, App.ZwstId);
            OnSecondPassed(null, null);
            UpdateLsNr().GetAwaiter().GetResult();
            InitialInputs();
        }

        protected override void UpdateButtons() {
            if (!IsEditing && !IsCreating) return;
            bool ch = HasChanged, v = IsValid;
            ResetButton.IsEnabled = ch;
            SaveButton.IsEnabled = v && ch;
            FinishButton.IsEnabled = v && ch;
            NewDeliveryPartButton.IsEnabled = v && ch;
            CancelCreatingButton.IsEnabled = DeliveryList.SelectedItem == null || DeliveryPartList.SelectedItem == null;
        }

        private void Input_KeyUp(object sender, KeyEventArgs evt) {
            if (sender is not Control ctrl) return;
            if (evt.Key != Key.Enter) return;
            if (ctrl == MgNrInput || ctrl == MemberInput) {
                SortIdInput.Focus();
                SortIdInput.SelectAll();
            } else if (ctrl == SortIdInput || ctrl == WineVarietyInput || ctrl == AttributeInput || ctrl == CultivationInput) {
                GradationOeInput.Focus();
                GradationOeInput.SelectAll();
            } else if (ctrl == GradationKmwInput || ctrl == GradationOeInput || ctrl == WineQualityLevelInput) {
                if (WeighingAButton.IsVisible) WeighingAButton.Focus();
                else WeighingManualButton.Focus();
            }
        }

        private async Task RefreshList(bool updateSort = false) {
            using var ctx = new AppDbContext();
            var (_, deliveryQuery, deliveryPartsQuery, predicate, filter) = await ViewModel.GetFilters(ctx);
            var deliveries = await deliveryQuery
                .Include(d => d.Parts).ThenInclude(p => p.PartModifiers).ThenInclude(m => m.Modifier)
                .Include(d => d.Parts).ThenInclude(p => p.Attribute)
                .Include(d => d.Parts).ThenInclude(p => p.Cultivation)
                .Include(d => d.Parts).ThenInclude(p => p.Variety)
                .Include(d => d.Member.EmailAddresses)
                .AsSplitQuery()
                .ToListAsync();
            deliveries.Reverse();

            if (filter.Count > 0 && deliveries.Count > 0) {
                var dict = deliveries.AsParallel()
                    .ToDictionary(d => d, d => d.SearchScore(ViewModel.TextFilter))
                    .OrderByDescending(a => a.Value)
                    .ThenBy(a => a.Key.DateTime);
                var threshold = dict.Select(a => a.Value).Max() * 3 / 4;
                deliveries = dict
                    .Where(a => a.Value > threshold)
                    .Select(a => a.Key)
                    .ToList();
            }

            deliveries.ForEach(d => { d.PartFilter = predicate; });
            ControlUtils.RenewItemsSource(DeliveryList, deliveries,
                DeliveryList_SelectionChanged, filter.Count > 0 ? ControlUtils.RenewSourceDefault.IfOnly : ControlUtils.RenewSourceDefault.None, !updateSort);
            await RefreshDeliveryParts();

            var members = deliveries.Select(d => d.Member).DistinctBy(m => m.MgNr).ToList();
            ViewModel.StatusMembers = $"{members.Count:N0}" + (members.Count > 0 && members.Count <= 4 ? $" ({string.Join(", ", members.Select(m => m.AdministrativeName))})" : "");
            ViewModel.StatusDeliveries = $"{deliveries.Count:N0}";

            if (filter.Count == 0) {
                var deliveryParts = deliveryPartsQuery;
                ViewModel.StatusDeliveries = $"{deliveries.Count:N0} ({await deliveryParts.CountAsync():N0})";
                var varieties = await deliveryParts.Select(d => d.SortId).Distinct().ToListAsync();
                ViewModel.StatusVarieties = $"{varieties.Count:N0}" + (varieties.Count > 0 && varieties.Count <= 10 ? $" ({string.Join(", ", varieties)})" : "");
                var (wText, wData, gText, gData) = await DeliveryService.GenerateToolTipData(deliveryParts);
                ViewModel.StatusWeight = wText;
                ViewModel.StatusGradation = gText;
                (ViewModel.StatusWeightToolTip, ViewModel.StatusGradationToolTip) = DeliveryService.GenerateToolTip(wData, gData);
            } else {
                ViewModel.StatusVarieties = "-";
                ViewModel.StatusWeight = "-";
                ViewModel.StatusGradation = "-";
            }

            if (updateSort && DeliveryList.SelectedItem != null)
                DeliveryList.ScrollIntoView(DeliveryList.SelectedItem);
        }

        protected override async Task OnRenewContext(AppDbContext ctx) {
            await base.OnRenewContext(ctx);

            if (ViewModel.FilterMember != null) {
                if (await DeliveryService.GetMemberAsync(ViewModel.FilterMember.MgNr) is not Member m) {
                    Close();
                    return;
                }
                ViewModel.FilterMember = m;
                ViewModel.Title = $"Lieferungen - {ViewModel.FilterMember.AdministrativeName} - Elwig";
            }

            Menu_Bki_SaveList.Items.Clear();
            foreach (var s in await ctx.Seasons.OrderByDescending(s => s.Year).ToListAsync()) {
                var i = new MenuItem {
                    Header = $"Saison {s.Year}",
                };
                i.Click += Menu_Bki_SaveList_Click;
                Menu_Bki_SaveList.Items.Add(i);
            }

            await RefreshList();
            var d = DeliveryList.SelectedItem as Delivery;
            var y = d?.Year ?? ViewModel.FilterSeason;
            ControlUtils.RenewItemsSource(MemberInput, await ctx.Members
                .Where(m => m.IsActive || !IsCreating)
                .Include(m => m.PostalDest.AtPlz!.Ort)
                .Include(m => m.DefaultWbKg!.AtKg)
                .OrderBy(m => m.Name)
                .ThenBy(m => m.GivenName)
                .ToListAsync());
            ControlUtils.RenewItemsSource(BranchInput, await ctx.Branches.OrderBy(b => b.Name).ToListAsync());
            ControlUtils.RenewItemsSource(WineVarietyInput, await ctx.WineVarieties.OrderBy(v => v.Name).ToListAsync());
            var attrList = await ctx.WineAttributes.Where(a => !IsCreating || a.IsActive).OrderBy(a => a.Name).Cast<object>().ToListAsync();
            attrList.Insert(0, new NullItem(""));
            ControlUtils.RenewItemsSource(AttributeInput, attrList, null, ControlUtils.RenewSourceDefault.First);
            var cultList = await ctx.WineCultivations.OrderBy(a => a.Name).Cast<object>().ToListAsync();
            cultList.Insert(0, new NullItem(""));
            ControlUtils.RenewItemsSource(CultivationInput, cultList, null, ControlUtils.RenewSourceDefault.First);
            WineQualityLevels = await ctx.WineQualityLevels.ToListAsync();
            ControlUtils.RenewItemsSource(WineQualityLevelInput, WineQualityLevels);
            ControlUtils.RenewItemsSource(ModifiersInput, await ctx.Modifiers
                .Where(m => m.Year == y && (!IsCreating || m.IsActive))
                .OrderBy(m => m.Ordering)
                .Include(m => m.Season.Currency)
                .ToListAsync());
            ControlUtils.RenewItemsSource(WineOriginInput, (await ctx.WineOrigins.ToListAsync()).OrderByDescending(o => o.SortKey).ThenBy(o => o.HkId));
            var kgList = (await ctx.Katastralgemeinden
                .Where(k => k.WbKg != null)
                .Include(k => k.WbKg)
                .Include(k => k.Gem.WbGem)
                .OrderBy(k => k.Name)
                .AsSplitQuery()
                .ToListAsync()).Cast<object>().ToList();
            kgList.Insert(0, new NullItem());
            ControlUtils.RenewItemsSource(WineKgInput, kgList);
            UpdateRdInput();
            if (IsCreating) await UpdateLsNr();

            await RefreshDeliveryParts();
            RefreshInputs();
        }

        private void FocusSearchInput(object sender, RoutedEventArgs evt) {
            if (!IsEditing && !IsCreating) {
                SearchInput.Focus();
                SearchInput.SelectAll();
            }
        }

        private async Task RefreshDeliveryParts() {
            using var ctx = new AppDbContext();
            if (DeliveryList.SelectedItem is Delivery d) {
                ControlUtils.RenewItemsSource(ModifiersInput, await ctx.Modifiers
                    .Where(m => m.Year == d.Year && (!IsCreating || m.IsActive))
                    .OrderBy(m => m.Ordering)
                    .Include(m => m.Season.Currency)
                    .ToListAsync());
                ControlUtils.RenewItemsSource(DeliveryPartList, d.FilteredParts.OrderBy(p => p.DPNr).ToList(), DeliveryPartList_SelectionChanged, ControlUtils.RenewSourceDefault.First);
            } else {
                ControlUtils.RenewItemsSource(ModifiersInput, await ctx.Modifiers
                    .Where(m => m.Year == ViewModel.FilterSeason && (!IsCreating || m.IsActive))
                    .OrderBy(m => m.Ordering)
                    .Include(m => m.Season.Currency)
                    .ToListAsync());
                DeliveryPartList.ItemsSource = null;
            }
        }

        private void RefreshInputs(bool validate = false) {
            ClearInputStates();
            if (DeliveryPartList.SelectedItem is DeliveryPart p) {
                FillInputs(p);
            } else if (DeliveryList.SelectedItem is Delivery d) {
                FillInputs(d);
            } else {
                ClearOriginalValues();
                ClearDefaultValues();
                ClearInputs(validate);
                ClearInputStates();
            }            
            GC.Collect();
        }

        private void FillInputs(Delivery d) {
            ClearOriginalValues();
            ClearDefaultValues();
            ViewModel.FillInputs(d);
            FinishInputFilling();
        }

        private void FillInputs(DeliveryPart p) {
            FillInputs(p.Delivery);
            ClearOriginalValues();
            ClearDefaultValues();
            ViewModel.FillInputs(p);
            FinishInputFilling();
        }

        new protected void ClearInputs(bool validate = false) {
            ViewModel.ClearInputs();
            base.ClearInputs(validate);
        }

        private void WeighingButton_Click(object sender, RoutedEventArgs evt) {
            int index = Array.IndexOf(WeighingButtons, sender as Button);
            if (index >= 0) WeighingButton_Click(index);
        }

        private async void WeighingButton_Click(int index) {
            DisableWeighingButtons();
            var start = DateTimeOffset.Now.ToUnixTimeMilliseconds();
            FinishButton.IsEnabled = false;
            NewDeliveryPartButton.IsEnabled = false;
            CancelCreatingButton.IsEnabled = false;
            var s = App.CommandScales[index];
            try {
                var res = await s.Weigh();
                OnWeighingResult(s, res);
            } catch (Exception ex) {
                ViewModel.LastScaleError = ex.Message.Split(": ")[^1];
                OnWeighingResult(s, new());
                MessageBox.Show($"Beim Wiegen ist ein Fehler aufgetreten:\n\n{ex.Message}", "Waagenfehler",
                       MessageBoxButton.OK, MessageBoxImage.Error);
            }
            ViewModel.ManualWeighingReason = null;
            ManualWeighingInput.IsChecked = false;
            var end = DateTimeOffset.Now.ToUnixTimeMilliseconds();
            int diff = (int)(end - start);
            if (diff < 1000 && ViewModel.Weight != null) await Task.Delay(1000 - diff);
            EnableWeighingButtons();
        }

        private void OnWeighingResult(IScale scale, WeighingResult res) {
            if ((res.NetWeight ?? 0) > 0 && res.FullWeighingId != null) {
                ViewModel.Weight = res.NetWeight;
                ViewModel.ScaleId = scale.ScaleId;
                ViewModel.WeighingData = res.ToJson().ToJsonString();
                ViewModel.ManualWeighingReason = null;
                ManualWeighingInput.IsChecked = false;
            } else {
                ViewModel.Weight = null;
                ViewModel.ScaleId = null;
                ViewModel.WeighingData = null;
            }
            ViewModel.LastScaleError = null;
            TextBox_TextChanged(WeightInput, null);
            UpdateButtons();
        }

        private void Scale_Weighing(object sender, WeighingEventArgs evt) {
            if (sender is not IScale scale) return;
            App.MainDispatcher.BeginInvoke(() => OnWeighingResult(scale, evt.Result));
        }

        private async void SearchInput_TextChanged(object sender, RoutedEventArgs evt) {
            await RefreshList(true);
        }

        private async void SeasonInput_TextChanged(object sender, TextChangedEventArgs evt) {
            if (ViewModel.FilterSeason == null || TodayOnlyInput == null || AllSeasonsInput == null) return;
            TodayOnlyInput.IsChecked = false;
            AllSeasonsInput.IsChecked = false;
            await RefreshList();
        }

        private async void TodayOnlyInput_Changed(object sender, RoutedEventArgs evt) {
            if (TodayOnlyInput.IsChecked == true && AllSeasonsInput.IsChecked == false) {
                ViewModel.FilterSeason = Utils.Today.Year;
                ViewModel.FilterTodayOnly = true;
            }
            await RefreshList();
        }

        private async void AllSeasonsInput_Changed(object sender, RoutedEventArgs evt) {
            if (AllSeasonsInput.IsChecked == true) {
                SeasonInput.IsEnabled = false;
                ViewModel.FilterSeason = null;
            } else {
                SeasonInput.IsEnabled = true;
                var today = ViewModel.FilterTodayOnly;
                ViewModel.FilterSeason = Utils.CurrentLastSeason;
                ViewModel.FilterTodayOnly = today;
            }
            await RefreshList();
        }

        private async void DeliveryList_SelectionChanged(object sender, SelectionChangedEventArgs evt) {
            await RefreshDeliveryParts();
            if (DeliveryList.SelectedItem is Delivery d) {
                DeleteDeliveryButton.IsEnabled = true;
                Menu_DeliveryNote_Show.IsEnabled = !IsEditing && !IsCreating;
                Menu_DeliveryNote_SavePdf.IsEnabled = !IsEditing && !IsCreating;
                Menu_DeliveryNote_Print.IsEnabled = !IsEditing && !IsCreating;
                Menu_DeliveryNote_Email.IsEnabled = !IsEditing && !IsCreating && App.Config.Smtp != null && d.Member.EmailAddresses.Count > 0;
                Menu_Export_ExportSelected.IsEnabled = !IsEditing && !IsCreating;
                Menu_Export_UploadSelected.IsEnabled = !IsEditing && !IsCreating && App.Config.SyncUrl != null;
            } else {
                DeleteDeliveryButton.IsEnabled = false;
                Menu_DeliveryNote_Show.IsEnabled = false;
                Menu_DeliveryNote_SavePdf.IsEnabled = false;
                Menu_DeliveryNote_Print.IsEnabled = false;
                Menu_DeliveryNote_Email.IsEnabled = false;
                Menu_Export_ExportSelected.IsEnabled = false;
                Menu_Export_UploadSelected.IsEnabled = false;
            }
        }

        private void DeliveryPartList_SelectionChanged(object? sender, SelectionChangedEventArgs? evt) {
            RefreshInputs();
            if (DeliveryPartList.SelectedItem is DeliveryPart p) {
                DepreciateButton.IsEnabled = true;
                EditDeliveryButton.IsEnabled = true;
            } else {
                DepreciateButton.IsEnabled = false;
                EditDeliveryButton.IsEnabled = false;
            }
        }

        private void MgNrInput_TextChanged(object sender, TextChangedEventArgs evt) {
            var valid = InputTextChanged((TextBox)sender, Validator.CheckMgNr);
            var text = MgNrInput.Text;
            var caret = MgNrInput.CaretIndex;
            ControlUtils.SelectItemWithPk(MemberInput, valid ? ViewModel.MgNr : null);
            MgNrInput.Text = text;
            MgNrInput.CaretIndex = caret;
        }

        private void MgNrInput_LostFocus(object sender, RoutedEventArgs evt) {
            var valid = InputLostFocus((TextBox)sender, Validator.CheckMgNr);
            ControlUtils.SelectItemWithPk(MemberInput, valid ? ViewModel.MgNr : null);
        }

        private void MemberInput_SelectionChanged(object? sender, SelectionChangedEventArgs? evt) {
            var m = MemberInput.SelectedItem as Member;
            ViewModel.MgNr = m?.MgNr;
            ViewModel.MemberAddress = m?.FullAddress;
            if (m == null) {
                UnsetDefaultValue(WineKgInput);
                WineKgInput.SelectedItem = null;
                ComboBox_SelectionChanged(WineKgInput, null);
            } else {
                if (m.DefaultKgNr is int kgnr) {
                    ControlUtils.SelectItemWithPk(WineKgInput, kgnr);
                } else {
                    WineKgInput.SelectedItem = null;
                }
                SetDefaultValue(WineKgInput);
                ComboBox_SelectionChanged(WineKgInput, null);
            }
        }

        private void EmptyScale() {
            var scale = App.Scales.Where(s => s.ScaleId == ViewModel.ScaleId).FirstOrDefault();
            if (scale is not ICommandScale cs) return;
            cs.Empty();
        }

        private async void NewDeliveryPartButton_Click(object sender, RoutedEventArgs evt) {
            FinishButton.IsEnabled = false;
            NewDeliveryPartButton.IsEnabled = false;
            Mouse.OverrideCursor = Cursors.Wait;
            DeliveryPartList.IsEnabled = false;

            DeliveryPart? p;
            try {
                p = await ViewModel.UpdateDeliveryPart(
                    (DeliveryList.SelectedItem as Delivery)?.Year,
                    (DeliveryList.SelectedItem as Delivery)?.DId,
                    (DeliveryPartList.SelectedItem as DeliveryPart)?.DPNr,
                    InputHasChanged(DateInput),
                    InputHasChanged(TimeInput),
                    !InputIsNotDefault(TimeInput)
                );
                App.HintContextChange();
            } catch (Exception exc) {
                var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
                if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
                MessageBox.Show(str, "Lieferung aktualisieren", MessageBoxButton.OK, MessageBoxImage.Error);
                FinishButton.IsEnabled = true;
                SaveButton.IsEnabled = true;
                Mouse.OverrideCursor = null;
                DeliveryPartList.IsEnabled = true;
                return;
            }

            EmptyScale();
            await RefreshList();
            await RefreshDeliveryParts();
            Mouse.OverrideCursor = null;
            ControlUtils.SelectItem(DeliveryList, p?.Delivery);
            DeliveryPartList.SelectedItem = null;
            DeliveryPartList.ScrollIntoView(DeliveryPartList.ItemsSource.Cast<object>().Last());
            RefreshInputs();
            InitialInputs();
        }

        private async void FinishButton_Click(object sender, RoutedEventArgs evt) {
            FinishButton.IsEnabled = false;
            NewDeliveryPartButton.IsEnabled = false;
            Mouse.OverrideCursor = Cursors.Wait;
            DeliveryPartList.IsEnabled = false;

            DeliveryPart? p;
            try {
                p = await ViewModel.UpdateDeliveryPart(
                    (DeliveryList.SelectedItem as Delivery)?.Year,
                    (DeliveryList.SelectedItem as Delivery)?.DId,
                    (DeliveryPartList.SelectedItem as DeliveryPart)?.DPNr,
                    InputHasChanged(DateInput),
                    InputHasChanged(TimeInput),
                    !InputIsNotDefault(TimeInput)
                );
                App.HintContextChange();
            } catch (Exception exc) {
                var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
                if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
                MessageBox.Show(str, "Lieferung aktualisieren", MessageBoxButton.OK, MessageBoxImage.Error);
                FinishButton.IsEnabled = true;
                SaveButton.IsEnabled = true;
                Mouse.OverrideCursor = null;
                DeliveryPartList.IsEnabled = true;
                return;
            }

            EmptyScale();
            await RefreshList();
            await RefreshDeliveryParts();
            if (p?.Delivery != null) {
                try {
                    using var ctx = new AppDbContext();
                    using var doc = new DeliveryNote((await ctx.Deliveries.FindAsync(p.Year, p.DId))!, ctx);
                    await doc.Generate();
                    if (App.Config.Debug) {
                        doc.Show();
                    } else {
                        await doc.Print(2);
                    }
                } catch (Exception exc) {
                    MessageBox.Show(exc.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
                }
            }

            Mouse.OverrideCursor = null;
            DeliveryList.SelectedItem = null;
            await RenewContext();
            RefreshInputs();
            InitInputs();
        }

        private async void CancelCreatingButton_Click(object sender, RoutedEventArgs evt) {
            if (IsCreating && HasChanged) {
                var r = MessageBox.Show("Soll der Vorgang wirklich abgebrochen werden?", "Abbrechen bestätigen",
                    MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
                if (r != MessageBoxResult.Yes) return;
            }

            using var ctx = new AppDbContext();
            var attrList = await ctx.WineAttributes.OrderBy(a => a.Name).Cast<object>().ToListAsync();
            attrList.Insert(0, new NullItem(""));
            ControlUtils.RenewItemsSource(AttributeInput, attrList, null, ControlUtils.RenewSourceDefault.First);
            ControlUtils.RenewItemsSource(MemberInput, await ctx.Members
                .Where(m => m.IsActive || !ViewModel.IsReceipt)
                .Include(m => m.PostalDest.AtPlz!.Ort)
                .Include(m => m.DefaultWbKg!.AtKg)
                .OrderBy(m => m.Name)
                .ThenBy(m => m.GivenName)
                .ToListAsync());
            if (DeliveryList.SelectedItem is not Delivery d) {
                // switch away from creating mode
                IsCreating = false;
                IsEditing = false;
                DeliveryList.IsEnabled = true;
                DeliveryPartList.IsEnabled = true;
                DisableWeighingButtons();
                HideFinishNewPartDeliveryCancelButtons();
                ShowNewEditDeleteButtons();
                await RenewContext();
                RefreshInputs();
                ClearInputStates();
                LockInputs();
                UnlockSearchInputs();
            } else {
                // switch to last delivery part
                DeliveryPartList.IsEnabled = true;
                ControlUtils.SelectItem(DeliveryPartList, d.FilteredParts.Last());
            }
        }

        protected override void ShortcutNew() {
            if (!NewDeliveryButton.IsEnabled || NewDeliveryButton.Visibility != Visibility.Visible)
                return;
            NewDeliveryButton_Click(null, null);
        }

        private async void NewDeliveryButton_Click(object? sender, RoutedEventArgs? evt) {
            ViewModel.FilterTodayOnly = true;
            ViewModel.SearchQuery = "";
            using var ctx = new AppDbContext();
            var attrList = await ctx.WineAttributes.Where(a => a.IsActive).OrderBy(a => a.Name).Cast<object>().ToListAsync();
            attrList.Insert(0, new NullItem(""));
            ControlUtils.RenewItemsSource(AttributeInput, attrList, null, ControlUtils.RenewSourceDefault.First);
            ControlUtils.RenewItemsSource(ModifiersInput, await ctx.Modifiers
                .Where(m => m.Year == ViewModel.FilterSeason && m.IsActive)
                .OrderBy(m => m.Ordering)
                .Include(m => m.Season.Currency)
                .ToListAsync());
            ControlUtils.RenewItemsSource(MemberInput, await ctx.Members
                .Where(m => m.IsActive || !ViewModel.IsReceipt)
                .Include(m => m.PostalDest.AtPlz!.Ort)
                .Include(m => m.DefaultWbKg!.AtKg)
                .OrderBy(m => m.Name)
                .ThenBy(m => m.GivenName)
                .ToListAsync());
            IsCreating = true;
            DeliveryList.IsEnabled = false;
            DeliveryPartList.IsEnabled = false;
            EnableWeighingButtons();
            DeliveryList.SelectedItem = null;
            HideNewEditDeleteButtons();
            UnlockInputs();
            InitInputs();
            ShowFinishNewPartDeliveryCancelButtons();
            LockSearchInputs();
        }

        private async void DepreciateButton_Click(object sender, RoutedEventArgs evt) {
            if (DeliveryList.SelectedItem is not Delivery d) return;
            var res = Utils.ShowDeliverySplittingDialog(d);
            if (res == null)
                return;

            Mouse.OverrideCursor = Cursors.Wait;
            try {
                var id = res.Value.Item1;
                var weights = res.Value.Item2;
                if (id == null) {
                    // abwerten
                    await DeliveryService.DepreciateDelivery(d.Year, d.DId, weights);
                } else if (id.All(char.IsAsciiDigit)) {
                    // auf Mitglied übertragen
                    var n = await DeliveryService.SplitDeliveryToMember(d.Year, d.DId, weights, int.Parse(id));
                    await Task.Delay(500);
                    ControlUtils.SelectItemWithPk(DeliveryList, n.Year, n.DId);
                } else {
                    // zu existierender Lieferung hinzufügen
                    var n = await DeliveryService.SplitDeliveryToLsNr(d.Year, d.DId, weights, id);
                    ControlUtils.SelectItemWithPk(DeliveryList, n.Year, n.DId);
                }
                App.HintContextChange();
            } catch (Exception exc) {
                var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
                if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
                MessageBox.Show(str, "Lieferung aktualisieren", MessageBoxButton.OK, MessageBoxImage.Error);
            }
            Mouse.OverrideCursor = null;
        }

        private void WeighingManualButton_Click(object sender, RoutedEventArgs evt) {
            var res = Utils.ShowManualWeighingDialog(ViewModel.LastScaleError);
            if (res == null) return;
            ViewModel.Weight = res?.Item1;
            ManualWeighingInput.IsChecked = true;
            ViewModel.ManualWeighingReason = res?.Item2;
            ViewModel.ScaleId = null;
            ViewModel.WeighingData = null;
        }

        protected override void ShortcutEdit() {
            if (!EditDeliveryButton.IsEnabled || EditDeliveryButton.Visibility != Visibility.Visible)
                return;
            EditDeliveryButton_Click(null, null);
        }

        private void EditDeliveryButton_Click(object? sender, RoutedEventArgs? evt) {
            if (DeliveryPartList.SelectedItem == null)
                return;

            IsEditing = true;
            DeliveryList.IsEnabled = false;
            DeliveryPartList.IsEnabled = false;

            HideNewEditDeleteButtons();
            ShowSaveResetCancelButtons();
            UnlockInputs();
            LockSearchInputs();

            DepreciateButton.IsEnabled = false;
        }

        protected override void ShortcutDelete() {
            if (!DeleteDeliveryButton.IsEnabled || DeleteDeliveryButton.Visibility != Visibility.Visible)
                return;
            DeleteDeliveryButton_Click(null, null);
        }

        private async void DeleteDeliveryButton_Click(object? sender, RoutedEventArgs? evt) {
            if (DeliveryList.SelectedItem is not Delivery d)
                return;

            var r = MessageBox.Show(
                $"Soll die Lieferung {d.LsNr} ({d.Member.AdministrativeName}, MgNr. {d.Member.MgNr}) wirklich unwiderruflich gelöscht werden?",
                "Lieferung löschen", MessageBoxButton.OKCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);
            if (r == MessageBoxResult.OK) {
                Mouse.OverrideCursor = Cursors.Wait;
                try {
                    await DeliveryService.DeleteDelivery(d.LsNr);
                    App.HintContextChange();
                } catch (Exception exc) {
                    var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
                    if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
                    MessageBox.Show(str, "Lieferung löschen", MessageBoxButton.OK, MessageBoxImage.Error);
                }
                Mouse.OverrideCursor = null;
            }
        }

        protected override void ShortcutSave() {
            if (!SaveButton.IsEnabled || SaveButton.Visibility != Visibility.Visible)
                return;
            SaveButton_Click(null, null);
        }

        private async void SaveButton_Click(object? sender, RoutedEventArgs? evt) {
            SaveButton.IsEnabled = false;
            Mouse.OverrideCursor = Cursors.Wait;

            DeliveryPart? p;
            try {
                p = await ViewModel.UpdateDeliveryPart(
                    (DeliveryList.SelectedItem as Delivery)?.Year,
                    (DeliveryList.SelectedItem as Delivery)?.DId,
                    (DeliveryPartList.SelectedItem as DeliveryPart)?.DPNr,
                    InputHasChanged(DateInput),
                    InputHasChanged(TimeInput),
                    !InputIsNotDefault(TimeInput)
                );
                App.HintContextChange();
            } catch (Exception exc) {
                var str = "Der Eintrag konnte nicht in der Datenbank aktualisiert werden!\n\n" + exc.Message;
                if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
                MessageBox.Show(str, "Lieferung aktualisieren", MessageBoxButton.OK, MessageBoxImage.Error);
                SaveButton.IsEnabled = true;
                Mouse.OverrideCursor = null;
                return;
            }

            IsEditing = false;
            IsCreating = false;
            DeliveryList.IsEnabled = true;
            DeliveryPartList.IsEnabled = true;

            HideSaveResetCancelButtons();
            ShowNewEditDeleteButtons();
            LockInputs();
            UnlockSearchInputs();
            FinishInputFilling();
            await RefreshList();
            await RefreshDeliveryParts();
            RefreshInputs();

            Mouse.OverrideCursor = null;
            DepreciateButton.IsEnabled = true;
        }

        protected override void ShortcutReset() {
            if (!ResetButton.IsEnabled || ResetButton.Visibility != Visibility.Visible)
                return;
            ResetButton_Click(null, null);
        }

        private void ResetButton_Click(object? sender, RoutedEventArgs? evt) {
            if (IsEditing) {
                RefreshInputs();
            } else if (IsCreating) {
                ClearInputs();
                InitInputs();
            }
            UpdateButtons();
        }

        private void CancelButton_Click(object sender, RoutedEventArgs evt) {
            IsEditing = false;
            IsCreating = false;
            DeliveryList.IsEnabled = true;
            DeliveryPartList.IsEnabled = true;

            HideSaveResetCancelButtons();
            ShowNewEditDeleteButtons();
            RefreshInputs();
            LockInputs();
            UnlockSearchInputs();

            DepreciateButton.IsEnabled = DeliveryPartList.SelectedItem != null;
        }

        private void ShowSaveResetCancelButtons() {
            SaveButton.IsEnabled = false;
            ResetButton.IsEnabled = false;
            CancelButton.IsEnabled = true;
            SaveButton.Visibility = Visibility.Visible;
            ResetButton.Visibility = Visibility.Visible;
            CancelButton.Visibility = Visibility.Visible;
        }

        private void HideSaveResetCancelButtons() {
            SaveButton.IsEnabled = false;
            ResetButton.IsEnabled = false;
            CancelButton.IsEnabled = false;
            SaveButton.Visibility = Visibility.Hidden;
            ResetButton.Visibility = Visibility.Hidden;
            CancelButton.Visibility = Visibility.Hidden;
        }

        private void ShowNewEditDeleteButtons() {
            NewDeliveryButton.IsEnabled = ViewModel.IsReceipt;
            DepreciateButton.IsEnabled = DeliveryList.SelectedItem != null;
            EditDeliveryButton.IsEnabled = DeliveryPartList.SelectedItem != null;
            DeleteDeliveryButton.IsEnabled = DeliveryList.SelectedItem != null;
            NewDeliveryButton.Visibility = ViewModel.IsReceipt ? Visibility.Visible : Visibility.Hidden;
            DepreciateButton.Visibility = !ViewModel.IsReceipt ? Visibility.Visible : Visibility.Hidden;
            EditDeliveryButton.Visibility = Visibility.Visible;
            DeleteDeliveryButton.Visibility = Visibility.Visible;
        }

        private void HideNewEditDeleteButtons() {
            NewDeliveryButton.IsEnabled = false;
            DepreciateButton.IsEnabled = false;
            EditDeliveryButton.IsEnabled = false;
            DeleteDeliveryButton.IsEnabled = false;
            NewDeliveryButton.Visibility = Visibility.Hidden;
            DepreciateButton.Visibility = Visibility.Hidden;
            EditDeliveryButton.Visibility = Visibility.Hidden;
            DeleteDeliveryButton.Visibility = Visibility.Hidden;
        }

        private void ShowFinishNewPartDeliveryCancelButtons() {
            FinishButton.IsEnabled = false;
            NewDeliveryPartButton.IsEnabled = false;
            CancelCreatingButton.IsEnabled = true;
            FinishButton.Visibility = Visibility.Visible;
            NewDeliveryPartButton.Visibility = Visibility.Visible;
            CancelCreatingButton.Visibility = Visibility.Visible;
        }

        private void HideFinishNewPartDeliveryCancelButtons() {
            FinishButton.IsEnabled = false;
            NewDeliveryPartButton.IsEnabled = false;
            CancelCreatingButton.IsEnabled = false;
            FinishButton.Visibility = Visibility.Hidden;
            NewDeliveryPartButton.Visibility = Visibility.Hidden;
            CancelCreatingButton.Visibility = Visibility.Hidden;
        }

        private void LockSearchInputs() {
            SearchInput.IsEnabled = false;
            SeasonInput.IsEnabled = false;
            TodayOnlyInput.IsEnabled = false;
            AllSeasonsInput.IsEnabled = false;
        }

        private void UnlockSearchInputs() {
            SearchInput.IsEnabled = true;
            SeasonInput.IsEnabled = true;
            TodayOnlyInput.IsEnabled = true;
            AllSeasonsInput.IsEnabled = (ViewModel.FilterMember != null);
        }

        new protected void UnlockInputs() {
            base.UnlockInputs();
            if (WineQualityLevelInput.SelectedItem != null && WineKgInput.SelectedItem != null)
                WineOriginInput.IsEnabled = false;
            if (WineKgInput.SelectedItem == null)
                WineRdInput.IsEnabled = false;
            WeightInput.IsReadOnly = true;
            AbgewertetInput.IsEnabled = false;
            ManualWeighingInput.IsEnabled = false;
            LsNrInput.IsReadOnly = true;
            DateInput.IsReadOnly = !Menu_Settings_EnableFreeEditing.IsChecked;
            TimeInput.IsReadOnly = !Menu_Settings_EnableFreeEditing.IsChecked;
            BranchInput.IsEnabled = Menu_Settings_EnableFreeEditing.IsChecked;
            GerebeltGewogenInput.IsEnabled = App.Config.WeighingMode == WeighingMode.Net || Menu_Settings_EnableFreeEditing.IsChecked;
        }

        private void DisableWeighingButtons() {
            WeighingManualButton.IsEnabled = false;
            WeighingAButton.IsEnabled = false;
            WeighingBButton.IsEnabled = false;
            WeighingCButton.IsEnabled = false;
            WeighingDButton.IsEnabled = false;
        }

        private void EnableWeighingButtons() {
            WeighingManualButton.IsEnabled = true;
            var n = App.CommandScales.Count;
            WeighingAButton.IsEnabled = n > 0 && App.CommandScales[0].IsReady;
            WeighingBButton.IsEnabled = n > 1 && App.CommandScales[1].IsReady;
            WeighingCButton.IsEnabled = n > 2 && App.CommandScales[2].IsReady;
            WeighingDButton.IsEnabled = n > 3 && App.CommandScales[3].IsReady;
        }

        public void UpdateScales() {
            if (!ViewModel.IsReceipt) return;
            foreach (var s in App.EventScales) {
                s.WeighingEvent -= Scale_Weighing;
                s.WeighingEvent += Scale_Weighing;
            }
            if (WeighingManualButton.IsEnabled) {
                EnableWeighingButtons();
            } else {
                DisableWeighingButtons();
            }
        }

        private async Task UpdateLsNr() {
            if (string.IsNullOrEmpty(ViewModel.Date) || ViewModel.Branch == null) {
                ViewModel.LsNr = "";
            } else {
                try {
                    var branch = (Branch)BranchInput.SelectedItem;
                    var date = DateOnly.ParseExact(ViewModel.Date!, "dd.MM.yyyy");
                    using var ctx = new AppDbContext();
                    var lnr = await ctx.NextLNr(date, branch.ZwstId);
                    ViewModel.LsNr = Utils.GenerateLsNr(date, branch.ZwstId, lnr);
                } catch {
                    ViewModel.LsNr = "";
                }
            }
        }

        private new async void DateInput_TextChanged(object sender, TextChangedEventArgs evt) {
            base.DateInput_TextChanged(sender, evt);
            if (IsEditing || IsCreating) await UpdateLsNr();
        }

        private async void BranchInput_SelectionChanged(object sender, SelectionChangedEventArgs evt) {
            base.ComboBox_SelectionChanged(sender, evt);
            if (IsEditing || IsCreating) {
                await UpdateLsNr();
                InitialDefaultInputs();
            }
        }

        private void UpdateWineVariety(bool valid) {
            if (valid) {
                var text = ViewModel.SortId!;
                ControlUtils.SelectItemWithPk(WineVarietyInput, text[0..2]);
                if (text.Length >= 3) {
                    ControlUtils.SelectItemWithPk(AttributeInput, text[2..]);
                    ControlUtils.SelectItemWithPk(CultivationInput, text[2..]);
                    ViewModel.SortId = text[0..2];
                }
            } else {
                WineVarietyInput.SelectedItem = null;
                AttributeInput.SelectedIndex = 0;
                CultivationInput.SelectedIndex = 0;
            }
            UpdateWineQualityLevels();
        }

        private void SortIdInput_TextChanged(object sender, TextChangedEventArgs evt) {
            UpdateWineVariety(InputTextChanged((TextBox)sender, Validator.CheckSortId));
        }

        private void SortIdInput_LostFocus(object sender, RoutedEventArgs evt) {
            UpdateWineVariety(InputLostFocus((TextBox)sender, Validator.CheckSortId));
        }

        private void WineVarietyInput_SelectionChanged(object sender, SelectionChangedEventArgs evt) {
            if (WineVarietyInput.SelectedItem is WineVar s)
                ViewModel.SortId = s.SortId;
            UpdateWineQualityLevels();
            if (!ViewModel.WineVar?.IsQuw ?? false) {
                App.MainDispatcher.BeginInvoke(() => {
                    MessageBox.Show("Die eingegebene Sorte darf nicht als Qualitätswein\nübernommen werden!", "Kein Qualitätswein",
                        MessageBoxButton.OK, MessageBoxImage.Warning);
                });
            }
        }

        private WineQualLevel GetWineQualityLevel(double kmw, string? maxQualId = null) {
            return WineQualityLevels
                .Where(q => !q.IsPredicate && (q.MinKmw == null || q.MinKmw <= kmw))
                .Where(q => maxQualId == null || q.QualId == "WEI" || q.QualId == maxQualId)
                .OrderBy(q => q.MinKmw)
                .Last();
        }

        private void UpdateWineQualityLevels() {
            if (!GetInputValid(GradationKmwInput)) {
                UnsetDefaultValue(WineQualityLevelInput);
                ComboBox_SelectionChanged(WineQualityLevelInput, null);
                WineQualityLevelInput.ItemsSource = WineQualityLevels;
                WineQualityLevelInput.SelectedItem = null;
                return;
            }
            var kmw = (double)ViewModel.GradationKmw!;
            var max = ViewModel.WineVar?.MaxQualId;
            var quw = ViewModel.WineVar?.IsQuw ?? true;
            WineQualityLevelInput.ItemsSource = WineQualityLevels
                .Where(q => q.MinKmw == null || q.MinKmw <= kmw)
                .Where(q => quw || q.QualId == "WEI" || q.QualId == max)
                .ToList();
            var qual = GetWineQualityLevel(kmw, !quw ? max : null);
            SetDefaultValue(WineQualityLevelInput, qual);
            if (WineQualityLevelInput.SelectedItem == null || (WineQualityLevelInput.SelectedItem is WineQualLevel selected && !selected.IsPredicate)) {
                ControlUtils.SelectItem(WineQualityLevelInput, qual);
            }
        }

        private void UpdateGradationKmw() {
            IsUpdatingGradation = true;
            var caret = GradationKmwInput.CaretIndex;
            GradationKmwInput.Text = $"{Utils.OeToKmw(double.Parse(GradationOeInput.Text)):#.0}";
            GradationKmwInput.CaretIndex = caret;
            IsUpdatingGradation = false;
        }

        private void UpdateGradationOe() {
            IsUpdatingGradation = true;
            var caret = GradationOeInput.CaretIndex;
            GradationOeInput.Text = $"{Utils.KmwToOe(double.Parse(GradationKmwInput.Text)):#}";
            GradationOeInput.CaretIndex = caret;
            IsUpdatingGradation = false;
        }

        private void GradationOeInput_TextChanged(object sender, TextChangedEventArgs evt) {
            var valid = InputTextChanged((TextBox)sender, Validator.CheckGradatoinOe);
            if (!IsUpdatingGradation) {
                if (valid) UpdateGradationKmw();
                else if (GradationOeInput.Text.Length == 0) GradationKmwInput.Text = "";
                if (valid || GradationOeInput.Text.Length == 0) UpdateWineQualityLevels();
            }
        }

        private void GradationOeInput_LostFocus(object sender, RoutedEventArgs evt) {
            InputLostFocus((TextBox)sender, Validator.CheckGradatoinOe);
        }

        private void GradationKmwInput_TextChanged(object sender, TextChangedEventArgs evt) {
            var valid = InputTextChanged((TextBox)sender, Validator.CheckGradationKmw);
            if (!IsUpdatingGradation) {
                if (valid) UpdateGradationOe();
                else if (GradationKmwInput.Text.Length == 0) GradationOeInput.Text = "";
                if (valid || GradationKmwInput.Text.Length == 0) UpdateWineQualityLevels();
            }
        }

        private void GradationKmwInput_LostFocus(object sender, RoutedEventArgs evt) {
            if (GradationKmwInput.Text.EndsWith(',')) GradationKmwInput.Text += "0";
            InputLostFocus((TextBox)sender, Validator.CheckGradationKmw);
            if (GradationKmwInput.Text.Length != 0 && !GradationKmwInput.Text.Contains(','))
                GradationKmwInput.Text += ",0";
        }

        private void ModifiersInput_SelectionChanged(object sender, SelectionChangedEventArgs evt) {
            if (!IsEditing && !IsCreating) return;
            var mod = ViewModel.Modifiers.ToList();
            var source = ViewModel.ModifiersSource.ToList();
            if (App.Client.IsMatzen) {
                var kl = mod.Where(m => m.Name.StartsWith("Klasse "));
                if (kl.Count() > 1) {
                    App.MainDispatcher.BeginInvoke(() => {
                        foreach (var r in kl.Take(kl.Count() - 1))
                            ViewModel.Modifiers.Remove(r);
                    });
                }
            } else if (App.Client.IsWinzerkeller) {
                if (source.Any(m => m.Name.Contains("Lesewagen"))) {
                    if (mod.Any(m => m.Name.Contains("Lesewagen"))) {
                        ViewModel.IsUnloadingPumped = true;
                    } else {
                        ViewModel.IsUnloadingPumped = false;
                    }
                }
            } else if (App.Client.IsBaden) {
                if (source.Any(m => m.Name.Contains("Kiste"))) {
                    if (mod.Any(m => m.Name.Contains("Kiste"))) {
                        ViewModel.IsUnloadingBox = true;
                    } else {
                        ViewModel.IsUnloadingBox = false;
                    }
                }
            }
        }

        private void UnloadingInput_Checked(object sender, RoutedEventArgs evt) {
            if (!IsEditing && !IsCreating) return;
            var mod = ViewModel.Modifiers.ToList();
            var source = ViewModel.ModifiersSource.ToList();
            if (App.Client.IsMatzen) {
                var kl = mod.Where(m => m.Name.StartsWith("Klasse ")).Select(m => m.ModId).LastOrDefault("_")[0];
                if (ViewModel.IsUnloadingPumped && (kl == 'A' || kl == '_')) {
                    kl = 'B';
                } else if (ViewModel.IsUnloadingDumper && kl == '_') {
                    kl = 'A';
                } else {
                    kl = '_';
                }
                var newKl = source.FirstOrDefault(m => m?.ModId == kl.ToString(), null);
                if (newKl != null) ViewModel.Modifiers.Add(newKl);
            } else if (App.Client.IsWinzerkeller) {
                if (source.Any(m => m.Name.Contains("Lesewagen"))) {
                    if (ViewModel.IsUnloadingPumped && !mod.Any(m => m.Name.Contains("Lesewagen"))) {
                        ViewModel.Modifiers.Add(source.First(m => m.Name.Contains("Lesewagen")));
                    } else if (!ViewModel.IsUnloadingPumped && mod.Any(m => m.Name.Contains("Lesewagen"))) {
                        ViewModel.Modifiers.Remove(mod.First(m => m.Name.Contains("Lesewagen")));
                    }
                }
            } else if (App.Client.IsBaden) {
                if (source.Any(m => m.Name.Contains("Kiste"))) {
                    if (ViewModel.IsUnloadingBox && !mod.Any(m => m.Name.Contains("Kiste"))) {
                        ViewModel.Modifiers.Add(source.First(m => m.Name.Contains("Kiste")));
                    } else if (!ViewModel.IsUnloadingBox && mod.Any(m => m.Name.Contains("Kiste"))) {
                        ViewModel.Modifiers.Remove(mod.First(m => m.Name.Contains("Kiste")));
                    }
                }
            }
            RadioButton_Changed(sender, evt);
        }

        private void UnloadingInput_Unchecked(object sender, RoutedEventArgs evt) {
            if (!IsEditing && !IsCreating) return;
            RadioButton_Changed(sender, evt);
        }

        private void UpdateWineOrigin() {
            var qual = WineQualityLevelInput.SelectedItem as WineQualLevel;
            var kg = (WineKgInput.SelectedItem as AT_Kg)?.WbKg;
            if (qual == null || kg == null) {
                WineOriginInput.IsEnabled = (IsEditing || IsCreating);
                UnsetDefaultValue(WineOriginInput);
                ComboBox_SelectionChanged(WineOriginInput, null);
                return;
            }
            WineOriginInput.IsEnabled = false;
            var o = kg.Origin;
            while (o != null && o.Level > qual.OriginLevel) o = o.Parent;
            SetDefaultValue(WineOriginInput, o);
            ControlUtils.SelectItem(WineOriginInput, o);
        }

        private void WineQualityLevelInput_SelectionChanged(object sender, SelectionChangedEventArgs evt) {
            UpdateWineOrigin();
            UpdateAbgewertet();
        }

        private void UpdateRdInput() {
            if (WineKgInput.SelectedItem is AT_Kg kg) {
                using var ctx = new AppDbContext();
                var list = ctx.WbRde.Where(r => r.KgNr == kg.KgNr).OrderBy(r => r.Name).Cast<object>().ToList();
                list.Insert(0, new NullItem());
                ControlUtils.RenewItemsSource(WineRdInput, list);
                if (WineRdInput.SelectedItem == null) WineRdInput.SelectedIndex = 0;
                WineRdInput.IsEnabled = (IsEditing || IsCreating) && list.Count > 1;
            } else {
                ViewModel.WineRdSource = [];
                WineRdInput.IsEnabled = false;
            }
        }

        private void WineKgInput_SelectionChanged(object sender, SelectionChangedEventArgs evt) {
            UpdateWineOrigin();
            UpdateRdInput();
        }

        private void UpdateAbgewertet() {
            if (!GetInputValid(GradationKmwInput))
                return;
            var qual = WineQualityLevelInput.SelectedItem as WineQualLevel;
            if (qual == null) {
                AbgewertetInput.IsChecked = false;
                return;
            }
            var defQual = GetWineQualityLevel(ViewModel.GradationKmw!.Value, !(ViewModel.WineVar?.IsQuw ?? true) ? ViewModel.WineVar?.MaxQualId : null);
            AbgewertetInput.IsChecked = !qual.IsPredicate && defQual != qual;
        }

        private void WeightInput_TextChanged(object sender, TextChangedEventArgs evt) {
            InputTextChanged((TextBox)sender);
        }

        private void TemperatureAcidInput_TextChanged(object sender, TextChangedEventArgs evt) {
            InputTextChanged((TextBox)sender, Validator.CheckDecimal((TextBox)sender, false, 2, 1));
        }

        private void TemperatureAcidInput_LostFocus(object sender, RoutedEventArgs evt) {
            if (sender is not TextBox tb) return;
            if (tb.Text.Length > 0) {
                if (!tb.Text.Contains(',')) tb.Text += ",0";
                if (tb.Text.EndsWith(',')) tb.Text += "0";
            }
            InputLostFocus(tb, Validator.CheckDecimal(tb, false, 2, 1));
        }

        private void GerebeltGewogenInput_Changed(object sender, RoutedEventArgs evt) {
            if ((IsEditing || IsCreating) && App.Config.WeighingMode != WeighingMode.Net) {
                HandPickedInput.IsChecked = !GerebeltGewogenInput.IsChecked;
            }
            if (!ViewModel.IsReceipt || App.Config.WeighingMode == WeighingMode.Net) {
                GerebeltGewogenInput.IsChecked ??= false;
            }
            CheckBox_Changed(sender, evt);
        }

        private void HandPickedInput_Changed(object sender, RoutedEventArgs evt) {
            if ((IsEditing || IsCreating) && App.Config.WeighingMode != WeighingMode.Net) {
                GerebeltGewogenInput.IsChecked = !HandPickedInput.IsChecked;
            }
            CheckBox_Changed(sender, evt);
        }

        private void MemberReferenceButton_Click(object sender, RoutedEventArgs evt) {
            if (MemberInput.SelectedItem is not Member m) return;
            App.FocusMember(m.MgNr);
        }
    }
}
