using GraphicData.Class;
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace GraphicData.User_Control
{

    public partial class uDataGraph : UserControl
    {
        public uDataGraph()
        {
            InitializeComponent();
        }

        #region Variables
        int DataDurationStart = 0;
        int DataDurationEnd = 0;
        int DataDurationTotal = 0;
        bool ReducedTimeStamps = false;
        DateTime firstTime;
        #endregion

        #region Properties

        public List<object[]> JsonData
        {
            get { return (List<object[]>)GetValue(JsonDataProperty); }
            set { SetValue(JsonDataProperty, value); }
        }

        // Using a DependencyProperty as the backing store for JsonData.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty JsonDataProperty =
            DependencyProperty.Register("JsonData", typeof(List<object[]>), typeof(uDataGraph), new PropertyMetadata(new List<object[]>(), new PropertyChangedCallback(HandleDependancyPropertyChanged)));

        public int IndexTime
        {
            get { return (int)GetValue(IndexTimeProperty); }
            set { SetValue(IndexTimeProperty, value); }
        }

        // Using a DependencyProperty as the backing store for IndexTime.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IndexTimeProperty =
            DependencyProperty.Register("IndexTime", typeof(int), typeof(uDataGraph), new PropertyMetadata(0, new PropertyChangedCallback(HandleDependancyPropertyChanged)));

        public int IndexValue
        {
            get { return (int)GetValue(IndexValueProperty); }
            set { SetValue(IndexValueProperty, value); }
        }

        // Using a DependencyProperty as the backing store for IndexValue.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IndexValueProperty =
            DependencyProperty.Register("IndexValue", typeof(int), typeof(uDataGraph), new PropertyMetadata(1, new PropertyChangedCallback(HandleDependancyPropertyChanged)));

        public int IndexDuration
        {
            get { return (int)GetValue(IndexDurationProperty); }
            set { SetValue(IndexDurationProperty, value); }
        }

        // Using a DependencyProperty as the backing store for IndexDuration.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IndexDurationProperty =
            DependencyProperty.Register("IndexDuration", typeof(int), typeof(uDataGraph), new PropertyMetadata(2, new PropertyChangedCallback(HandleDependancyPropertyChanged)));

        public int WarningThreshold
        {
            get { return (int)GetValue(WarningThresholdProperty); }
            set { SetValue(WarningThresholdProperty, value); }
        }

        // Using a DependencyProperty as the backing store for WarningThreshold.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty WarningThresholdProperty =
            DependencyProperty.Register("WarningThreshold", typeof(int), typeof(uDataGraph), new PropertyMetadata(40, new PropertyChangedCallback(HandleDependancyPropertyChanged)));

        public int ErrorThreshold
        {
            get { return (int)GetValue(ErrorThresholdProperty); }
            set { SetValue(ErrorThresholdProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ErrorThreshold.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ErrorThresholdProperty =
            DependencyProperty.Register("ErrorThreshold", typeof(int), typeof(uDataGraph), new PropertyMetadata(60, new PropertyChangedCallback(HandleDependancyPropertyChanged)));

        public bool IsThresholdActive
        {
            get { return (bool)GetValue(IsThresholdActiveProperty); }
            set { SetValue(IsThresholdActiveProperty, value); }
        }

        // Using a DependencyProperty as the backing store for IsThresholdActive.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsThresholdActiveProperty =
            DependencyProperty.Register("IsThresholdActive", typeof(bool), typeof(uDataGraph), new PropertyMetadata(false, new PropertyChangedCallback(HandleDependancyPropertyChanged)));

        public string GraphicTitle
        {
            get { return (string)GetValue(GraphicTitleProperty); }
            set { SetValue(GraphicTitleProperty, value); }
        }

        // Using a DependencyProperty as the backing store for GraphicTitle.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty GraphicTitleProperty =
            DependencyProperty.Register("GraphicTitle", typeof(string), typeof(uDataGraph), new PropertyMetadata("CPU-Auslastung", new PropertyChangedCallback(HandleDependancyPropertyChanged)));


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

        #endregion

        #region Binding
        
        public void BindingValueChange()
        {
            if (JsonData == null || JsonData.Count <= 0) return;
            GraphicCanvas.Children.Clear();
            TimeStampGrid.Children.Clear();
            TimeStampGrid.ColumnDefinitions.Clear();
            GraphicDataProperty.Clear();
            ReducedTimeStamps = false;
            FillGraphicDataProperty();
            FillVariables();
            CreateDateTitle();
            SetCanvasBorderPadding();
            CreateTimeStamps();
            DrawGraphicData();
        }

        #endregion

        #region GraphicDataProperty

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

        public void FillGraphicDataProperty()
        {

            var reverseData = JsonData.ToList();
            reverseData.Reverse();

            if ((DateTime)reverseData[0][IndexTime] < (DateTime)reverseData[reverseData.Count() - 1][IndexTime])
            {
                foreach (var item in reverseData)
                {
                    cGraphicData data = new cGraphicData();
                    data.DataValue = (double)item[IndexValue];
                    data.DataDuration = int.Parse(item[IndexDuration].ToString());
                    data.DataTime = (DateTime)item[IndexTime];
                    data.DataTime = data.DataTime.AddHours(2);
                    GraphicDataProperty.Add(data);
                }
            }
            else if ((DateTime)JsonData[0][IndexTime] < (DateTime)JsonData[JsonData.Count() - 1][IndexTime])
            {
                foreach (var item in JsonData)
                {
                    cGraphicData data = new cGraphicData();
                    data.DataValue = (double)item[IndexValue];
                    data.DataDuration = int.Parse(item[IndexDuration].ToString());
                    data.DataTime = (DateTime)item[IndexTime];
                    data.DataTime = data.DataTime.AddHours(2);
                    GraphicDataProperty.Add(data);
                }
            }

        }

        #endregion

        #region FillVariables

        public void FillVariables()
        {
            firstTime = GraphicDataProperty.First<cGraphicData>().DataTime;
            DateTime lastTime = GraphicDataProperty.Last<cGraphicData>().DataTime;
            int lastDuration = GraphicDataProperty.Last<cGraphicData>().DataDuration;

            lastTime = lastTime.AddMilliseconds(lastDuration);

            DataDurationStart = firstTime.Hour;
            if(lastTime.Minute != 0)
            {
                lastTime = lastTime.AddMinutes(60 - (lastDuration/60000));
            }
            DataDurationEnd = lastTime.Hour;
            DataDurationTotal = DataDurationEnd - DataDurationStart;
            if (firstTime.Day < lastTime.Day)
            {
                if (DataDurationTotal < 2)
                {
                    DataDurationTotal = (24 - DataDurationStart) + DataDurationEnd;
                }
            }

            if(DataDurationTotal > 10)
            {
                ReducedTimeStamps = true;
                if(DataDurationTotal % 2 == 1)
                {
                    DataDurationTotal += 1;
                }
            }
        }

        #endregion

        #region Create Date Title

        public void CreateDateTitle()
        {
            DateTime dataDate = GraphicDataProperty.First<cGraphicData>().DataTime;
            dataDate = dataDate.Date;
            DateBlock.Text = dataDate.ToString("dddd, dd.MM.yyyy");
            TitleBlock.Text = GraphicTitle;
        }

        #endregion

        #region SetCanvasBorderPadding

        public void SetCanvasBorderPadding()
        {
            double borderWidth = GraphicBorder.ActualWidth;
            double paddingWidth = borderWidth / (((double)DataDurationTotal + 1)* 2.0);
            double borderHeight = GraphicBorder.ActualHeight;
            double paddingHeight = borderHeight / 10.0;
            GraphicBorder.Padding = new Thickness(0);
            GraphicBorder.Padding = new Thickness(paddingWidth, paddingHeight - 4, paddingWidth, paddingHeight - 4);
            GraphicBorder.UpdateLayout();
            CanvasBorder.UpdateLayout();
        }

        #endregion

        #region Time Stamps

        public void CreateTimeStamps()
        {
            SolidColorBrush customBrush = (SolidColorBrush)Application.Current.Resources["color9B9B9B"];

            for (int i = 0; i <= DataDurationTotal; i++)
            {
                ColumnDefinition timeStamp = new ColumnDefinition();
                timeStamp.Width = new GridLength(1, GridUnitType.Star);
                TimeStampGrid.ColumnDefinitions.Add(timeStamp);
                if (ReducedTimeStamps == true && i % 2 == 1)
                {
                    continue;
                }
                TextBlock TimeStampText = new TextBlock()
                {
                    Text = (firstTime.AddHours(i)).Hour.ToString() + ":00",
                    FontFamily = new FontFamily("Calibri"),
                    FontSize = 11,
                    HorizontalAlignment = HorizontalAlignment.Center,
                    VerticalAlignment = VerticalAlignment.Center,
                    Foreground = customBrush,
                    TextAlignment = TextAlignment.Center,
                    TextTrimming = TextTrimming.None,
                };
                if(DataDurationTotal > 20)
                {
                    TimeStampText.Margin = new Thickness(-2,0,-2,0);
                }
                Grid.SetColumn(TimeStampText, i);
                TimeStampGrid.Children.Add(TimeStampText);
            }
            
        }

        #endregion

        #region Draw Data

        public double CalculateSetLeft(DateTime dataTime)
        {
            double setLeft = (double)dataTime.Hour + ((double)dataTime.Minute / 60.0);
            setLeft = (setLeft - (double)DataDurationStart) * ((double)CanvasBorder.ActualWidth / ((double)DataDurationTotal));
            return setLeft;
        }

        public void DrawGraphicData()
        {
            double setLeft = 0.0;
            double setFactor = 0.0;

            setFactor = (GraphicCanvas.ActualHeight - 8.0) / 100.0;

            foreach (var data in GraphicDataProperty)
            {

                DateTime dataTime = data.DataTime;
                double dataValue = data.DataValue;
                int dataDuration = data.DataDuration;

                Border border = new Border
                {
                    Height = 8.0,
                    Width = (double)dataDuration * ((double)CanvasBorder.ActualWidth / ((double)DataDurationTotal)) / 3600000.0,
                    CornerRadius = new CornerRadius(4.0),
                    ToolTip = dataTime.ToShortTimeString() + " - " + dataTime.AddMilliseconds(dataDuration).ToShortTimeString() + " | " + (int)dataValue + "%"
                };
                if(border.Width < 8)
                {
                    border.Width = 8;
                }
                if(IsThresholdActive == false)
                {
                    border.Background = new SolidColorBrush(Color.FromRgb(0, 157, 221));
                }
                else if(dataValue >= ErrorThreshold)
                {
                    border.Background = new SolidColorBrush(Color.FromRgb(206, 61, 54));
                }
                else if(dataValue >= WarningThreshold)
                {
                    border.Background = new SolidColorBrush(Color.FromRgb(251, 157, 40));
                }
                else
                {
                    border.Background = new SolidColorBrush(Color.FromRgb(117, 177, 89));
                }
                setLeft = CalculateSetLeft(dataTime);
                Canvas.SetBottom(border, dataValue * setFactor);
                Canvas.SetLeft(border, setLeft);
                GraphicCanvas.Children.Add(border);

            }

        }

        #endregion

        #region Close Button

        private void Border_MouseEnter(object sender, MouseEventArgs e)
        {
            SolidColorBrush customBrush = (SolidColorBrush)Application.Current.Resources["color009DDD"];
            CloseButton.PrimaryIconColor = customBrush;
        }

        private void Border_MouseLeave(object sender, MouseEventArgs e)
        {
            SolidColorBrush customBrush = (SolidColorBrush)Application.Current.Resources["color9B9B9B"];
            CloseButton.PrimaryIconColor = customBrush;
        }

        private void Border_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            Close_Window();
        }

        private void Border_TouchDown(object sender, TouchEventArgs e)
        {
            Close_Window();
        }

        private Window GetMainWindow()
        {
            DependencyObject parent = VisualTreeHelper.GetParent(this);

            while (parent != null && !(parent is Window))
            {
                parent = VisualTreeHelper.GetParent(parent);
            }

            return parent as Window;
        }

        private void Close_Window()
        {

            Window mainWindow = Window.GetWindow(this);

            if (mainWindow != null)
            {
                mainWindow.Close();
            }

        }

        #endregion

    }
}
