using OxyPlot;
using OxyPlot.Axes;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;

namespace VSview
{
    public class Graph : BaseViewModel
    {
        #region Private membres
        //Object for locking
        private object _lock = new object();

        //Rereference to MainViewModel
        private MainViewModel ViewModel;

        //Graph thread
        private Thread GraphThread;

        #endregion

        #region Public properties

        /// <summary>
        /// Collection of datapoints containing detector leakrate
        /// </summary>
        public List<DataPoint> Leakrate { get; set; }

        /// <summary>
        /// Determines if leakrate should be visible in graph
        /// </summary>
        public bool LeakVisibility { get; set; } = true;

        /// <summary>
        /// Collection of datapoints containing detector testport pressure
        /// </summary>
        public List<DataPoint> Testpressure { get; set; }

        /// <summary>
        /// Determines if testport pressure should be visible in graph
        /// </summary>
        public bool TPVisibility { get; set; } = true;

        /// <summary>
        /// Collection of datapoints containing Turck Analog 1 values
        /// </summary>
        public List<DataPoint> Analog1 { get; set; }

        /// <summary>
        /// Determines if analog 1 should be visible in graph
        /// </summary>
        public bool Analog1Visiblity { get; set; }

        /// <summary>
        /// Collection of datapoints containing Turck Analog 1 values
        /// </summary>
        public List<DataPoint> Analog2 { get; set; }

        /// <summary>
        /// Determines if analog 1 should be visible in graph
        /// </summary>
        public bool Analog2Visiblity { get; set; }

        /// <summary>
        /// Collection of datapoints containing Turck Analog 1 values
        /// </summary>
        public List<DataPoint> Analog3 { get; set; }

        /// <summary>
        /// Determines if analog 1 should be visible in graph
        /// </summary>
        public bool Analog3Visiblity { get; set; }

        /// <summary>
        /// Collection of datapoints containing Turck Analog 1 values
        /// </summary>
        public List<DataPoint> Analog4 { get; set; }

        /// <summary>
        /// Determines if analog 1 should be visible in graph
        /// </summary>
        public bool Analog4Visiblity { get; set; }

        /// <summary>
        /// Time axis maximum
        /// </summary>
        public double XaxisMax { get; set; }

        /// <summary>
        /// Time axis minimum
        /// </summary>
        public double XaxisMin { get; set; }

        /// <summary>
        /// Graph label formatter
        /// </summary>
        public Func<double, string> Formatter { get; set; }

        #endregion

        #region Constructor
        /// <summary>
        /// Default constructor
        /// </summary>
        public Graph() { }

        /// <summary>
        /// Main constructor - passing reference to MainViewModel
        /// </summary>
        /// <param name="viewModel"></param>
        public Graph(MainViewModel viewModel)
        {
            ViewModel = viewModel;

            SetupGraph();
        }
        #endregion

        #region Methods
        /// <summary>
        /// Formater for the Y axes
        /// </summary>
        /// <param name="d"></param>
        /// <returns></returns>
        public static string Valueformatter(double d)
        {
            if (d < 1)
            {
                return d.ToString("0.0#E0");
            }
            else
            {
                return String.Format("{0}", d);
            }
        }

        /// <summary>
        /// Method for starting graph
        /// </summary>
        private void SetupGraph()
        {
            try
            {
                Leakrate = new List<DataPoint>();
                Testpressure = new List<DataPoint>();
                Analog1 = new List<DataPoint>();
                Analog2 = new List<DataPoint>();
                Analog3 = new List<DataPoint>();
                Analog4 = new List<DataPoint>();

                XaxisMax = DateTimeAxis.ToDouble(DateTime.Now.AddSeconds(60));
                XaxisMin = DateTimeAxis.ToDouble(DateTime.Now);

                Formatter = Valueformatter;

                GraphThread = new Thread(UpdatePlot)
                {
                    IsBackground = true
                };
                GraphThread.Start();
            }
            catch (Exception ex) { MessageBox.Show("Error during creating graph. \nReason: " + ex.Message, "Graph error", MessageBoxButton.OK, MessageBoxImage.Warning); }

        }

        /// <summary>
        /// Method for adding points to graph series
        /// </summary>
        private void AddPoints()
        {
            try
            {
                //If detector is connected
                if (ViewModel.Detector.Settings.IsConnected)
                {
                    //Add leakrate value
                    Leakrate.Add(new DataPoint(DateTimeAxis.ToDouble(DateTime.Now), ViewModel.Detector.Ionchamber.Leakrate));

                    //Add testport pressure value
                    Testpressure.Add(new DataPoint(DateTimeAxis.ToDouble(DateTime.Now), ViewModel.Detector.Ionchamber.TPpressure));
                }

                //If Turck module is connected
                if (ViewModel.ControlModule.IsConnected)
                {
                    //If Turck module is Turck - S2 - 4AI (4 analog inputs)
                    if (SettingViewModel.ControlIndex == 4)
                    {
                        //Add analog 1 value
                        Analog1.Add(new DataPoint(DateTimeAxis.ToDouble(DateTime.Now), double.Parse(ViewModel.ControlModule.Analogs["Analog1"].Result)));

                        //Add analog 2 value
                        Analog2.Add(new DataPoint(DateTimeAxis.ToDouble(DateTime.Now), double.Parse(ViewModel.ControlModule.Analogs["Analog2"].Result)));

                        //Add analog 3 value
                        Analog3.Add(new DataPoint(DateTimeAxis.ToDouble(DateTime.Now), double.Parse(ViewModel.ControlModule.Analogs["Analog3"].Result)));

                        //Add analog 4 value
                        Analog4.Add(new DataPoint(DateTimeAxis.ToDouble(DateTime.Now), double.Parse(ViewModel.ControlModule.Analogs["Analog4"].Result)));
                    }
                }
            }
            catch (Exception ex) { MessageBox.Show("Error during adding values to graph. \nReason: " + ex.Message, "Graph error", MessageBoxButton.OK, MessageBoxImage.Warning); }
        }

        /// <summary>
        /// Removes points from the series
        /// </summary>
        private void RemovePoints()
        {
            try
            {
                if (Leakrate.Count > 1000)
                    Leakrate.RemoveRange(0, 10);
                if (Testpressure.Count > 1000)
                    Testpressure.RemoveRange(0, 10);
                if (Analog1.Count > 1000)
                    Analog1.RemoveRange(0, 10);
                if (Analog2.Count > 1000)
                    Analog2.RemoveRange(0, 10);
                if (Analog3.Count > 1000)
                    Analog3.RemoveRange(0, 10);
                if (Analog4.Count > 1000)
                    Analog4.RemoveRange(0, 10);
            }
            catch (Exception ex) { MessageBox.Show("Error during erasing graph values. \nReason: " + ex.Message, "Graph error", MessageBoxButton.OK, MessageBoxImage.Warning); }
        }

        /// <summary>
        /// Method for updating graph values and time axis
        /// </summary>
        private void UpdatePlot()
        {
            while (true)
            {
                try
                {
                    lock (_lock)
                    {
                        RemovePoints();
                        AddPoints();

                        XaxisMax = DateTimeAxis.ToDouble(DateTime.Now.AddSeconds(1));
                        XaxisMin = DateTimeAxis.ToDouble(DateTime.Now.AddSeconds(-59));

                        Thread.Sleep(1000);
                    }
                }
                catch (Exception ex) { MessageBox.Show("Error during graph refresh. \nReason: " + ex.Message, "Graph error", MessageBoxButton.OK, MessageBoxImage.Warning); }
            }
        }
        #endregion
    }
}
