503 lines
19 KiB
C#
503 lines
19 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Drawing;
|
|
using System.Linq;
|
|
using System.Text.Json;
|
|
using System.Text.Json.Nodes;
|
|
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 Microsoft.EntityFrameworkCore;
|
|
using Microsoft.EntityFrameworkCore.ChangeTracking;
|
|
using ScottPlot;
|
|
using ScottPlot.Plottable;
|
|
|
|
namespace Elwig.Windows {
|
|
public partial class ChartWindow : ContextWindow {
|
|
|
|
public readonly int Year;
|
|
public readonly int AvNr;
|
|
private readonly PaymentVar PaymentVar;
|
|
|
|
private ScatterPlot OechslePricePlotScatter;
|
|
private MarkerPlot HighlightedPoint;
|
|
private MarkerPlot PrimaryMarkedPoint;
|
|
private MarkerPlot SecondaryMarkedPoint;
|
|
private Tooltip Tooltip;
|
|
|
|
private int LastHighlightedIndex = -1;
|
|
private int HighlightedIndex = -1;
|
|
private int PrimaryMarkedPointIndex = -1;
|
|
private int SecondaryMarkedPointIndex = -1;
|
|
private bool HoverChanged = false;
|
|
private bool HoverActive = false;
|
|
|
|
private const int MinOechsle = 50;
|
|
private const int MaxOechsle = 140;
|
|
|
|
private List<GraphEntry> GraphEntries = [];
|
|
private GraphEntry? SelectedGraphEntry;
|
|
|
|
public ChartWindow(int year, int avnr) {
|
|
InitializeComponent();
|
|
Year = year;
|
|
AvNr = avnr;
|
|
PaymentVar = Context.PaymentVariants.Find(year, avnr) ?? throw new ArgumentException("PaymentVar not found");
|
|
Title = $"{PaymentVar?.Name} - Lese {year} - Elwig";
|
|
}
|
|
|
|
private void Window_Loaded(object sender, RoutedEventArgs evt) {
|
|
OechslePricePlot.IsEnabled = false;
|
|
}
|
|
|
|
private async Task RefreshGraphList() {
|
|
await Context.PaymentVariants.LoadAsync();
|
|
await RefreshGraphListQuery();
|
|
}
|
|
|
|
private static JsonObject? ParseData(PaymentVar variant) {
|
|
try {
|
|
return BillingData.ParseJson(variant.Data);
|
|
} catch (ArgumentException) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private async Task RefreshGraphListQuery(bool updateSort = false) {
|
|
var data = ParseData(PaymentVar);
|
|
if (data == null) return;
|
|
|
|
var curves = BillingData.GetCurves(data, BillingData.CalculationMode.Elwig);
|
|
|
|
foreach (var (id, curve) in curves) {
|
|
GraphEntries.Add(new GraphEntry(id, curve.Mode, curve.Normal, MinOechsle, MaxOechsle));
|
|
}
|
|
|
|
var attrVariants = Context.DeliveryParts
|
|
.Where(d => d.Year == Year)
|
|
.Select(d => $"{d.SortId}{d.AttrId}")
|
|
.Distinct()
|
|
.ToList()
|
|
.Union(Context.WineVarieties.Select(v => v.SortId))
|
|
.Order()
|
|
.ToList();
|
|
ControlUtils.RenewItemsSource(AppliedInput, attrVariants, g => (g as GraphEntry)?.Id);
|
|
|
|
ControlUtils.RenewItemsSource(GraphList, GraphEntries, g => (g as GraphEntry)?.Id);
|
|
if (GraphEntries.Count == 1) {
|
|
GraphList.SelectedIndex = 0;
|
|
}
|
|
|
|
RefreshInputs();
|
|
}
|
|
|
|
private string ParseContracts(JsonObject auszahlungsSorten, int num) {
|
|
return "";
|
|
}
|
|
|
|
|
|
private void RefreshInputs(bool validate = false) {
|
|
ResetPlot();
|
|
if (!PaymentVar.TestVariant) {
|
|
AddButton.IsEnabled = false;
|
|
CopyButton.IsEnabled = false;
|
|
DeleteButton.IsEnabled = false;
|
|
OechsleInput.IsReadOnly = true;
|
|
PriceInput.IsReadOnly = true;
|
|
} else if (SelectedGraphEntry != null) {
|
|
CopyButton.IsEnabled = true;
|
|
DeleteButton.IsEnabled = true;
|
|
OechsleInput.IsReadOnly = false;
|
|
EnableOptionButtons();
|
|
FillInputs();
|
|
} else {
|
|
CopyButton.IsEnabled = false;
|
|
DeleteButton.IsEnabled = false;
|
|
OechsleInput.IsReadOnly = true;
|
|
DisableOptionButtons();
|
|
}
|
|
GC.Collect();
|
|
}
|
|
|
|
private void FillInputs() {
|
|
GraphNum.Text = SelectedGraphEntry.Id.ToString();
|
|
|
|
InitPlot();
|
|
OechslePricePlot.IsEnabled = true;
|
|
}
|
|
|
|
protected override async Task OnRenewContext() {
|
|
await RefreshGraphList();
|
|
}
|
|
|
|
private void InitPlot() {
|
|
OechslePricePlotScatter = OechslePricePlot.Plot.AddScatter(SelectedGraphEntry.DataGraph.DataX, SelectedGraphEntry.DataGraph.DataY);
|
|
|
|
OechslePricePlot.Configuration.DoubleClickBenchmark = false;
|
|
OechslePricePlotScatter.LineColor = Color.Blue;
|
|
OechslePricePlotScatter.MarkerColor = Color.Blue;
|
|
OechslePricePlotScatter.MarkerSize = 9;
|
|
|
|
//OechslePricePlot.Plot.XAxis.ManualTickSpacing(1);
|
|
OechslePricePlot.Plot.YAxis.ManualTickSpacing(0.1);
|
|
OechslePricePlot.Plot.SetAxisLimits(MinOechsle - 1, MaxOechsle + 1, -0.1, 2);
|
|
|
|
OechslePricePlot.Plot.Layout(padding: 0);
|
|
OechslePricePlot.Plot.XAxis2.Layout(padding: 0);
|
|
OechslePricePlot.Plot.YAxis.Layout(padding: 0);
|
|
OechslePricePlot.Plot.YAxis2.Layout(padding: 0);
|
|
|
|
HighlightedPoint = OechslePricePlot.Plot.AddPoint(0, 0);
|
|
HighlightedPoint.Color = Color.Red;
|
|
HighlightedPoint.MarkerSize = 10;
|
|
HighlightedPoint.MarkerShape = MarkerShape.openCircle;
|
|
HighlightedPoint.IsVisible = false;
|
|
|
|
PrimaryMarkedPoint = OechslePricePlot.Plot.AddPoint(0, 0);
|
|
PrimaryMarkedPoint.Color = Color.Red;
|
|
PrimaryMarkedPoint.MarkerSize = 6;
|
|
PrimaryMarkedPoint.MarkerShape = MarkerShape.filledCircle;
|
|
PrimaryMarkedPoint.IsVisible = false;
|
|
|
|
SecondaryMarkedPoint = OechslePricePlot.Plot.AddPoint(0, 0);
|
|
SecondaryMarkedPoint.Color = Color.Red;
|
|
SecondaryMarkedPoint.MarkerSize = 6;
|
|
SecondaryMarkedPoint.MarkerShape = MarkerShape.filledCircle;
|
|
SecondaryMarkedPoint.IsVisible = false;
|
|
|
|
OechslePricePlot.Refresh();
|
|
|
|
RefreshFreeZoom();
|
|
RefreshGradationLines();
|
|
}
|
|
|
|
private void ResetPlot() {
|
|
PrimaryMarkedPointIndex = -1;
|
|
OechslePricePlot.Plot.Remove(OechslePricePlotScatter);
|
|
OechslePricePlot.Plot.Clear();
|
|
OechslePricePlot.Reset();
|
|
OechslePricePlot.Refresh();
|
|
}
|
|
|
|
private void ChangeMarker(MarkerPlot point, bool visible, double x = 0, double y = 0) {
|
|
point.X = x;
|
|
point.Y = y;
|
|
point.IsVisible = visible;
|
|
}
|
|
|
|
private void FlattenGraph(int begin, int end, double value) {
|
|
SelectedGraphEntry.DataGraph.FlattenGraph(begin, end, value);
|
|
OechslePricePlot.Render();
|
|
}
|
|
|
|
private void LinearIncreaseGraph(int begin, int end, double inc) {
|
|
SelectedGraphEntry.DataGraph.LinearIncreaseGraph(begin, end, inc);
|
|
OechslePricePlot.Render();
|
|
}
|
|
|
|
private void EnableActionButtons() {
|
|
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() {
|
|
OechslePricePlot.Plot.XAxis.SetBoundary(MinOechsle - 1, MaxOechsle + 1);
|
|
OechslePricePlot.Plot.YAxis.SetBoundary(-0.1, 2);
|
|
OechslePricePlot.Plot.XAxis.SetZoomOutLimit(MaxOechsle - MinOechsle + 2);
|
|
OechslePricePlot.Plot.YAxis.SetZoomOutLimit(2.1);
|
|
OechslePricePlot.Plot.SetAxisLimits(MinOechsle - 1, MaxOechsle + 1, -0.1, 2);
|
|
}
|
|
|
|
private void UnlockZoom() {
|
|
OechslePricePlot.Plot.XAxis.SetBoundary();
|
|
OechslePricePlot.Plot.YAxis.SetBoundary();
|
|
OechslePricePlot.Plot.XAxis.SetZoomOutLimit((MaxOechsle - MinOechsle) * 1.5);
|
|
OechslePricePlot.Plot.YAxis.SetZoomOutLimit(3.5);
|
|
}
|
|
|
|
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) {
|
|
ShowGradationLines();
|
|
ShowLegend();
|
|
} else {
|
|
HideGradationLines();
|
|
HideLegend();
|
|
}
|
|
OechslePricePlot.Refresh();
|
|
}
|
|
|
|
private void ShowGradationLines() {
|
|
OechslePricePlot.Plot.AddVerticalLine(68, Color.Red, 2, label: "68 Oechsle (LDW)");
|
|
OechslePricePlot.Plot.AddVerticalLine(73, Color.Orange, 2, label: "73 Oechsle (QUW)");
|
|
OechslePricePlot.Plot.AddVerticalLine(84, Color.Green, 2, label: "84 Oechsle (KAB)");
|
|
}
|
|
|
|
private void HideGradationLines() {
|
|
OechslePricePlot.Plot.Clear(typeof(VLine));
|
|
}
|
|
|
|
private void ShowLegend() {
|
|
OechslePricePlot.Plot.Legend(true, Alignment.UpperRight);
|
|
}
|
|
|
|
private void HideLegend() {
|
|
OechslePricePlot.Plot.Legend(false, Alignment.UpperRight);
|
|
}
|
|
|
|
private void OechsleInput_TextChanged(object sender, TextChangedEventArgs evt) {
|
|
bool success = int.TryParse(OechsleInput.Text, out int oechsle);
|
|
|
|
SecondaryMarkedPointIndex = -1;
|
|
ChangeMarker(SecondaryMarkedPoint, false);
|
|
|
|
if (success) {
|
|
if (oechsle >= MinOechsle && oechsle <= MaxOechsle) {
|
|
PrimaryMarkedPointIndex = oechsle - MinOechsle;
|
|
ChangeMarker(PrimaryMarkedPoint, true, SelectedGraphEntry.DataGraph.DataX[PrimaryMarkedPointIndex], SelectedGraphEntry.DataGraph.DataY[PrimaryMarkedPointIndex]);
|
|
|
|
PriceInput.Text = SelectedGraphEntry.DataGraph.DataY[PrimaryMarkedPointIndex].ToString();
|
|
|
|
EnableActionButtons();
|
|
OechslePricePlot.Render();
|
|
PriceInput.IsReadOnly = false;
|
|
return;
|
|
}
|
|
}
|
|
|
|
PrimaryMarkedPointIndex = -1;
|
|
ChangeMarker(PrimaryMarkedPoint, false);
|
|
DisableActionButtons();
|
|
PriceInput.Text = "";
|
|
OechslePricePlot.Render();
|
|
PriceInput.IsReadOnly = true;
|
|
}
|
|
|
|
private void PriceInput_TextChanged(object sender, RoutedEventArgs evt) {
|
|
if (PrimaryMarkedPointIndex != -1) {
|
|
bool success = Double.TryParse(PriceInput.Text, out double price);
|
|
|
|
if (success) {
|
|
SelectedGraphEntry.DataGraph.DataY[PrimaryMarkedPointIndex] = price;
|
|
PrimaryMarkedPoint.Y = price;
|
|
OechslePricePlot.Refresh();
|
|
}
|
|
}
|
|
}
|
|
|
|
private void LeftFlatButton_Click(object sender, RoutedEventArgs evt) {
|
|
if (PrimaryMarkedPointIndex == -1) {
|
|
return;
|
|
}
|
|
FlattenGraph(0, PrimaryMarkedPointIndex, SelectedGraphEntry.DataGraph.DataY[PrimaryMarkedPointIndex]);
|
|
}
|
|
|
|
private void RightFlatButton_Click(object sender, RoutedEventArgs evt) {
|
|
if (PrimaryMarkedPointIndex == -1) {
|
|
return;
|
|
}
|
|
FlattenGraph(PrimaryMarkedPointIndex, SelectedGraphEntry.DataGraph.DataY.Length - 1, SelectedGraphEntry.DataGraph.DataY[PrimaryMarkedPointIndex]);
|
|
}
|
|
|
|
private void InterpolateButton_Click(object sender, RoutedEventArgs evt) {
|
|
int steps = Math.Abs(PrimaryMarkedPointIndex - SecondaryMarkedPointIndex);
|
|
if (PrimaryMarkedPointIndex == -1 || SecondaryMarkedPointIndex == -1 || steps < 2) {
|
|
return;
|
|
}
|
|
var (lowIndex, highIndex) = PrimaryMarkedPointIndex < SecondaryMarkedPointIndex ? (PrimaryMarkedPointIndex, SecondaryMarkedPointIndex): (SecondaryMarkedPointIndex, PrimaryMarkedPointIndex);
|
|
|
|
double step = (SelectedGraphEntry.DataGraph.DataY[highIndex] - SelectedGraphEntry.DataGraph.DataY[lowIndex]) / steps;
|
|
|
|
for (int i = lowIndex; i < highIndex - 1; i++) {
|
|
SelectedGraphEntry.DataGraph.DataY[i + 1] = Math.Round(SelectedGraphEntry.DataGraph.DataY[i] + step, 4); // TODO richtig runden
|
|
}
|
|
}
|
|
|
|
private void LinearIncreaseButton_Click(object sender, RoutedEventArgs e) {
|
|
if (PrimaryMarkedPointIndex == -1) {
|
|
return;
|
|
}
|
|
double? priceIncrease = Utils.ShowLinearPriceIncreaseDialog();
|
|
if (priceIncrease == null) {
|
|
return;
|
|
}
|
|
LinearIncreaseGraph(PrimaryMarkedPointIndex, SelectedGraphEntry.DataGraph.DataY.Length - 1, priceIncrease.Value);
|
|
}
|
|
|
|
private void OechslePricePlot_MouseDown(object sender, MouseEventArgs e) {
|
|
if (GraphList.SelectedItem == null) {
|
|
return;
|
|
}
|
|
|
|
if (HoverActive) {
|
|
if (PaymentVar.TestVariant && Keyboard.IsKeyDown(Key.LeftCtrl)) {
|
|
if (PrimaryMarkedPointIndex == -1) {
|
|
return;
|
|
}
|
|
SecondaryMarkedPointIndex = HighlightedIndex;
|
|
ChangeMarker(SecondaryMarkedPoint, true, SelectedGraphEntry.DataGraph.DataX[SecondaryMarkedPointIndex], SelectedGraphEntry.DataGraph.DataY[SecondaryMarkedPointIndex]);
|
|
|
|
InterpolateButton.IsEnabled = true;
|
|
|
|
return;
|
|
}
|
|
|
|
PrimaryMarkedPointIndex = HighlightedIndex;
|
|
ChangeMarker(PrimaryMarkedPoint, true, SelectedGraphEntry.DataGraph.DataX[PrimaryMarkedPointIndex], SelectedGraphEntry.DataGraph.DataY[PrimaryMarkedPointIndex]);
|
|
|
|
OechsleInput.Text = SelectedGraphEntry.DataGraph.DataX[HighlightedIndex].ToString();
|
|
PriceInput.Text = SelectedGraphEntry.DataGraph.DataY[HighlightedIndex].ToString();
|
|
|
|
if (PaymentVar.TestVariant) {
|
|
EnableActionButtons();
|
|
}
|
|
} else {
|
|
PrimaryMarkedPointIndex = -1;
|
|
SecondaryMarkedPointIndex = -1;
|
|
ChangeMarker(PrimaryMarkedPoint, false);
|
|
ChangeMarker(SecondaryMarkedPoint, false);
|
|
|
|
OechsleInput.Text = "";
|
|
PriceInput.Text = "";
|
|
|
|
DisableActionButtons();
|
|
}
|
|
}
|
|
|
|
private void OechslePricePlot_MouseMove(object sender, MouseEventArgs e) {
|
|
if (GraphList.SelectedItem == null) {
|
|
return;
|
|
}
|
|
|
|
(double mouseCoordX, double mouseCoordY) = OechslePricePlot.GetMouseCoordinates();
|
|
double xyRatio = OechslePricePlot.Plot.XAxis.Dims.PxPerUnit / OechslePricePlot.Plot.YAxis.Dims.PxPerUnit;
|
|
(double pointX, double pointY, int pointIndex) = OechslePricePlotScatter.GetPointNearest(mouseCoordX, mouseCoordY, xyRatio);
|
|
|
|
(double mousePixelX, double mousePixelY) = OechslePricePlot.GetMousePixel();
|
|
(double pointPixelX, double pointPixelY) = OechslePricePlot.Plot.GetPixel(pointX, pointY);
|
|
|
|
HighlightedIndex = LastHighlightedIndex;
|
|
|
|
if (Math.Abs(mousePixelX - pointPixelX) < 3 && Math.Abs(mousePixelY - pointPixelY) < 3) {
|
|
ChangeMarker(HighlightedPoint, true, pointX, pointY);
|
|
HighlightedPoint.IsVisible = true;
|
|
HoverChanged = true ^ HoverActive;
|
|
HoverActive = true;
|
|
} else {
|
|
ChangeMarker(HighlightedPoint, false);
|
|
HoverChanged= false ^ HoverActive;
|
|
HoverActive= false;
|
|
OechslePricePlot.Plot.Remove(Tooltip);
|
|
OechslePricePlot.Render();
|
|
}
|
|
|
|
if (LastHighlightedIndex != HighlightedIndex || HoverChanged) {
|
|
OechslePricePlot.Plot.Remove(Tooltip);
|
|
if (TooltipInput.IsChecked == true) {
|
|
Tooltip = OechslePricePlot.Plot.AddTooltip($"Oechsle: {pointX:N2}, Preis: {Math.Round(pointY, 4)})", pointX, pointY);
|
|
}
|
|
LastHighlightedIndex = pointIndex;
|
|
HoverChanged = false;
|
|
OechslePricePlot.Render();
|
|
}
|
|
}
|
|
|
|
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, BillingData.CurveMode.Oe, MinOechsle, MaxOechsle);
|
|
GraphEntries.Add(newGraphEntry);
|
|
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);
|
|
GraphList.Items.Refresh();
|
|
GraphList.SelectedItem = newGraphEntry;
|
|
}
|
|
|
|
private void DeleteButton_Click(object sender, RoutedEventArgs e) {
|
|
if (SelectedGraphEntry == null) return;
|
|
|
|
var r = MessageBox.Show(
|
|
$"Soll der Graph {SelectedGraphEntry.Id} (verwendet in folgenden Verträgen: {SelectedGraphEntry.Contracts}) wirklich gelöscht werden?",
|
|
"Graph löschen", MessageBoxButton.YesNo, MessageBoxImage.Warning, MessageBoxResult.No);
|
|
|
|
if (r == MessageBoxResult.Yes) {
|
|
GraphEntries.Remove(SelectedGraphEntry);
|
|
GraphList.Items.Refresh();
|
|
}
|
|
}
|
|
|
|
private void SaveButton_Click(object sender, RoutedEventArgs e) {
|
|
//TODO SAVE
|
|
}
|
|
|
|
private void GraphList_SelectionChanged(object sender, SelectionChangedEventArgs e) {
|
|
SelectedGraphEntry = (GraphEntry)GraphList.SelectedItem;
|
|
RefreshInputs();
|
|
|
|
//var x = OechslePricePlot.Plot.GetPlottables().OfType<ScatterPlot>();
|
|
//MessageBox.Show($"SelectionChanged\nLength: {x.ToList().Count}, Ys: {string.Join(", ", ((ScatterPlot)x.First()).Ys)}");
|
|
}
|
|
|
|
private void PriceInput_LostFocus(object sender, RoutedEventArgs e) {
|
|
|
|
}
|
|
|
|
private void OechsleInput_LostFocus(object sender, RoutedEventArgs e) {
|
|
|
|
}
|
|
|
|
private void GebundenBonus_TextChanged(object sender, TextChangedEventArgs evt) {
|
|
|
|
}
|
|
}
|
|
}
|