using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Web.UI.DataVisualization.Charting;
using System.Web.UI.WebControls;
using LeafWeb.Core.Charter;
using LeafWeb.Core.Utility;

namespace LeafWeb.WebCms.Services
{
	public static class LeafGasCharter
	{
	    private struct Fonts
	    {
	        public Font Regular { get; }
	        public Font Title { get; }
	        public Font Axis { get; }

	        public Fonts(double multiplier)
	        {
	            var regSize = CeilingMultiply(9, multiplier);
	            var titleSize = CeilingMultiply(10, multiplier);
                Regular = new Font(new FontFamily("Trebuchet MS"), regSize, FontStyle.Bold);
	            Title = new Font(new FontFamily("Times New Roman"), titleSize, FontStyle.Bold);
	            Axis = new Font(new FontFamily("Times New Roman"), titleSize, FontStyle.Bold);
	        }
        }

	    private struct ChartSizes
	    {
            public int Width { get; }
            public int Height { get; }
            public int BorderlineWidth { get; }

            public int RubiscoLimitedMarkerSize { get; }
            public int RubiscoLimitedBorderWidth { get; }

            public int RuBPMarkerSize { get; }
            public int RuBPBorderWidth { get; }

            public int TpuLimitedMarkerSize { get; }
            public int TpuLimitedBorderWidth { get; }

	        public int aCurveMarkerSize { get; }
            public int aCurveBorderWidth { get; }

	        public ChartSizes(double multiplier)
	        {
	            Width = CeilingMultiply(550, multiplier);
	            Height = CeilingMultiply(400, multiplier);
	            BorderlineWidth = CeilingMultiply(1, multiplier);

	            RubiscoLimitedMarkerSize = CeilingMultiply(8, multiplier);
	            RubiscoLimitedBorderWidth = CeilingMultiply(3, multiplier);

	            RuBPMarkerSize = CeilingMultiply(9, multiplier);
	            RuBPBorderWidth = CeilingMultiply(3, multiplier);

	            TpuLimitedMarkerSize = CeilingMultiply(9, multiplier);
	            TpuLimitedBorderWidth = CeilingMultiply(3, multiplier);

	            aCurveMarkerSize = CeilingMultiply(2, multiplier);
	            aCurveBorderWidth = CeilingMultiply(1, multiplier);
            }
	    }

	    private static int CeilingMultiply(int num, double multiplier)
	    {
	        return Convert.ToInt32(Math.Ceiling(num * multiplier));
	    }

	    // cntrlcomparison
		public static IEnumerable<Chart> ProduceCharts(CurveData curve, double multiplier = 1d)
		{
    		var curveId = curve.CurveId;
		    var fonts = new Fonts(multiplier);
		    var sizes = new ChartSizes(multiplier);

		    return curve.ParamSets.SelectMany(item => CurveSeries(curveId, item, item.CurveType.GetDescription(), sizes, fonts));
		}

		private static IEnumerable<Chart> CurveSeries(string curveId, CurveParamSet paramSet, string chartTitle, ChartSizes sizes, Fonts fonts)
		{
			var chloroChart = CreateEmptyChart("Chloroplastic CO2 partial pressure (Pa)", sizes, fonts);
			var interChart = CreateEmptyChart("Intercellular CO2 partial pressure (Pa)", sizes, fonts);

			// Set the points for the symbol series for paramater set 1, chloroplastic
			AddAnetMeasPoints(paramSet.AnetMeasChloro1Data, chloroChart.Series["Rubisco-limited"]);
			AddAnetMeasPoints(paramSet.AnetMeasChloro2Data, chloroChart.Series["RuBP regeneration-limited"]);

			var tpuSeries = NewTpuSeries(paramSet.AnetMeasChloro3Data, sizes);
			AddAnetMeasPoints(paramSet.AnetMeasChloro3Data, tpuSeries);
			chloroChart.Series.Add(tpuSeries);

			// Set the points for the symbol series for paramater set 1, intercellular
			AddAnetMeasPoints(paramSet.AnetMeasInter1Data, interChart.Series["Rubisco-limited"]);
			AddAnetMeasPoints(paramSet.AnetMeasInter2Data, interChart.Series["RuBP regeneration-limited"]);

			tpuSeries = NewTpuSeries(paramSet.AnetMeasInter3Data, sizes);
			AddAnetMeasPoints(paramSet.AnetMeasInter3Data, tpuSeries);
			interChart.Series.Add(tpuSeries);

			// Set the points on the asymptote curve for parameter set 1, chloroplast
			AddAsymptotePoints(paramSet.AcChloroData, chloroChart.Series["acCurve"]);
			AddAsymptotePoints(paramSet.AjChloroData, chloroChart.Series["ajCurve"]);
			AddAsymptotePoints(paramSet.AtChloroData, chloroChart.Series["atCurve"]);

			// Set the points on the asymptote curve for parameter set 1, intercellular
			AddAsymptotePoints(paramSet.AcInterData, interChart.Series["acCurve"]);
			AddAsymptotePoints(paramSet.AjInterData, interChart.Series["ajCurve"]);
			AddAsymptotePoints(paramSet.AtInterData, interChart.Series["atCurve"]);

			var title = new Title($"LeafWeb curveID = {curveId}\n{chartTitle}"){Font = fonts.Title};
			chloroChart.Titles.Add(title);
			interChart.Titles.Add(title);

			chloroChart.ChartAreas["ChartArea"].AxisX.TitleFont = fonts.Axis;
			chloroChart.ChartAreas["ChartArea"].AxisY.TitleFont = fonts.Axis;

			interChart.ChartAreas["ChartArea"].AxisX.TitleFont = fonts.Axis;
			interChart.ChartAreas["ChartArea"].AxisY.TitleFont = fonts.Axis;

			yield return chloroChart;
			yield return interChart;
		}

		private static Series NewTpuSeries(IReadOnlyCollection<XyPoint> data, ChartSizes sizes)
		{
			var seriesName = "TPU-limited";
			if (data.Count == 0)
				seriesName = "Curve Asymptote";
			var series3 = new Series(seriesName)
			{
				MarkerSize = sizes.TpuLimitedMarkerSize,
				BorderWidth = sizes.TpuLimitedBorderWidth,
				XValueType = ChartValueType.Double,
				ChartType = SeriesChartType.Point,
				MarkerStyle = MarkerStyle.Square,
				ShadowColor = Color.Black,
				BorderColor = Color.Black,
				Color = Color.Orange,
				ShadowOffset = 0,
				YValueType = ChartValueType.Double
			};

			return series3;
		}

		private static void AddAnetMeasPoints(IEnumerable<XyPoint> data, Series series)
		{
			// Set the points for the series from the ArrayList
			foreach (var xy in data)
			{
				series.Points.AddXY(xy.X, xy.Y);
			}
		}

		private static void AddAsymptotePoints(IEnumerable<XyPoint> data, Series series)
		{
			// Set the points for the series from the ArrayList
			foreach (var xy in data.Where(xy => (xy.X != -9999) && (xy.Y != -9999)))
			{
				series.Points.AddXY(xy.X, xy.Y);
			}
		}

		private static Chart CreateEmptyChart(string axisXTitle, ChartSizes sizes, Fonts fonts)
		{
			var borderColor = Color.FromArgb(180, 26, 59, 105);
			var chart = new Chart
			{
				BackColor = Color.White,
				Width = Unit.Pixel(sizes.Width),
				Height = Unit.Pixel(sizes.Height),
				//BorderSkin = {SkinStyle = BorderSkinStyle.None},
				BorderColor = borderColor,
				BorderlineColor = borderColor,
				BorderlineWidth = sizes.BorderlineWidth,
				BorderlineDashStyle = ChartDashStyle.Solid
			};

			chart.Legends.Add(new Legend
			{
				Enabled = true,
				IsTextAutoFit = false,
				Name = "Default",
				Docking = Docking.Bottom,
				BackColor = Color.Transparent,
				Font = fonts.Regular
            });

			chart.ChartAreas.Add(new ChartArea
			{
				Name = "ChartArea",
				BorderColor = Color.FromArgb(64, 64, 64, 64),
				BorderDashStyle = ChartDashStyle.Solid,
				BackSecondaryColor = Color.White,
				BackColor = Color.OldLace,
				ShadowColor = Color.Transparent,
				BackGradientStyle = GradientStyle.TopBottom,
				AxisY = new Axis
				{
					LineColor = Color.FromArgb(64, 64, 64, 64),
					Title = "Net assimilation rate (umol/m2/s)",
					LabelStyle = { Font = fonts.Regular },
					MajorGrid = new Grid { LineColor = Color.FromArgb(64, 64, 64, 64)}
				},
				AxisX = new Axis
				{
					LineColor = Color.FromArgb(64, 64, 64, 64),
					Minimum = 0,
					Title = axisXTitle,
					LabelStyle = { Font = fonts.Regular },
					MajorGrid = new Grid { LineColor = Color.FromArgb(64, 64, 64, 64)}
				}
			});

			chart.Series.Add(new Series
			{
				MarkerSize = sizes.RubiscoLimitedMarkerSize,
				BorderWidth = sizes.RubiscoLimitedBorderWidth,
				XValueType = ChartValueType.Double,
				Name = "Rubisco-limited",
				ChartType = SeriesChartType.Point,
				MarkerStyle = MarkerStyle.Diamond,
				ShadowColor = Color.Black,
				BorderColor = borderColor,
				Color = Color.Red,
				ShadowOffset = 0,
				YValueType = ChartValueType.Double
			});

			chart.Series.Add(new Series
			{
				MarkerSize = sizes.RuBPMarkerSize,
				BorderWidth = sizes.RuBPBorderWidth,
				XValueType = ChartValueType.Double,
				Name = "RuBP regeneration-limited",
				ChartType = SeriesChartType.Point,
				MarkerStyle = MarkerStyle.Circle,
				ShadowColor = Color.Black,
				BorderColor = borderColor,
				Color = Color.Blue,
				ShadowOffset = 0,
				YValueType = ChartValueType.Double
			});

			chart.Series.Add(new Series
			{
				MarkerSize = sizes.aCurveMarkerSize,
				BorderWidth = sizes.aCurveBorderWidth,
				XValueType = ChartValueType.Double,
				Name = "acCurve",
				ChartType = SeriesChartType.Line,
				MarkerStyle = MarkerStyle.None,
				ShadowColor = Color.Black,
				BorderColor = borderColor,
				Color = Color.Red,
				ShadowOffset = 0,
				YValueType = ChartValueType.Double,
				IsVisibleInLegend = false
			});

			chart.Series.Add(new Series
			{
			    MarkerSize = sizes.aCurveMarkerSize,
			    BorderWidth = sizes.aCurveBorderWidth,
				XValueType = ChartValueType.Double,
				Name = "ajCurve",
				ChartType = SeriesChartType.Line,
				MarkerStyle = MarkerStyle.None,
				ShadowColor = Color.Black,
				BorderColor = borderColor,
				Color = Color.Blue,
				ShadowOffset = 0,
				YValueType = ChartValueType.Double,
				IsVisibleInLegend = false
			});

			chart.Series.Add(new Series
			{
			    MarkerSize = sizes.aCurveMarkerSize,
			    BorderWidth = sizes.aCurveBorderWidth,
				XValueType = ChartValueType.Double,
				Name = "atCurve",
				ChartType = SeriesChartType.Line,
				MarkerStyle = MarkerStyle.None,
				ShadowColor = Color.Black,
				BorderColor = borderColor,
				Color = Color.Orange,
				ShadowOffset = 0,
				YValueType = ChartValueType.Double,
				IsVisibleInLegend = false
			});

			return chart;
		}
	}
}