using ScottPlot;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json.Nodes;

namespace Elwig.Helpers.Billing {
    public class Graph : ICloneable {

        public string Type { get; set; }
        public int Num { get; set; }
        private int MinX { get; set; }
        private int MaxX { get; set; }
        public string Contracts { get; set; }
        public double[] DataX { get; set; }
        public double[] DataY { get; set; }

        public Graph(int num, int minX, int maxX) {
            Type = "oe";
            Num = num;
            Contracts = "";
            MinX = minX;
            MaxX = maxX;

            DataX = DataGen.Range(MinX, MaxX + 1);
            DataY = DataGen.Zeros(MaxX - MinX + 1);
        }

        public Graph(string type, int num, JsonObject graphData, string contracts, int minX, int maxX) {
            Type = type;
            Num = num;
            Contracts =  contracts;
            MinX = minX;
            MaxX = maxX;

            DataX = DataGen.Range(MinX, MaxX + 1);
            DataY = DataGen.Zeros(MaxX - MinX + 1);
            ParseGraphData(graphData);
        }

        public Graph(string type, int num, int minX, int maxX, string contracts, double[] dataX, double[] dataY) {
            Type = type;
            Num = num;
            MinX = minX;
            MaxX = maxX;
            Contracts = contracts;
            DataX = dataX;
            DataY = dataY;
        }

        private void ParseGraphData(JsonObject graphData) {
            var GraphPoints = graphData.ToDictionary(p => int.Parse(p.Key[..^2]), p => (double)p.Value?.AsValue());

            if (GraphPoints.Keys.Count < 1) {
                return;
            }

            var minKey = GraphPoints.Keys.Order().First();
            var maxKey = GraphPoints.Keys.OrderDescending().First();

            if (!GraphPoints.ContainsKey(MinX)) {
                GraphPoints.Add(MinX, GraphPoints.GetValueOrDefault(minKey));
            }
            if (!GraphPoints.ContainsKey(MaxX)) {
                GraphPoints.Add(MaxX, GraphPoints.GetValueOrDefault(maxKey));
            }

            var keys = GraphPoints.Keys.Order().ToArray();

            for (int i = 0; i < keys.Length; i++) {
                double point1Value = GraphPoints[keys[i]];
                if (i + 1 < keys.Length) {
                    double point2Value = GraphPoints[keys[i + 1]];
                    if (point1Value == point2Value) {
                        for (int j = keys[i] - MinX; j < keys[i + 1] - MinX; j++) {
                            DataY[j] = point1Value;
                        }
                    } else {
                        int steps = Math.Abs(keys[i + 1] - keys[i]);
                        double step = (point2Value - point1Value) / steps;

                        DataY[keys[i] - MinX] = point1Value;
                        DataY[keys[i + 1] - MinX] = point2Value;

                        for (int j = keys[i] - MinX; j < keys[i + 1] - MinX - 1; j++) {
                            DataY[j + 1] = Math.Round(DataY[j] + step, 4); // TODO richtig runden
                        }
                    }
                }
                else {
                    for (int j = keys[i] - MinX; j < DataX.Length; j++) {
                        DataY[j] = point1Value;
                    }
                }
            }
        }

        public JsonObject ToJson() {
            JsonObject graph = new();

            if (DataY[0] != DataY[1]) {
                graph.Add(new KeyValuePair<string, JsonNode?>(DataX[0] + Type.ToLower(), Math.Round(DataY[0], 4)));
            }
            for (int i = 1; i < DataX.Length - 1; i++) {
                if (Math.Round(DataY[i] - DataY[i - 1], 4) != Math.Round(DataY[i + 1] - DataY[i], 4)) {
                    graph.Add(new KeyValuePair<string, JsonNode?>(DataX[i] + Type.ToLower(), Math.Round(DataY[i], 4)));
                }
            }
            if (DataY[^1] != DataY[^2]) {
                graph.Add(new KeyValuePair<string, JsonNode?>(DataX[^1] + Type.ToLower(), Math.Round(DataY[^1], 4)));
            }
            return graph;
        }

        public object Clone() {
            return new Graph(Type, Num, MinX, MaxX, Contracts, (double[])DataX.Clone(), (double[])DataY.Clone());
        }
    }
}