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 C1.Chart;
using System.IO;
using System.Threading;

namespace FlexChartExplorer.Samples
{
    public partial class Zones : UserControl
    {
        const int STUDENTS_COUNT = 200;
        const int MAX_POINT = 1600;

        Color[] _zones_colors = new Color[]
        {
            Color.FromArgb(64, 255,192,192),
            Color.FromArgb(128, 55,228,228),
            Color.FromArgb(128, 255,228,128),
            Color.FromArgb(128, 128,255,128),
            Color.FromArgb(128, 128,128,225)
        };
        double[] _zones;

        Random rnd = new Random();

        public Zones()
        {
            InitializeComponent();
            InitializeControls();

            SetupChart();
        }

        void SetupChart()
        {
            // Generate data
            var data = new StudentScore[STUDENTS_COUNT];
            for (var i = 0; i < STUDENTS_COUNT; i++)
            {
                data[i] = new StudentScore() { Number = i, Score = (int)(MAX_POINT * 0.5 * (1 + rnd.NextDouble())) };
            }

            // Calculate statistics
            var mean = FindMean(data);
            var stdDev = FindStdDev(data, mean);

            flexChart1.BeginUpdate();
            flexChart1.Series.Clear();

            // Format axis
            flexChart1.AxisX.AxisLine = false;
            flexChart1.AxisX.Min = 0;
            flexChart1.AxisX.Max = STUDENTS_COUNT;
            flexChart1.AxisX.Title = "student number";
            flexChart1.AxisY.Title = "student accumalated points";
            flexChart1.AxisY.MinorGrid = flexChart1.AxisY.MajorGrid = false;

            // Add data to the chart
            var seriesScore = new Series()
            {
                Name = "raw score",
                ChartType = ChartType.Scatter,
                BindingMode = C1.Win.Chart.BindingMode.Direct,
                Binding = "Score",
                DataSource = data
            };

            flexChart1.Series.Add(seriesScore);

            // Statistics series
            for (var i = -2; i <= 2; i++)
            {
                var y = mean + i * stdDev;
                var sdata = new PointF[]
                    {
                        new PointF(0, (float)y), 
                        new PointF(STUDENTS_COUNT - 1, (float)y)
                    };

                var series = new Series();
                series.DataSource = sdata;
                series.BindingX = "X";
                series.Binding = "Y";
                series.ChartType = ChartType.Line;
                series.Style.Stroke = new SolidBrush(Color.FromArgb(20, 20, 20));
                series.Style.StrokeWidth = 2;

                if (Math.Abs(i) == 1)
                    series.Style.StrokeDashPattern = new float[] { 5, 1 };
                else if (Math.Abs(i) == 2)
                    series.Style.StrokeDashPattern = new float[] { 2, 2 };

                if (i > 0)
                    series.Name = "m+" + i + "s";
                else if (i < 0)
                    series.Name = "m" + i + "s";
                else
                    series.Name = "mean";
                
                flexChart1.Series.Add(series);
            }

            var scores = data.Select(x => x.Score).OrderByDescending(x => x).ToArray();

            _zones = new double[]{
                scores[GetBoundingIndex(scores, 0.95)],
                scores[GetBoundingIndex(scores, 0.75)],
                scores[GetBoundingIndex(scores, 0.25)],
                scores[GetBoundingIndex(scores, 0.05)]
            };

            // Add _zones to legend
            for (var i = 0; i < 5; i++)
            {
                var series = new Series();
                series.ChartType = ChartType.Area;
                series.Style.Fill = new SolidBrush(_zones_colors[4 - i]);
                series.Style.Stroke = new SolidBrush(Color.Transparent);
                series.Name = "ABCDE"[i].ToString();
                flexChart1.Series.Add(series);
            }

            flexChart1.Rendering += flexChart1_Rendering;

            flexChart1.EndUpdate();
        }

        double FindMean(StudentScore[] data)
        {
            double sum = 0;
            for (var i = 0; i < data.Count(); i++)
            {
                sum += data[i].Score;
            }
            return sum / data.Count();
        }

        double FindStdDev(StudentScore[] data, double mean)
        {
            double sum = 0;
            for (var i = 0; i < data.Count(); i++)
            {
                var d = data[i].Score - mean;
                sum += d * d;
            }
            return Math.Sqrt(sum / data.Count());
        }

        int GetBoundingIndex(int[] data, double frac)
        {
            var n = data.Count();
            var i = (int)Math.Ceiling(n * frac);
            while (i > data[0] && data[i] == data[i + 1])
                i--;
            return i;
        }

        void DrawAlarmZone(FlexChart chart, IRenderEngine engine, double xmin, double ymin, double xmax, double ymax, Color fill)
        {
            var pt1 = chart.DataToPoint(new PointF((float)xmin, (float)ymin));
            var pt2 = chart.DataToPoint(new PointF((float)xmax, (float)ymax));
            engine.SetFill(new SolidBrush(fill));
            engine.SetStroke(new SolidBrush(Color.Transparent));
            engine.DrawRect(Math.Min(pt1.X, pt2.X), Math.Min(pt1.Y, pt2.Y), Math.Abs(pt2.X - pt1.X), Math.Abs(pt2.Y - pt1.Y));
        }

        void flexChart1_Rendering(object sender, RenderEventArgs e)
        {
            var engine = e.Engine;
            var chart = (sender as FlexChart);

            if (engine == null || chart == null)
                return;

            for (var i = 0; i < 5; i++)
            {
                var ymin = i == 0 ? chart.AxisY.ActualMin : _zones[i - 1];
                var ymax = i == 4 ? chart.AxisY.ActualMax : _zones[i];
                DrawAlarmZone(chart, engine, chart.AxisX.ActualMin, ymin, chart.AxisX.ActualMax, ymax, _zones_colors[i]);
            }
        }

        private void InitializeControls()
        {
            #region Init controls

            flexChart1 = baseSample1.flexChart1;
            baseSample1.lblTitle.Text = Localizer.GetItem("zones", "title");
            baseSample1.tbDescription.Rtf = Localizer.GetItem("zones", "description").MakeRtf();
            baseSample1.pDescription.Height = 100;
            baseSample1.pControls.Visible = false;

            #endregion
        }

        public class StudentScore
        {
            public int Number { get; set; }
            public int Score { get; set; }
        }
    }
}
