using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using C1.Win.Chart;
using System.Drawing.Drawing2D;
using System.Collections.ObjectModel;
using System.Diagnostics;
using C1.Win.C1SuperTooltip;
using C1.Chart;

namespace FlexChartExplorer.Samples
{
    public partial class TrendLine : UserControl
    {
        C1.Win.Chart.TrendLine trendLine;
        C1SuperLabel slEquation;
        ObservableCollection<DataItem> dataList = new ObservableCollection<DataItem>();
        DataItem clickedItem;

        public TrendLine()
        {
            InitializeComponent();

            flexChart1 = baseSample1.flexChart1;

            //Moving points on chart 
            flexChart1.MouseDown += flexChart1_MouseDown;
            flexChart1.MouseMove += flexChart1_MouseMove;
            flexChart1.MouseUp += flexChart1_MouseUp;
            flexChart1.MouseLeave += flexChart1_MouseLeave;

            //Show Equation
            flexChart1.Rendered += flexChart1_Rendered;

            SetupChart();

            InitializeControls();

        }

        void flexChart1_Rendered(object sender, RenderEventArgs e)
        {
            if (trendLine == null) return;

            //change label text
            if (chbShowEquation.Checked)
                slEquation.Text = GetEquationString(trendLine);
        }

        void flexChart1_MouseLeave(object sender, EventArgs e)
        {
            clickedItem = null;
        }

        void flexChart1_MouseUp(object sender, MouseEventArgs e)
        {
            clickedItem = null;
        }

        void flexChart1_MouseMove(object sender, MouseEventArgs e)
        {
            if (clickedItem != null)
            {
                var newY = (int)flexChart1.PointToData(new Point(e.X, e.Y)).Y;
                if (newY >= 0 && newY <= 100)
                {
                    clickedItem.Y = newY;
                    flexChart1.Rebind();
                }
            }
        }

        void flexChart1_MouseDown(object sender, MouseEventArgs e)
        {
            var ht = flexChart1.HitTest(new Point(e.X, e.Y));
            if (ht.Distance < 3 && ht.X != null && ht.Y != null)
            {
                int x, y;
                if (Int32.TryParse(ht.X.ToString(), out x) && Int32.TryParse(ht.Y.ToString(), out y))
                    clickedItem = dataList.FirstOrDefault(item => item.X == x && item.Y == y);
            }
        }

        string GetEquationString(C1.Win.Chart.TrendLine trendLine)
        {
            string result = String.Empty;
            int X = 1, Y0 = 0;

            switch (trendLine.FitType)
            {
                case FitType.Linear:
                    result = String.Format("y={1:0.0000}x{0:+0.0000;-0.0000;+0}", trendLine.Coefficients[0], trendLine.Coefficients[1]);
                    break;
                case FitType.Exponent:
                    result = String.Format("y={0:0.0000}e<sup>{1:0.0000}x</sup>", trendLine.Coefficients[0], trendLine.Coefficients[1]);
                    break;
                case FitType.Logarithmic:
                    result = String.Format("y={1:0.0000}ln(x){0:+0.0000;-0.0000;+0}", trendLine.Coefficients[0], trendLine.Coefficients[1]);
                    break;
                case FitType.Power:
                    result = String.Format("y={0:0.0000}x<sup>{1:0.0000}</sup>", trendLine.Coefficients[0], trendLine.Coefficients[1]);
                    break;
                case FitType.Polynom:
                    result = String.Format("{1:+0.0000;-0.0000;+0}x{0:+0.0000;-0.0000;+0}", trendLine.Coefficients[0], trendLine.Coefficients[1]);
                    for (int i = 2; i <= (int)udOrder.Value; i++)
                        result = result.Insert(0, String.Format("{0:+0.000;-0.0000;+0}x<sup>{1}</sup>", trendLine.Coefficients[i], i));
                    result = result.Remove(0, 1).Insert(0, "y=");
                    break;
                case FitType.Fourier:
                    result = String.Format("{0:+0.0000;-0.0000;+0}", trendLine.Coefficients[0]);
                    for (int i = 2, a = 1; i <= (int)udOrder.Value; i++, a = i % 2 == 0 ? a + 1 : a)
                        result += String.Format("{0:+0.000;-0.0000;+0}{2}({1}x)", trendLine.Coefficients[i - 1], a == 1 ? "" : a.ToString(), (i) % 2 == 0 ? "cos" : "sin");
                    result = result.Remove(0, 1).Insert(0, "y=");
                    break;
                case FitType.MaxX: result = "x=" + trendLine.GetValues(X).Max(); break;
                case FitType.MinX: result = "x=" + trendLine.GetValues(X).Min(); break;
                case FitType.MaxY: result = "y=" + trendLine.GetValues(Y0).Max(); break;
                case FitType.MinY: result = "y=" + trendLine.GetValues(Y0).Min(); break;
                case FitType.AverageX: result = "x=" + trendLine.GetValues(X).Average(); break;
                case FitType.AverageY: result = "y=" + trendLine.GetValues(Y0).Average(); break;
            }
            return result;
        }

        void SetupChart()
        {
            var rnt = new Random();

            for (int i = 1; i < 11; i++)
            {
                dataList.Add(new DataItem() { X = i, Y = rnt.Next(100) });
            }

            flexChart1.BeginUpdate();
            flexChart1.AxisY.Min = 0;
            flexChart1.AxisY.Max = 100;

            flexChart1.Series.Clear();

            flexChart1.ChartType = C1.Chart.ChartType.LineSymbols;

            var serie = new Series()
            {
                Name = "Base dataList",
                DataSource = dataList,
                Binding = "Y",
                BindingX = "X",
            };

            trendLine = new C1.Win.Chart.TrendLine()
            {
                Name = "Trend line",
                DataSource = dataList,
                Binding = "Y",
                BindingX = "X",
                Order = 4
            };

            flexChart1.Series.Add(serie);
            flexChart1.Series.Add(trendLine);

            flexChart1.EndUpdate();
        }

        private void cbFitType_SelectedIndexChanged(object sender, EventArgs e)
        {
            //change fit type
            trendLine.FitType = (C1.Chart.FitType)Enum.Parse(typeof(C1.Chart.FitType), (cbFitType.SelectedItem as ComboBoxItem).Text);

            //change Order setting visibility depending on fit type
            lblOrder.Visible = udOrder.Visible = trendLine.FitType == C1.Chart.FitType.Fourier || trendLine.FitType == C1.Chart.FitType.Polynom;
        }

        private void InitializeControls()
        {

            #region Init controls

            //Set localized text
            baseSample1.lblTitle.Text = Localizer.GetItem("trendline", "title");
            baseSample1.tbDescription.Rtf = Localizer.GetItem("trendline", "description").MakeRtf();

            //Init ShowEquation check box
            chbShowEquation = new CheckBox()
            {
                Text = "Show Equation",
                FlatStyle = FlatStyle.Flat,
                ForeColor = Color.DimGray,
                Checked = true
            };
            chbShowEquation.CheckedChanged += chbShowEquation_CheckedChanged;

            //Init FitType combobox
            cbFitType = new ComboBox()
            {
                Size = new Size(200, 21),
                FlatStyle = FlatStyle.Flat,
                ForeColor = Color.DimGray
            };
            cbFitType.SelectedIndexChanged += cbFitType_SelectedIndexChanged;

            //Init Order control
            udOrder = new NumericUpDown()
            {
                Size = new Size(150, 21),
                Minimum = 1,
                Maximum = 9,
                Value = 4,
                Visible = false,
                ForeColor = Color.DimGray
            };
            udOrder.ValueChanged += udOrder_ValueChanged;

            lblOrder = new Label()
            {
                Text = "Order:",
                Size = new Size(50, 21),
                TextAlign = ContentAlignment.BottomRight
            };

            slEquation = new C1SuperLabel()
            {
                Location = new Point((int)flexChart1.PlotRect.Location.X + 20, (int)flexChart1.PlotRect.Location.Y + 20),
                UseMnemonic = true,
                AutoSize = true
            };

            baseSample1.pControls.Controls.Add(chbShowEquation);
            baseSample1.pControls.Controls.Add(cbFitType);
            baseSample1.pControls.Controls.Add(lblOrder);
            baseSample1.pControls.Controls.Add(udOrder);
            flexChart1.Controls.Add(slEquation);

            #endregion

            foreach (var item in Enum.GetValues(typeof(C1.Chart.FitType)))
                cbFitType.Items.Add(new ComboBoxItem("Fit type") { Text = ((C1.Chart.FitType)item).ToString(), Value = (int)item });
            cbFitType.SelectedIndex = 0;
        }

        void chbShowEquation_CheckedChanged(object sender, EventArgs e)
        {
            slEquation.Visible = chbShowEquation.Checked;

            //change label text
            if (chbShowEquation.Checked)
                slEquation.Text = GetEquationString(trendLine);
        }

        void udOrder_ValueChanged(object sender, EventArgs e)
        {
            trendLine.Order = (int)udOrder.Value;
        }

        public class DataItem : INotifyPropertyChanged
        {
            int _y;
            public int X { get; set; }

            public int Y
            {
                get { return _y; }
                set
                {
                    if (value == _y) return;
                    _y = value;

                    OnPropertyChanged("Y");
                }
            }

            public event PropertyChangedEventHandler PropertyChanged;

            protected virtual void OnPropertyChanged(string propertyName)
            {
                var handler = PropertyChanged;
                if (handler != null)
                    handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}
