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 Elwig.Helpers;
using Elwig.Helpers.Billing;
using Elwig.Models.Entities;
using ScottPlot.Plottables;
using ScottPlot;

namespace Elwig.Windows {
    public partial class ChartWindow : ContextWindow {

        public static readonly Color ColorUngebunden = Colors.Blue;
        public static readonly Color ColorGebunden = Colors.Gold;

        public readonly int Year;
        public readonly int AvNr;
        public Season Season;
        public string CurrencySymbol;
        private PaymentVar PaymentVar;
        private bool HasChanged = false;
        private bool Updating = false;

        private Scatter DataPlot;
        private Scatter? GebundenPlot;
        private Marker HighlightedPointPlot;
        private Marker PrimaryMarkedPointPlot;
        private Marker SecondaryMarkedPointPlot;
        private Text TooltipPlot;

        private static readonly LegendItem
            UngebundenLegend = new() {
                LabelText = "Ungebunden", LineWidth = 1, LineColor = ColorUngebunden,
                MarkerStyle = new(MarkerShape.FilledCircle, 5, ColorUngebunden)
            },
            GebundenLegend = new() {
                LabelText = "Gebunden", LineWidth = 1, LineColor = ColorGebunden,
                MarkerStyle = new(MarkerShape.FilledCircle, 5, ColorGebunden)
            },
            LdwLegend = new() {
                LabelText = "68 °Oe (LDW)", LineWidth = 2, LineColor = Colors.Red, MarkerStyle = MarkerStyle.None
            },
            QuwLegend = new() {
                LabelText = "73 °Oe (QUW)", LineWidth = 2, LineColor = Colors.Orange, MarkerStyle = MarkerStyle.None
            },
            KabLegend = new() {
                LabelText = "84 °Oe (KAB)", LineWidth = 2, LineColor = Colors.Green, MarkerStyle = MarkerStyle.None
            };

        private (Graph? Graph, int Index) LastHighlighted = (null, -1);
        private (Graph? Graph, int Index) Highlighted = (null, -1);
        private Graph? ActiveGraph = null;
        private int PrimaryMarkedPoint = -1;
        private int SecondaryMarkedPoint = -1;
        private bool HoverChanged = false;
        private bool HoverActive = false;
        private bool FillingInputs = false;

        private List<GraphEntry> GraphEntries = [];
        private GraphEntry? SelectedGraphEntry => (GraphEntry)GraphList.SelectedItem;
        private List<Varibute> Vaributes = [];
        private bool AllVaributesAssigned => Vaributes.All(v => v.AssignedGraphId != null);
        private bool AllVaributesAssignedAbgew => Vaributes.All(v => v.AssignedAbgewGraphId != null);

        public ChartWindow(int year, int avnr) {
            InitializeComponent();
            Year = year;
            AvNr = avnr;
            using var ctx = new AppDbContext();
            Season = ctx.Seasons.Find(year) ?? throw new ArgumentException("Season not found");
            CurrencySymbol = Season.Currency.Symbol ?? Season.Currency.Code;
            PriceInput.Unit = $"{CurrencySymbol}/kg";
            GebundenFlatBonus.Unit = $"{CurrencySymbol}/kg";
            PaymentVar = ctx.PaymentVariants.Find(year, avnr) ?? throw new ArgumentException("PaymentVar not found");
            Title = $"{PaymentVar?.Name} - Lese {year} - Elwig";
            LockContext = true;
        }

        private void Window_Loaded(object sender, RoutedEventArgs evt) {
        }

        private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) {
            if (HasChanged) {
                var r = MessageBox.Show("Soll das Fenster wirklich geschlossen werden? Nicht gespeicherte Änderungen werden NICHT übernommen!", "Schließen bestätigen",
                    MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
                if (r != MessageBoxResult.Yes) {
                    e.Cancel = true;
                    return;
                }
            }
        }

        private void SetHasChanged(bool hasChanged = true) {
            HasChanged = hasChanged;
            SaveButton.IsEnabled = hasChanged;
        }

        private async Task RefreshGraphList(AppDbContext ctx) {
            PaymentVar = await ctx.PaymentVariants.FindAsync(Year, AvNr) ?? throw new ArgumentException("PaymentVar not found");
            Season = await ctx.Seasons.FindAsync(Year) ?? throw new ArgumentException("Season not found");
            CurrencySymbol = Season.Currency.Symbol ?? Season.Currency.Code;
            PriceInput.Unit = $"{CurrencySymbol}/kg";
            GebundenFlatBonus.Unit = $"{CurrencySymbol}/kg";

            try {
                var data = EditBillingData.FromJson(PaymentVar.Data, Utils.GetVaributes(ctx, Year));
                var paymentEntries = data.GetPaymentGraphEntries(ctx, Season);
                GraphEntries = [
                    ..paymentEntries,
                    ..data.GetQualityGraphEntries(ctx, Season, paymentEntries.Any() ? paymentEntries.Max(e => e.Id) : 0)
                ];
            } catch (KeyNotFoundException ex) {
                var key = ex.Message.Split('\'')[1].Split('\'')[0];
                MessageBox.Show($"Fehler beim Laden der Auszahlungsvariante:\n\n" +
                    $"Mit unbekanntem Attribut '{key}' kann nicht umgegangen werden.", "Fehler",
                    MessageBoxButton.OK, MessageBoxImage.Error);
            } catch (ArgumentException) {
                MessageBox.Show($"Fehler beim Laden der Auszahlungsvariante:\n\n" +
                    $"Die Daten der Auszahlungsvariante entsprechen nicht dem benötigtem Format.", "Fehler",
                    MessageBoxButton.OK, MessageBoxImage.Error);
            } catch (Exception ex) {
                MessageBox.Show("Fehler beim Laden der Auszahlungsvariante:\n\n" + ex.Message, "Fehler",
                    MessageBoxButton.OK, MessageBoxImage.Error);
            }
            Vaributes = Utils.GetVaributeList(ctx, Year);
            GraphEntries.ForEach(e => {
                e.Vaributes.ForEach(v => {
                    var found = Vaributes.Find(a => a.Variety?.SortId == v.Variety?.SortId && a.Attribute?.AttrId == v.Attribute?.AttrId && a.Cultivation?.CultId == v.Cultivation?.CultId);
                    if (found == null) return;
                    if (e.Abgewertet) {
                        found.AssignedAbgewGraphId = e.Id;
                    } else {
                        found.AssignedGraphId = e.Id;
                    }
                });
            });

            FillingInputs = true;
            ControlUtils.RenewItemsSource(VaributeInput, Vaributes);
            FillingInputs = false;
            ControlUtils.RenewItemsSource(GraphList, GraphEntries, GraphList_SelectionChanged, ControlUtils.RenewSourceDefault.First);

            RefreshInputs();
        }

        private void RefreshInputs() {
            ResetPlot();
            if (SelectedGraphEntry != null) {
                CopyButton.IsEnabled = true;
                DeleteButton.IsEnabled = true;
                EnableTextBox(OechsleInput);
                GebInput.IsEnabled = SelectedGraphEntry?.GebundenGraph != null;
                GebundenTypeFixed.IsEnabled = true;
                GebundenTypeGraph.IsEnabled = true;
                GebundenTypeNone.IsEnabled = true;
                VaributeInput.IsEnabled = true;
                AbgewertetInput.IsEnabled = true;
                EnableOptionButtons();
                FillInputs();
            } else {
                CopyButton.IsEnabled = false;
                DeleteButton.IsEnabled = false;
                DisableTextBox(OechsleInput);
                GebInput.IsEnabled = false;
                DisableOptionButtons();
            }
            if (!PaymentVar.TestVariant) {
                AddButton.IsEnabled = false;
                CopyButton.IsEnabled = false;
                DeleteButton.IsEnabled = false;
                DisableTextBox(PriceInput);
                GebundenTypeFixed.IsEnabled = false;
                GebundenTypeGraph.IsEnabled = false;
                GebundenTypeNone.IsEnabled = false;
                VaributeInput.IsEnabled = false;
                AbgewertetInput.IsEnabled = false;
            }
            GC.Collect();
        }

        private void FillInputs() {
            FillingInputs = true;

            AbgewertetInput.IsChecked = SelectedGraphEntry?.Abgewertet;
            if (SelectedGraphEntry?.GebundenFlatBonus is double bonus) {
                GebundenTypeFixed.IsChecked = true;
                GebundenFlatBonus.Text = $"{bonus}";
            } else if (SelectedGraphEntry?.GebundenGraph != null) {
                GebundenTypeGraph.IsChecked = true;
                GebundenFlatBonus.Text = "";
            } else {
                GebundenTypeNone.IsChecked = true;
                GebundenFlatBonus.Text = "";
            }

            ControlUtils.SelectItems(VaributeInput, SelectedGraphEntry?.Vaributes ?? []);

            InitPlot();
            ChangeActiveGraph(SelectedGraphEntry?.DataGraph);
            OechsleInput.Text = "73";
            OechslePricePlot.IsEnabled = true;
            FillingInputs = false;
        }

        protected override async Task OnRenewContext(AppDbContext ctx) {
            await RefreshGraphList(ctx);
        }

        private void InitPlot() {
            RefreshGradationLines();

            if (SelectedGraphEntry?.GebundenGraph != null) {
                GebundenPlot = OechslePricePlot.Plot.Add.Scatter(SelectedGraphEntry.GebundenGraph.DataX, SelectedGraphEntry.GebundenGraph.DataY);
                GebundenPlot.LineStyle.Color = ColorGebunden;
                GebundenPlot.Color = ColorGebunden;
                GebundenPlot.MarkerStyle = new MarkerStyle(MarkerShape.FilledCircle, 9, ColorGebunden);
            }

            DataPlot = OechslePricePlot.Plot.Add.Scatter(SelectedGraphEntry!.DataGraph.DataX, SelectedGraphEntry!.DataGraph.DataY);
            DataPlot.LineStyle.Color = ColorUngebunden;
            DataPlot.Color = ColorUngebunden;
            DataPlot.MarkerStyle = new MarkerStyle(MarkerShape.FilledCircle, 9, ColorUngebunden);

            OechslePricePlot.Plot.Axes.SetLimits(Math.Min(GraphEntry.MinX, GraphEntry.MinXGeb) - 1, GraphEntry.MaxX + 1, -0.1, 1.5);

            HighlightedPointPlot = OechslePricePlot.Plot.Add.Marker(0, 0, MarkerShape.OpenCircle, 10, Colors.Red);
            HighlightedPointPlot.IsVisible = false;

            PrimaryMarkedPointPlot = OechslePricePlot.Plot.Add.Marker(0, 0, MarkerShape.FilledCircle, 6, Colors.Red);
            PrimaryMarkedPointPlot.IsVisible = false;

            SecondaryMarkedPointPlot = OechslePricePlot.Plot.Add.Marker(0, 0, MarkerShape.FilledCircle, 6, Colors.Red);
            SecondaryMarkedPointPlot.IsVisible = false;



            OechslePricePlot.Refresh();

            RefreshFreeZoom();
            OechslePricePlot.Refresh();
        }

        private void ResetPlot() {
            PrimaryMarkedPoint = -1;
            SecondaryMarkedPoint = -1;
            ChangeActiveGraph(null);
            HideGradationLines();
            OechslePricePlot.Plot.Remove(DataPlot);
            if (GebundenPlot != null) {
                OechslePricePlot.Plot.Remove(GebundenPlot);
                GebundenPlot = null;
            }
            OechslePricePlot.Plot.Clear();
            OechslePricePlot.Reset();
            OechslePricePlot.Refresh();
        }

        private void ChangeMarker(Marker point, bool visible, double x = 0, double y = 0) {
            point.Location = new Coordinates(x, y);
            point.IsVisible = visible;
        }

        private void EnableActionButtons() {
            if (PaymentVar.TestVariant) {
                LeftFlatButton.IsEnabled = true;
                RightFlatButton.IsEnabled = true;
                LinearIncreaseButton.IsEnabled = true;
            }
        }

        private void DisableActionButtons() {
            LeftFlatButton.IsEnabled = false;
            RightFlatButton.IsEnabled = false;
            InterpolateButton.IsEnabled = false;
            LinearIncreaseButton.IsEnabled = false;
        }

        private void FreeZoomInput_Changed(object sender, RoutedEventArgs evt) {
            RefreshFreeZoom();
        }

        private void RefreshFreeZoom() {
            if (FreeZoomInput.IsChecked == true) {
                UnlockZoom();
            } else {
                LockZoom();
            }
            OechslePricePlot.Refresh();
        }

        private void LockZoom() {
            ScottPlot.AxisRules.MaximumBoundary BoundaryRule = new(
                xAxis: OechslePricePlot.Plot.Axes.Bottom,
                yAxis: OechslePricePlot.Plot.Axes.Left,
                limits: new AxisLimits(GraphEntry.MinX - 1, GraphEntry.MaxX + 1, -0.1, 2));

            ScottPlot.AxisRules.MaximumSpan SpanRule = new(
                xAxis: OechslePricePlot.Plot.Axes.Bottom,
                yAxis: OechslePricePlot.Plot.Axes.Left,
                xSpan: GraphEntry.MaxX - GraphEntry.MinX + 2,
                ySpan: 2.1);

            OechslePricePlot.Plot.Axes.Rules.Clear();
            OechslePricePlot.Plot.Axes.Rules.Add(BoundaryRule);
            OechslePricePlot.Plot.Axes.Rules.Add(SpanRule);
            OechslePricePlot.Plot.Axes.SetLimits(GraphEntry.MinX - 1, GraphEntry.MaxX + 1, -0.1, 1.5);
        }

        private void UnlockZoom() {
            ScottPlot.AxisRules.MaximumSpan SpanRule = new(
                xAxis: OechslePricePlot.Plot.Axes.Bottom,
                yAxis: OechslePricePlot.Plot.Axes.Left,
                xSpan: (GraphEntry.MaxX - GraphEntry.MinX) * 1.5,
                ySpan: 3.5);

            OechslePricePlot.Plot.Axes.Rules.Clear();
            OechslePricePlot.Plot.Axes.Rules.Add(SpanRule);
        }

        private void EnableOptionButtons() {
            FreeZoomInput.IsEnabled = true;
            GradationLinesInput.IsEnabled = true;
            TooltipInput.IsEnabled = true;
        }

        private void DisableOptionButtons() {
            FreeZoomInput.IsEnabled = false;
            GradationLinesInput.IsEnabled = false;
            TooltipInput.IsEnabled = false;
        }

        private void GradationLinesInput_Changed(object sender, RoutedEventArgs evt) {
            RefreshGradationLines();
        }

        private void RefreshGradationLines() {
            if (GradationLinesInput.IsChecked == true && SelectedGraphEntry != null && !OechslePricePlot.Plot.PlottableList.OfType<VerticalLine>().Any()) {
                ShowGradationLines();
                ShowLegend();
            } else if (GradationLinesInput.IsChecked == false) {
                HideGradationLines();
                HideLegend();
            }
            OechslePricePlot.Refresh();
        }

        private void ShowGradationLines() {
            OechslePricePlot.Plot.Add.VerticalLine(68, 2, Colors.Red);
            OechslePricePlot.Plot.Add.VerticalLine(73, 2, Colors.Orange);
            OechslePricePlot.Plot.Add.VerticalLine(84, 2, Colors.Green);
        }

        private void HideGradationLines() {
            OechslePricePlot.Plot.PlottableList.RemoveAll(p => p is VerticalLine);
        }

        private void ShowLegend() {
            OechslePricePlot.Plot.Legend.Alignment = Alignment.UpperLeft;
            OechslePricePlot.Plot.Legend.IsVisible = true;

            OechslePricePlot.Plot.Legend.ManualItems.Add(LdwLegend);
            OechslePricePlot.Plot.Legend.ManualItems.Add(QuwLegend);
            OechslePricePlot.Plot.Legend.ManualItems.Add(KabLegend);
            OechslePricePlot.Plot.Legend.ManualItems.Add(UngebundenLegend);
            if (SelectedGraphEntry?.GebundenGraph != null) OechslePricePlot.Plot.Legend.ManualItems.Add(GebundenLegend);
        }

        private void HideLegend() {
            OechslePricePlot.Plot.Legend.IsVisible = false;
            OechslePricePlot.Plot.Legend.ManualItems.Clear();
        }

        private void UpdateSelectedPoint() {
            if (ActiveGraph == null || SelectedGraphEntry == null)
                return;

            SecondaryMarkedPoint = -1;
            ChangeMarker(SecondaryMarkedPointPlot, false);

            if (int.TryParse(OechsleInput.Text, out int oe)) {
                if (oe >= ActiveGraph.MinX && oe <= ActiveGraph.MaxX) {
                    PrimaryMarkedPoint = oe - ActiveGraph.MinX;
                    ChangeMarker(PrimaryMarkedPointPlot, true, ActiveGraph.GetOechsleAt(PrimaryMarkedPoint), ActiveGraph.GetPriceAt(PrimaryMarkedPoint));

                    PriceInput.Text = Math.Round(ActiveGraph.GetPriceAt(PrimaryMarkedPoint), Season.Precision).ToString();

                    EnableActionButtons();
                    OechslePricePlot.Refresh();
                    EnableTextBox(PriceInput);
                    return;
                }
            }

            PrimaryMarkedPoint = -1;
            ChangeMarker(PrimaryMarkedPointPlot, false);
            DisableActionButtons();
            PriceInput.Text = "";
            DisableTextBox(PriceInput);
            OechslePricePlot.Refresh();
            DisableTextBox(PriceInput);
        }

        private void OechsleInput_TextChanged(object sender, TextChangedEventArgs evt) {
            UpdateSelectedPoint();
        }

        private void GebInput_Changed(object sender, RoutedEventArgs evt) {
            if (Updating)
                return;
            var sel = GebInput.IsChecked == true ? SelectedGraphEntry?.GebundenGraph : SelectedGraphEntry?.DataGraph;
            if (sel != ActiveGraph) ChangeActiveGraph(sel);
            UpdateSelectedPoint();
        }

        private void PriceInput_TextChanged(object sender, TextChangedEventArgs evt) {
            if (PrimaryMarkedPoint != -1 && ActiveGraph != null && PriceInput.IsKeyboardFocusWithin == true) {
                var res = Validator.CheckDecimal(PriceInput, true, 2, Season.Precision);
                if (res.IsValid && double.TryParse(PriceInput.Text, out double price)) {
                    ActiveGraph.SetPriceAt(PrimaryMarkedPoint, price);
                    PrimaryMarkedPointPlot.Location = new Coordinates(PrimaryMarkedPointPlot.Location.X, price);
                    SetHasChanged();
                    OechslePricePlot.Refresh();
                    CheckGebundenTypeFixed();
                }
            }
        }

        private void LeftFlatButton_Click(object sender, RoutedEventArgs evt) {
            if (PrimaryMarkedPoint == -1 || ActiveGraph == null) {
                return;
            }
            ActiveGraph.FlattenGraphLeft(PrimaryMarkedPoint);
            SetHasChanged();
            OechslePricePlot.Refresh();
            CheckGebundenTypeFixed();
        }

        private void RightFlatButton_Click(object sender, RoutedEventArgs evt) {
            if (PrimaryMarkedPoint == -1 || ActiveGraph == null) {
                return;
            }
            ActiveGraph.FlattenGraphRight(PrimaryMarkedPoint);
            SetHasChanged();
            OechslePricePlot.Refresh();
            CheckGebundenTypeFixed();
        }

        private void InterpolateButton_Click(object sender, RoutedEventArgs evt) {
            if (PrimaryMarkedPoint == SecondaryMarkedPoint || PrimaryMarkedPoint == -1 || SecondaryMarkedPoint == -1 || ActiveGraph == null) {
                return;
            }
            ActiveGraph.InterpolateGraph(PrimaryMarkedPoint, SecondaryMarkedPoint);
            SetHasChanged();
            OechslePricePlot.Refresh();
            CheckGebundenTypeFixed();
        }

        private void LinearIncreaseButton_Click(object sender, RoutedEventArgs e) { 
            if (PrimaryMarkedPoint == -1 || ActiveGraph == null) {
                return;
            }
            double? priceIncrease = Utils.ShowLinearPriceIncreaseDialog();
            if (priceIncrease == null) {
                return;
            }
            ActiveGraph.LinearIncreaseGraphToEnd(PrimaryMarkedPoint, priceIncrease.Value);
            SetHasChanged();
            OechslePricePlot.Refresh();
            CheckGebundenTypeFixed();
        }

        private void OechslePricePlot_MouseDown(object sender, MouseEventArgs e) {
            if (GraphList.SelectedItem == null)
                return;

            if (HoverActive) {
                if (PaymentVar.TestVariant && Keyboard.IsKeyDown(System.Windows.Input.Key.LeftCtrl)) {
                    if (SecondaryMarkedPoint == -1 || ActiveGraph == null || ActiveGraph != Highlighted.Graph)
                        return;
                    SecondaryMarkedPoint = Highlighted.Index;
                    ChangeMarker(SecondaryMarkedPointPlot, true, ActiveGraph.GetOechsleAt(SecondaryMarkedPoint), ActiveGraph.GetPriceAt(SecondaryMarkedPoint));
                    InterpolateButton.IsEnabled = true;
                    return;
                }

                Updating = true;
                PrimaryMarkedPoint = Highlighted.Index;
                if (ActiveGraph != Highlighted.Graph)
                    ChangeActiveGraph(Highlighted.Graph);
                Updating = false;
                if (PrimaryMarkedPoint == -1 || ActiveGraph == null)
                    return;

                ChangeMarker(PrimaryMarkedPointPlot, true, ActiveGraph.GetOechsleAt(PrimaryMarkedPoint), ActiveGraph.GetPriceAt(PrimaryMarkedPoint));

                Updating = true;
                OechsleInput.Text = Highlighted.Graph!.GetOechsleAt(Highlighted.Index).ToString();
                GebInput.IsChecked = Highlighted.Graph == SelectedGraphEntry?.GebundenGraph;
                PriceInput.Text = Math.Round(Highlighted.Graph.GetPriceAt(Highlighted.Index), Season.Precision).ToString();
                Updating = false;

                EnableActionButtons();
            } else {
                PrimaryMarkedPoint = -1;
                SecondaryMarkedPoint = -1;
                if (SelectedGraphEntry!.GebundenGraph != null) {
                    ChangeActiveGraph(null);
                }
                ChangeMarker(PrimaryMarkedPointPlot, false);
                ChangeMarker(SecondaryMarkedPointPlot, false);

                OechsleInput.Text = "";
                GebInput.IsChecked = false;
                PriceInput.Text = "";
                DisableTextBox(PriceInput);

                DisableActionButtons();
            }
        }

        private void OechslePricePlot_MouseMove(object sender, MouseEventArgs e) {
            MouseChange(e);
        }

        private void OechslePricePlot_MouseWheel(object sender, MouseWheelEventArgs e) {
            MouseChange(e);
        }

        private Coordinates GetMouseCoordinates(Point p) {
            Pixel px = new(p.X, p.Y);
            var source = PresentationSource.FromVisual(this);
            if (source?.CompositionTarget != null) {
                var matrix = source.CompositionTarget.TransformToDevice;
                px.X *= (float)matrix.M11;
                px.Y *= (float)matrix.M22;
            }
            return OechslePricePlot.Plot.GetCoordinates(px);
        }

        private void MouseChange(MouseEventArgs e) {
            if (GraphList.SelectedItem == null) {
                return;
            }

            var mouseOnData = MouseOnPlot(DataPlot, GetMouseCoordinates(e.GetPosition(OechslePricePlot)));
            var mouseOnGebunden = MouseOnPlot(GebundenPlot, GetMouseCoordinates(e.GetPosition(OechslePricePlot)));

            Highlighted = LastHighlighted;

            if (mouseOnData != null) {
                ChangeMarker(HighlightedPointPlot, true, mouseOnData.Value.X, mouseOnData.Value.Y);
                HighlightedPointPlot.IsVisible = true;
                HoverChanged = true ^ HoverActive;
                HoverActive = true;
                HandleTooltip(mouseOnData.Value.X, mouseOnData.Value.Y, mouseOnData.Value.Index, SelectedGraphEntry!.DataGraph, e.GetPosition(OechslePricePlot), e is MouseWheelEventArgs);
            } else if (mouseOnGebunden != null) {
                ChangeMarker(HighlightedPointPlot, true, mouseOnGebunden.Value.X, mouseOnGebunden.Value.Y);
                HighlightedPointPlot.IsVisible = true;
                HoverChanged = true ^ HoverActive;
                HoverActive = true;
                HandleTooltip(mouseOnGebunden.Value.X, mouseOnGebunden.Value.Y, mouseOnGebunden.Value.Index, SelectedGraphEntry!.GebundenGraph!, e.GetPosition(OechslePricePlot), e is MouseWheelEventArgs);
            } else {
                ChangeMarker(HighlightedPointPlot, false);
                HoverChanged = false ^ HoverActive;
                HoverActive = false;
                OechslePricePlot.Plot.PlottableList.Remove(TooltipPlot);
                OechslePricePlot.Refresh();
            }
        }

        private (double X, double Y, int Index)? MouseOnPlot(Scatter? plot, Coordinates c) {
            if (plot == null)
                return null;
            OechslePricePlot.Refresh();
            DataPoint nearestPoint = plot.Data.GetNearest(c, OechslePricePlot.Plot.LastRender, 5);
            return nearestPoint.IsReal ? (nearestPoint.X, nearestPoint.Y, nearestPoint.Index) : null;
        }

        private void HandleTooltip(double pointX, double pointY, int pointIndex, Graph g, Point p, bool force) {
            if (force || LastHighlighted != Highlighted || HoverChanged) {
                OechslePricePlot.Plot.PlottableList.Remove(TooltipPlot);
                if (TooltipInput.IsChecked == true) {
                    Coordinates mouseLocation = GetMouseCoordinates(new(p.X, p.Y - 30));
                    TooltipPlot = OechslePricePlot.Plot.Add.Text($"Oechsle: {pointX:N2}, Preis: {Math.Round(pointY, Season.Precision)} {CurrencySymbol}/kg", mouseLocation.X, mouseLocation.Y);
                    TooltipPlot.LabelFontSize = 12;
                    TooltipPlot.LabelBold = true;
                    TooltipPlot.LabelBorderColor = Colors.Black;
                    TooltipPlot.LabelBorderWidth = 2;
                    TooltipPlot.LabelBackgroundColor = Colors.White;
                    TooltipPlot.LabelPadding = 10;
                    TooltipPlot.LabelAlignment = Alignment.MiddleLeft;
                }
                LastHighlighted = (g, pointIndex);
                HoverChanged = false;
                OechslePricePlot.Refresh();
            }
        }

        private int GetMaxGraphId() {
            return GraphEntries.Count == 0 ? 0 : GraphEntries.Select(g => g.Id).Max();
        }

        private void AddButton_Click(object sender, RoutedEventArgs e) {
            GraphEntry newGraphEntry = new(GetMaxGraphId() + 1, Season.Precision, BillingData.CurveMode.Oe);
            GraphEntries.Add(newGraphEntry);
            SetHasChanged();
            GraphList.Items.Refresh();
            GraphList.SelectedItem = newGraphEntry;
        }

        private void CopyButton_Click(object sender, RoutedEventArgs e) {
            if (SelectedGraphEntry == null) return;

            GraphEntry newGraphEntry = SelectedGraphEntry.Copy(GetMaxGraphId() + 1);
            GraphEntries.Add(newGraphEntry);
            SetHasChanged();
            GraphList.Items.Refresh();
            GraphList.SelectedItem = newGraphEntry;
        }

        private void DeleteButton_Click(object sender, RoutedEventArgs e) {
            if (SelectedGraphEntry == null) return;

            var r = MessageBox.Show(
                $"Soll die Kurve {SelectedGraphEntry.Id} (verwendet in folgenden Verträgen: {SelectedGraphEntry.VaributeStringSimple}) wirklich gelöscht werden?",
                "Kurve löschen", MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);

            if (r == MessageBoxResult.Yes) {
                GraphEntries.Remove(SelectedGraphEntry);
                SetHasChanged();
                GraphList.Items.Refresh();
                OechslePricePlot.IsEnabled = false;
            }
        }

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

            try {
                await Task.Run(async () => {
                    using var ctx = new AppDbContext();
                    var origData = BillingData.FromJson(PaymentVar.Data);
                    var data = BillingData.FromGraphEntries(GraphEntries, origData, Utils.GetVaributes(ctx, Year),
                        AllVaributesAssigned, AllVaributesAssignedAbgew);

                    PaymentVar.Data = data.ToJsonString();
                    ctx.Update(PaymentVar);
                    await ctx.SaveChangesAsync();
                });
            } catch (Exception exc) {
                Mouse.OverrideCursor = null;
                var str = "Der Eintrag konnte nicht in der Datenbank gespeichert werden!\n\n" + exc.Message;
                if (exc.InnerException != null) str += "\n\n" + exc.InnerException.Message;
                MessageBox.Show(str, "Auszahlungsvariante speichern", MessageBoxButton.OK, MessageBoxImage.Error);
                SaveButton.IsEnabled = true;
                return;
            }

            try {
                await Task.Run(async () => {
                    var b = new BillingVariant(PaymentVar.Year, PaymentVar.AvNr);
                    await b.Calculate(false);
                });
            } catch (KeyNotFoundException exc) {
                MessageBox.Show(exc.Message, "Noch nicht alle Preise festgelegt", MessageBoxButton.OK, MessageBoxImage.Information);
            } catch (Exception exc) {
                MessageBox.Show(exc.Message, "Berechnungsfehler", MessageBoxButton.OK, MessageBoxImage.Error);
            }

            LockContext = false;
            App.HintContextChange();
            LockContext = true;
            SetHasChanged(false);

            Mouse.OverrideCursor = null;
        }

        private void EnableTextBox(TextBox u) {
            if (PaymentVar.TestVariant || u == OechsleInput) {
                u.IsEnabled = true;
                u.IsReadOnly = false;
            }
        }

        private void DisableTextBox(TextBox u) {
            u.IsEnabled = false;
            u.IsReadOnly = true;
        }

        private void ChangeActiveGraph(Graph? g) {
            if (g != null && g == SelectedGraphEntry?.DataGraph) {
                EnableTextBox(OechsleInput);
                if (SelectedGraphEntry?.GebundenGraph != null) ChangeLineWidth(DataPlot, 4);
                ChangeLineWidth(GebundenPlot, 1);
                GebInput.IsEnabled = SelectedGraphEntry?.GebundenGraph != null;
                GebInput.IsChecked = false;
            } else if (g != null && g == SelectedGraphEntry?.GebundenGraph) {
                EnableTextBox(OechsleInput);
                ChangeLineWidth(GebundenPlot, 4);
                ChangeLineWidth(DataPlot, 1);
                GebInput.IsEnabled = SelectedGraphEntry?.GebundenGraph != null;
                GebInput.IsChecked = true;
            } else {
                DisableTextBox(OechsleInput);
                DisableTextBox(PriceInput);
                OechsleInput.Text = "";
                GebInput.IsEnabled = false;
                GebInput.IsChecked = false;
                PriceInput.Text = "";
                ChangeLineWidth(DataPlot, 1);
                ChangeLineWidth(GebundenPlot, 1);
            }
            ActiveGraph = g;
        }

        private void ChangeLineWidth(Scatter? p, double lineWidth) {
            if (p != null) {
                p.LineWidth = (float)lineWidth;
            }
        }

        private void GraphList_SelectionChanged(object sender, SelectionChangedEventArgs e) {
            RefreshInputs();
        }

        private void GebundenFlatBonus_TextChanged(object sender, TextChangedEventArgs e) {
            if (FillingInputs) return;
            var r = Validator.CheckDecimal(GebundenFlatBonus, true, 2, Season.Precision);
            if (r.IsValid && SelectedGraphEntry != null) {
                SelectedGraphEntry.GebundenFlatBonus = double.Parse(GebundenFlatBonus.Text);
                ResetPlot();
                InitPlot();
            }
        }

        private void VaributeInput_Changed(object sender, SelectionChangedEventArgs evt) {
            if (FillingInputs) return;
            var isOpen = VaributeInput.IsDropDownOpen;

            foreach (var i in evt.AddedItems) {
                if (i is not Varibute v) continue;
                if (RemoveVaributeFromOthers(v.ToString())) {
                    if (AbgewertetInput.IsChecked == true) {
                        v.AssignedAbgewGraphId = SelectedGraphEntry?.Id;
                    } else {
                        v.AssignedGraphId = SelectedGraphEntry?.Id;
                    }
                } else {
                    VaributeInput.SelectedItems.Remove(v);
                }
            }
            foreach (var i in evt.RemovedItems) {
                if (i is not Varibute v) continue;
                if (AbgewertetInput.IsChecked == true) {
                    v.AssignedAbgewGraphId = null;
                } else {
                    v.AssignedGraphId = null;
                }
            }

            SelectedGraphEntry!.Vaributes = VaributeInput.SelectedItems.Cast<Varibute>().ToList();
            SetHasChanged();
            GraphList.Items.Refresh();
            VaributeInput.Items.Refresh();
            VaributeInput.IsDropDownOpen = isOpen;
        }

        private bool RemoveVaributeFromOthers(string? varibute) {
            if (varibute == null) return true;
            foreach (var ge in GraphEntries) {
                if (ge != SelectedGraphEntry && ge.Abgewertet == SelectedGraphEntry?.Abgewertet) {
                    var toRemove = ge.Vaributes.Where(c => c.Listing.Equals(varibute)).ToList();
                    if (toRemove.Count == 0) continue;
                    var r = MessageBox.Show($"Achtung: {string.Join(", ", toRemove)} ist bereits in Kurve {ge.Id} in Verwendung!\nSoll die Zuweisung dort entfernt werden?", "Entfernen bestätigen",
                        MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
                    if (r != MessageBoxResult.Yes) {
                        return false;
                    }
                    ge.Vaributes.RemoveAll(c => c.Listing.Equals(varibute));
                }
            }
            return true;
        }

        private void AbgewertetInput_Changed(object sender, RoutedEventArgs e) {
            if (FillingInputs) return;
            if (SelectedGraphEntry == null) return;
            SelectedGraphEntry.Abgewertet = AbgewertetInput.IsChecked == true;
            SetHasChanged();
            GraphList.Items.Refresh();
        }

        private void GebundenType_Checked(object sender, RoutedEventArgs e) {
            if (FillingInputs) return;
            if (SelectedGraphEntry == null) {
                DisableTextBox(GebundenFlatBonus);
                return;
            }
            if (GebundenTypeNone.IsChecked == true) {
                SelectedGraphEntry.RemoveGebundenGraph();
                DisableTextBox(GebundenFlatBonus);
            } else if (GebundenTypeFixed.IsChecked == true) {
                SelectedGraphEntry.GebundenFlatBonus = double.TryParse(GebundenFlatBonus.Text, out var val) ? val : 0.1;
                SelectedGraphEntry.AddGebundenGraph();
                EnableTextBox(GebundenFlatBonus);
            } else if (GebundenTypeGraph.IsChecked == true) {
                SelectedGraphEntry.AddGebundenGraph();
                DisableTextBox(GebundenFlatBonus);
            }
            SetHasChanged();
            RefreshInputs();
        }

        private void CheckGebundenTypeFixed() {
            FillingInputs = true;
            if (SelectedGraphEntry?.GebundenFlatBonus is double bonus) {
                GebundenTypeFixed.IsChecked = true;
                GebundenFlatBonus.Text = $"{bonus}";
                EnableTextBox(GebundenFlatBonus);
            } else if (SelectedGraphEntry?.GebundenGraph != null) {
                GebundenTypeGraph.IsChecked = true;
                GebundenFlatBonus.Text = "";
                DisableTextBox(GebundenFlatBonus);
            }
            FillingInputs = false;
        }
    }
}
