using System;
using System.Collections.Generic;
using System.Linq;
using LeafWeb.Core.Entities;
using LeafWeb.Core.Utility;

namespace LeafWeb.Core.Charter
{
	public static class CurveDataConverter
	{
		public static IEnumerable<CurveData> Convert(IEnumerable<LeafGasComparison> leafGasComparison)
		{
			var comparisonGroups = 
				from comparison in leafGasComparison
				group comparison by comparison.CurveID into curves
				select new
				{
					CurveID = curves.Key,
					FitType = from curve in curves
						   group curve by new {curve.FitRwp, curve.FitGammaStar} into grp
						   select new
						   {
							   grp.Key.FitRwp,
							   grp.Key.FitGammaStar,
							   Comparison = grp.FirstOrDefault() // TODO: this may need to be SingleOrDefault
						   }
				};

			foreach (var comparisonGroup in comparisonGroups)
			{
				var curveData = new CurveData {CurveId = comparisonGroup.CurveID};

				foreach (var fitType in comparisonGroup.FitType)
				{
					var paramSet = ConvertParamSet(fitType.Comparison);

					// fixed = not fitted
					// estimated = fitted
					// gi = internal conductance = "Cnd"
					// gammastar = chloroplastic CO2 partial pressure photocompensation point = "Cmp"

					// put each comparison into the correct curve location
				    if      (!fitType.FitRwp && !fitType.FitGammaStar)
				        paramSet.CurveType = CurveType.FixedCndFixedCmp;
					else if (!fitType.FitRwp &&  fitType.FitGammaStar)
				        paramSet.CurveType = CurveType.FixedCndEstimatedCmp;
					else if ( fitType.FitRwp && !fitType.FitGammaStar)
				        paramSet.CurveType = CurveType.EstimatedCndFixedCmp;
					else if ( fitType.FitRwp &&  fitType.FitGammaStar)
				        paramSet.CurveType = CurveType.EstimatedCndEstimatedCmp;

                    curveData.ParamSets.Add(paramSet);
				}

				yield return curveData;
			}
		}

		private static CurveParamSet ConvertParamSet(LeafGasComparison comparison)
		{
			var curveParamSet = new CurveParamSet();
			foreach (var fittingInfo in comparison.FittingInfo)
			{
				Set(curveParamSet, fittingInfo);
			}
			foreach (var photosyntheticInfo in comparison.PhotosyntheticInfo)
			{
				Set(curveParamSet, photosyntheticInfo);
			}
			return curveParamSet;
		}

		private static void Set(CurveParamSet paramSet, LeafGasComparisonPhotosyntheticInfo item)
		{
			AddXyIfInRange(item.CO2cc, item.Ac, paramSet.AcChloroData.Add); // Ac(y),CO2cc(x)
			AddXyIfInRange(item.CO2cj, item.Aj, paramSet.AjChloroData.Add); // Aj(y),CO2cj(x)
			AddXyIfInRange(item.CO2ct, item.At, paramSet.AtChloroData.Add); // At(y),CO2ct(x)

			AddXyIfInRange(item.CO2i, item.Ac, paramSet.AcInterData.Add); // Ac(y),CO2i(x)
			AddXyIfInRange(item.CO2i, item.Aj, paramSet.AjInterData.Add); // Aj(y),CO2i(x)
			AddXyIfInRange(item.CO2i, item.At, paramSet.AtInterData.Add); // Aj(y),CO2i(x)
		}

		private static void AddXyIfInRange(double x, double y, Action<XyPoint> set)
		{
			var xyPoint = new XyPoint(x, y);
			if (xyPoint.YIsInRange(-20.0, 50.0))
				set(xyPoint);
		}

		private static void Set(CurveParamSet paramSet, LeafGasComparisonFittingInfo fittingInfo)
		{
			var xyPoint1 = new XyPoint(fittingInfo.CO2c, fittingInfo.Anet_obs); // AnetMeas(y), PCO2c(x)
			var xyPoint2 = new XyPoint(fittingInfo.CO2i_obs, fittingInfo.Anet_obs);

			switch (fittingInfo.LimitState)
			{
				case 1:
					paramSet.AnetMeasChloro1Data.Add(xyPoint1);
					paramSet.AnetMeasInter1Data.Add(xyPoint2);
					break;
				case 2:
					paramSet.AnetMeasChloro2Data.Add(xyPoint1);
					paramSet.AnetMeasInter2Data.Add(xyPoint2);
					break;
				case 3:
					paramSet.AnetMeasChloro3Data.Add(xyPoint1);
					paramSet.AnetMeasInter3Data.Add(xyPoint2);
					break;
			}
		}
	}
}