using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
using FasdDesktopUi.Basics.Models;
using static C4IT.Logging.cLogManager;

namespace FasdDesktopUi.Basics.UserControls
{

    public partial class DynamicChart : UserControl
    {

        #region Variables

        int DataDurationStart;
        int DataDurationEnd;
        int DataDurationTotal;
        int timeSkip;

        private double PaddingTop;
        private double PaddingBottom;
        private double PaddingLeft;
        private double PaddingRight;
        private double DataHeight;
        private double DataWidth;

        private LinearTransformation xCoordinate;
        private LinearTransformation yCoordinate;

        Size sizeY;
        Size sizeX;

        #endregion

        #region Properties

        #region ChartData

        public cChartModel ChartData
        {
            get { return (cChartModel)GetValue(ChartDataProperty); }
            set { SetValue(ChartDataProperty, value); }
        }

        public static readonly DependencyProperty ChartDataProperty =
            DependencyProperty.Register("ChartData", typeof(cChartModel), typeof(DynamicChart), new PropertyMetadata(null, new PropertyChangedCallback(HandleDependancyPropertyChanged)));


        private static void HandleDependancyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (!(d is DynamicChart me)) return;
            me.BindingValueChange();
        }

        #endregion

        #region IsCloseButtonVisible
        public bool IsCloseButtonVisible
        {
            get { return (bool)GetValue(IsCloseButtonVisibleProperty); }
            set { SetValue(IsCloseButtonVisibleProperty, value); }
        }

        public static readonly DependencyProperty IsCloseButtonVisibleProperty =
            DependencyProperty.Register("IsCloseButtonVisible", typeof(bool), typeof(DynamicChart), new PropertyMetadata(false));


        #endregion

        #region IsBarChart



        public bool isBarChart
        {
            get { return (bool)GetValue(isBarChartProperty); }
            set { SetValue(isBarChartProperty, value); }
        }

        // Using a DependencyProperty as the backing store for isBarChart.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty isBarChartProperty =
            DependencyProperty.Register("isBarChart", typeof(bool), typeof(DynamicChart), new PropertyMetadata(true, new PropertyChangedCallback(HandleBarChartPropertyChanged)));

        private static void HandleBarChartPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (!(d is DynamicChart me)) return;
            me.ChartTypeChange();
        }

        #endregion

        #region GraphicDataProperty

        public List<cChartValue> GraphicDataProperty { get; set; } = new List<cChartValue>();

        #endregion

        #endregion

        #region Internal Classes

        public class cChartValue
        {
            public DateTime Time { get; set; }
            public double Value { get; set; }
            public int Duration { get; set; }

        }

        public class LinearTransformation
        {

            private double x0;
            private double x1;
            private double z0;
            private double z1;
            private double a;
            private double c;

            public LinearTransformation(double x0, double x1, double z0, double z1)
            {
                this.x0 = x0;
                this.x1 = x1;
                this.z0 = z0;
                this.z1 = z1;

                a = (x1 - x0) / (z1 - z0);
                c = x0 - (a * z0);

            }

            public double GetCoordinate(double x)
            {
                return a * x + c;
            }

        }


        #endregion

        public Action CloseButtonClickedAction { get; set; }

        #region Restructure

        public void BindingValueChange()
        {

            try
            {
                ClearControl();

                if (ChartData == null)
                {
                    return;
                }

                if (this.ActualHeight == 0 || this.ActualWidth == 0)
                {
                    return;
                }

                SetGraphicDataProperty();
                SetTitle();
                SetDate();
                SetTime();
                SetCanvas();
                SetScale();
                SetTimeStamps();
                SetLines();
                if (isBarChart == true)
                {
                    SetValuesBarChart();
                }
                else
                {
                    SetValuesLineGraph();
                }


            }
            catch (Exception E)
            {
                LogException(E);
            }

        }

        public void ChartTypeChange()
        {

            try
            {

                GraphicCanvas.Children.Clear();

                if (isBarChart == true)
                {
                    BarChartButton.Visibility = Visibility.Collapsed;
                    LineGraphButton.Visibility = Visibility.Visible;
                    SetScale();
                    SetTimeStamps();
                    SetLines();
                    SetValuesBarChart();
                }
                else
                {
                    BarChartButton.Visibility = Visibility.Visible;
                    LineGraphButton.Visibility = Visibility.Collapsed;
                    SetScale();
                    SetTimeStamps();
                    SetLines();
                    SetValuesLineGraph();
                }

            }
            catch (Exception E)
            {
                LogException(E);
            }

        }

        #endregion

        #region Methods

        public void SetGraphicDataProperty()
        {
            try
            {
                if (ChartData?.Data == null) return;
                var reverseData = ChartData.Data.ToList();
                reverseData.Reverse();

                if ((DateTime)reverseData[0][ChartData.TimeIndex] < (DateTime)reverseData[reverseData.Count() - 1][ChartData.TimeIndex])
                {
                    foreach (var item in reverseData)
                    {
                        if (item[ChartData.ValueIndex] == null)
                        {
                            continue;
                        }
                        cChartValue data = new cChartValue();
                        data.Value = (double)Convert.ChangeType(item[ChartData.ValueIndex], typeof(double));
                        if (ChartData.DurationIndex > -1)
                            data.Duration = int.Parse(item[ChartData.DurationIndex].ToString());
                        data.Time = (DateTime)item[ChartData.TimeIndex];
                        data.Time = data.Time.ToLocalTime();
                        GraphicDataProperty.Add(data);
                    }
                }
                else if ((DateTime)ChartData.Data[0][ChartData.TimeIndex] < (DateTime)ChartData.Data[ChartData.Data.Count() - 1][ChartData.TimeIndex] || ChartData.Data.Count == 1)
                {
                    foreach (var item in ChartData.Data)
                    {
                        if (item[ChartData.ValueIndex] == null)
                        {
                            continue;
                        }
                        cChartValue data = new cChartValue();
                        data.Value = (double)Convert.ChangeType(item[ChartData.ValueIndex], typeof(double));
                        if (ChartData.DurationIndex > -1)
                            data.Duration = int.Parse(item[ChartData.DurationIndex].ToString());
                        data.Time = (DateTime)item[ChartData.TimeIndex];
                        data.Time = data.Time.ToLocalTime();
                        GraphicDataProperty.Add(data);
                    }
                }
            }
            catch (Exception E)
            {
                LogException(E);
            }
        }

        public void ClearControl()
        {
            TitleBlock.Text = string.Empty;
            DateBlock.Text = string.Empty;
            GraphicCanvas.Children.Clear();
            GraphicDataProperty.Clear();
        }

        public void SetTitle()
        {
            TitleBlock.Text = ChartData.ChartTitle;
        }

        public void SetDate()
        {
            try
            {
                DateTime dataDate = GraphicDataProperty.First<cChartValue>().Time;
                dataDate = dataDate.Date;
                DateBlock.Text = $"{dataDate.ToString("dddd", new CultureInfo(cFasdCockpitConfig.Instance.SelectedLanguage))}, {dataDate.ToString("d", new CultureInfo(cFasdCockpitConfig.Instance.SelectedLanguage))}";
            }
            catch (Exception E)
            {
                LogException(E);
            }
        }

        public void SetTime()
        {

            try
            {

                DateTime firstTime = GraphicDataProperty.First<cChartValue>().Time;
                DateTime lastTime = GraphicDataProperty.Last<cChartValue>().Time;
                int lastDuration = GraphicDataProperty.Last<cChartValue>().Duration;

                lastTime = lastTime.AddMilliseconds(lastDuration);

                DataDurationStart = firstTime.Hour;
                DataDurationEnd = lastTime.Hour + 1;

                TimeSpan totalDuration = lastTime - firstTime;

                DataDurationTotal = DataDurationEnd - DataDurationStart + (24 * totalDuration.Days);
                DataDurationTotal = GetNextLogicalTotalDuration(DataDurationTotal);

                // temp fix
                DataDurationEnd = DataDurationStart + DataDurationTotal;

                timeSkip = (int)Math.Ceiling(DataDurationTotal / 5.0);
            }
            catch (Exception E)
            {
                LogException(E);
            }


            // this is a temporary helper method to keep the structure of the current state of the charts.
            // however the logic should be rethought and maybe finally replaced for a even more dynamic approach
            int GetNextLogicalTotalDuration(int totalDuration)
            {
                if (totalDuration < 0)
                    return 24 + totalDuration;
                else if (totalDuration <= 3)
                    return 3;
                else if (totalDuration <= 5)
                    return 5;
                else if (totalDuration <= 8)
                    return 8;
                else if (totalDuration <= 10)
                    return 10;
                else if (totalDuration <= 12)
                    return 12;
                else if (totalDuration <= 15)
                    return 15;
                else if (totalDuration <= 18)
                    return 18;
                else if (totalDuration <= 20)
                    return 20;
                else return 24;
            }
        }

        public void SetCanvas()
        {

            try
            {

                string textY;
                if (ChartData.UnitFormat.Contains("{0}"))
                {
                    textY = String.Format(ChartData.UnitFormat, ChartData.MaxValue.ToString());
                }
                else
                {
                    textY = ChartData.MaxValue.ToString() + ChartData.UnitFormat;
                }
                sizeY = GetStringSize(textY, 11, new System.Windows.Media.FontFamily("Calibri"));

                string textX = "09:00";
                sizeX = GetStringSize(textX, 11, new System.Windows.Media.FontFamily("Calibri"));

                PaddingTop = 5 + (sizeY.Height / 2);
                PaddingRight = 10 + sizeY.Width;
                PaddingBottom = 10 + sizeX.Height + PaddingTop;
                PaddingLeft = 10 + (sizeX.Width / 2);

                DataHeight = GraphicCanvas.ActualHeight - PaddingTop - PaddingBottom;
                DataWidth = GraphicCanvas.ActualWidth - PaddingLeft - PaddingRight;

                yCoordinate = new LinearTransformation(PaddingTop + DataHeight, PaddingTop, ChartData.MinValue, ChartData.MaxValue);
                xCoordinate = new LinearTransformation(PaddingLeft, PaddingLeft + DataWidth, DataDurationStart * 3600000, (DataDurationStart + DataDurationTotal) * 3600000);

            }
            catch (Exception E)
            {

                LogException(E);
            }


        }

        public void SetScale()
        {
            try
            {

                int Minimum = 0;

                if ((ChartData.MinValue / ChartData.StepLengthScale) % 1 != 0)
                {
                    int Multiplikator = ChartData.MinValue / ChartData.StepLengthScale;
                    Multiplikator++;
                    Minimum = Multiplikator * ChartData.MinValue;
                }
                else
                {
                    Minimum = ChartData.MinValue;
                }

                for (int i = Minimum; i <= ChartData.MaxValue; i += ChartData.StepLengthScale)
                {
                    string text;

                    if (ChartData.UnitFormat.Contains("{0}"))
                    {
                        text = String.Format(ChartData.UnitFormat, i.ToString());
                    }
                    else
                    {
                        text = i.ToString() + ChartData.UnitFormat;
                    }

                    TextBlock textBlock = new TextBlock()
                    {
                        Text = text,
                        FontFamily = new FontFamily("Calibri"),
                        FontSize = 11,
                    };
                    textBlock.SetResourceReference(TextBlock.ForegroundProperty, "Color.Menu.Icon");
                    Canvas.SetTop(textBlock, yCoordinate.GetCoordinate(i) - (sizeY.Height / 2));
                    Canvas.SetLeft(textBlock, PaddingLeft + DataWidth + 1);
                    GraphicCanvas.Children.Add(textBlock);

                }


            }
            catch (Exception E)
            {

                LogException(E);
            }
        }
        public void SetTimeStamps()
        {
            try
            {

                for (int i = DataDurationStart; i <= DataDurationEnd; i++)
                {

                    if ((DataDurationStart - i) % timeSkip != 0)
                    {
                        continue;
                    }

                    string timeStamp;
                    int timeStampTime = i;
                    if (timeStampTime > 24)
                    {
                        timeStampTime -= 24;
                    }

                    if (i < 10)
                    {
                        timeStamp = "0" + timeStampTime.ToString() + ":00";
                    }
                    else
                    {
                        timeStamp = timeStampTime.ToString() + ":00";
                    }

                    TextBlock textBlock = new TextBlock()
                    {
                        Text = timeStamp,
                        FontFamily = new FontFamily("Calibri"),
                        FontSize = 11,
                    };

                    textBlock.SetResourceReference(TextBlock.ForegroundProperty, "Color.Menu.Icon");

                    Canvas.SetTop(textBlock, PaddingTop + DataHeight + (sizeX.Height / 2));
                    Canvas.SetLeft(textBlock, (xCoordinate.GetCoordinate(i * 3600000)) - (sizeX.Width / 2));
                    GraphicCanvas.Children.Add(textBlock);

                }
            }
            catch (Exception E)
            {

                LogException(E);
            }
        }

        public void SetLines()
        {
            try
            {
                int Minimum = 0;

                if ((ChartData.MinValue / ChartData.StepLengthLine) % 1 != 0)
                {
                    int Multiplikator = ChartData.MinValue / ChartData.StepLengthLine;
                    Multiplikator++;
                    Minimum = Multiplikator * ChartData.MinValue;
                }
                else
                {
                    Minimum = ChartData.MinValue;
                }

                for (int i = Minimum; i <= ChartData.MaxValue; i += ChartData.StepLengthLine)
                {

                    Point point1 = new Point(PaddingLeft, yCoordinate.GetCoordinate(i));
                    Point point2 = new Point(PaddingLeft + DataWidth, yCoordinate.GetCoordinate(i));

                    Line dottedLine = new Line();

                    dottedLine.SetResourceReference(Line.StrokeProperty, "HorizontalLineColor");
                    dottedLine.StrokeThickness = 1;

                    dottedLine.StrokeDashArray = new DoubleCollection { 5, 5 };

                    dottedLine.X1 = point1.X;
                    dottedLine.Y1 = point1.Y;
                    dottedLine.X2 = point2.X;
                    dottedLine.Y2 = point2.Y;

                    GraphicCanvas.Children.Add(dottedLine);

                }

            }
            catch (Exception E)
            {

                LogException(E);
            }
        }

        public void SetValuesLineGraph()
        {
            try
            {
                double oneDayMs = TimeSpan.FromDays(1).TotalMilliseconds;
                double chartStartMs = 0;
                double chartEndMs = oneDayMs;

                foreach (var data in GraphicDataProperty)
                {
                    DateTime dataTime = data.Time;
                    double dataValue = data.Value;
                    int dataDuration = data.Duration;

                    double startMs = dataTime.TimeOfDay.TotalMilliseconds;
                    double endMs = startMs + dataDuration;

                    if (startMs >= chartEndMs || endMs <= chartStartMs)
                        continue;

                    if (startMs < chartStartMs)
                        startMs = chartStartMs;

                    if (endMs > chartEndMs)
                        endMs = chartEndMs;

                    double xLeft = xCoordinate.GetCoordinate(startMs);
                    double xRight = xCoordinate.GetCoordinate(endMs);

                    double y = 0;

                    if (dataValue > ChartData.MaxValue)
                    {
                        y = yCoordinate.GetCoordinate(ChartData.MaxValue) - 5;
                    }
                    else if (dataValue < ChartData.MinValue)
                    {
                        y = yCoordinate.GetCoordinate(ChartData.MinValue) + 5;
                    }
                    else
                    {
                        y = yCoordinate.GetCoordinate(dataValue);
                    }
                     
                    
                    Border border = new Border()
                    {
                        Height = 6.0,
                        Width = xRight - xLeft,    
                        CornerRadius = new CornerRadius(4.0),
                        ToolTip = dataTime.ToShortTimeString() + " - " + dataTime.AddMilliseconds(dataDuration).ToShortTimeString() + " | " + dataValue,
                    };
                    if (border.Width < 6)
                    {
                        border.Width = 6;   
                    }                                      
                    if (ChartData.IsThresholdActive == false)
                    {
                        border.Background = new SolidColorBrush(Color.FromRgb(0, 157, 221));
                    }
                    else
                    {
                        if (ChartData.IsDirectionUp == false)
                        {
                            if (dataValue <= ChartData.ErrorThreshold)
                            {
                                border.Background = new SolidColorBrush(Color.FromRgb(206, 61, 54));
                            }
                            else if (dataValue <= ChartData.WarningThreshold)
                            {
                                border.Background = new SolidColorBrush(Color.FromRgb(251, 157, 40));
                            }
                            else
                            {
                                border.Background = new SolidColorBrush(Color.FromRgb(117, 177, 89));
                            }
                        }
                        else
                        {
                            if (dataValue >= ChartData.ErrorThreshold)
                            {
                                border.Background = new SolidColorBrush(Color.FromRgb(206, 61, 54));
                            }
                            else if (dataValue >= ChartData.WarningThreshold)
                            {
                                border.Background = new SolidColorBrush(Color.FromRgb(251, 157, 40));
                            }
                            else
                            {
                                border.Background = new SolidColorBrush(Color.FromRgb(117, 177, 89));
                            }
                        }
                    }

                    Canvas.SetTop(border, y - (border.Height / 2));
                    Canvas.SetLeft(border, xLeft);
                    GraphicCanvas.Children.Add(border);

                }
            }
            catch (Exception E)
            {
                LogException(E);
            }
        }

        public void SetValuesBarChart()
        {
            try
            {
                double oneDayMs = TimeSpan.FromDays(1).TotalMilliseconds;
                double chartStartMs = 0;
                double chartEndMs = oneDayMs;

                foreach (var data in GraphicDataProperty)
                {
                    DateTime dataTime = data.Time;
                    double dataValue = data.Value;
                    int dataDuration = data.Duration;

                    double startMs = dataTime.TimeOfDay.TotalMilliseconds;
                    double endMs = startMs + dataDuration;

                    if (startMs >= chartEndMs || endMs <= chartStartMs)
                        continue;

                    if (startMs < chartStartMs)
                        startMs = chartStartMs;

                    if (endMs > chartEndMs)
                        endMs = chartEndMs;

                    double xLeft = xCoordinate.GetCoordinate(startMs);
                    double xRight = xCoordinate.GetCoordinate(endMs);

                    double y = 0;

                    if (dataValue > ChartData.MaxValue)
                    {
                        y = yCoordinate.GetCoordinate(ChartData.MaxValue) - 5;
                    }
                    else if (dataValue < ChartData.MinValue)
                    {
                        y = yCoordinate.GetCoordinate(ChartData.MinValue) + 5;
                    }
                    else
                    {
                        y = yCoordinate.GetCoordinate(dataValue);
                    }

                    Border border = new Border()
                    {
                        Height = yCoordinate.GetCoordinate(0) - y,
                        Width = xRight - xLeft,
                        CornerRadius = new CornerRadius(2, 2, 0, 0),
                        ToolTip = dataTime.ToShortTimeString() + " - " + dataTime.AddMilliseconds(dataDuration).ToShortTimeString() + " | " + dataValue,
                    };

                    if (border.Width < 6)
                    {
                        border.Width = 6;
                    }

                    if (ChartData.IsThresholdActive == false)
                    {
                        border.Background = new SolidColorBrush(Color.FromRgb(0, 157, 221));
                    }
                    else
                    {
                        if (ChartData.IsDirectionUp == false)
                        {

                            if (dataValue <= ChartData.ErrorThreshold)
                            {
                                border.Background = new SolidColorBrush(Color.FromRgb(206, 61, 54));
                            }
                            else if (dataValue <= ChartData.WarningThreshold)
                            {
                                border.Background = new SolidColorBrush(Color.FromRgb(251, 157, 40));
                            }
                            else
                            {
                                border.Background = new SolidColorBrush(Color.FromRgb(117, 177, 89));
                            }
                        }
                        else
                        {
                            if (dataValue >= ChartData.ErrorThreshold)
                            {
                                border.Background = new SolidColorBrush(Color.FromRgb(206, 61, 54));
                            }
                            else if (dataValue >= ChartData.WarningThreshold)
                            {
                                border.Background = new SolidColorBrush(Color.FromRgb(251, 157, 40));
                            }
                            else
                            {
                                border.Background = new SolidColorBrush(Color.FromRgb(117, 177, 89));
                            }
                        }
                    }

                    Canvas.SetTop(border, y);
                    Canvas.SetLeft(border, xLeft);
                    GraphicCanvas.Children.Add(border);

                }
            }
            catch (Exception E)
            {
                LogException(E);
            }
        }

        #endregion

        public DynamicChart()
        {
            InitializeComponent();
        }

        #region CloseButton_Click

        private void CloseButton_Click()
        {
            if (CloseButtonClickedAction != null)
                CloseButtonClickedAction.Invoke();
        }

        private void CloseButton_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            CloseButton_Click();
        }

        private void CloseButton_TouchDown(object sender, TouchEventArgs e)
        {
            CloseButton_Click();
        }

        #endregion

        #region ChangeChartType_Click

        private void ChangeChartType_Click(object sender)
        {
            if (!(sender is FrameworkElement senderElement))
            {
                return;
            }

            if (senderElement.Name == "BarChartButton")
            {
                isBarChart = true;
            }

            if (senderElement.Name == "LineGraphButton")
            {
                isBarChart = false;
            }

        }

        private void ChangeChartType_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            ChangeChartType_Click(sender);
        }

        private void ChangeChartType_TouchDown(object sender, TouchEventArgs e)
        {
            ChangeChartType_Click(sender);
        }

        #endregion

        private void UserControl_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            BindingValueChange();
        }

        public static Size GetStringSize(string text, double fontSize, FontFamily fontFamily)
        {

            CultureInfo cultureInfo = CultureInfo.GetCultureInfo("de-DE");
            FlowDirection flowDirection = FlowDirection.LeftToRight;

#pragma warning disable CS0618 // Type or member is obsolete
            FormattedText formattedText = new FormattedText(
                text,
                cultureInfo,
                flowDirection,
                new Typeface(fontFamily.Source),
                fontSize,
                Brushes.Black
            );
#pragma warning restore CS0618 // Type or member is obsolete

            return new Size(formattedText.Width, formattedText.Height);

        }

    }
}
