using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Net;
using System.Web.Mvc;
using System.Web.UI.DataVisualization.Charting;
using System.Web.UI.WebControls;
using log4net;
using LeafWeb.Core.Charter;
using LeafWeb.Core.Entities;
using LeafWeb.Core.Parsers;
using LeafWeb.Core.Utility;
using LeafWeb.WebCms.Models;
using LeafWeb.WebCms.Services;

namespace LeafWeb.WebCms.Controllers
{
	public class ChartController : BaseController
	{
		public ActionResult Index(int? leafInputId)
		{
			if (!leafInputId.HasValue)
				return View("DataError", model: "Must specify LeafInputId");

		    //if (leafInputId == 2147483647)
		    //    return View(GetTestChartViewModel());

			var leafOutputFile = DataService.GetLeafOutput_ChartFile(leafInputId.Value);

			if (leafOutputFile == null)
				throw new ArgumentOutOfRangeException(); // TODO: break

			try
			{
				var curveIds = GetCurveIds(leafOutputFile.FileContents.Contents);

				var viewModel = new ChartViewModel
				{
					AvailableCurveId = curveIds,
					LeafInputId = leafInputId.Value,
					LeafInputIdentifier = leafOutputFile.LeafInput.Identifier
				};

				return View(viewModel);
			}
			catch (ParseException parseException)
			{
				var logger = LogManager.GetLogger(GetType());
				logger.Warn(parseException);
				return View("DataError", model: parseException.Message);
			}
			catch (Exception e)
			{
				var logger = LogManager.GetLogger(GetType());
				logger.Warn(e);
				return View("DataError", model: "Error while loading data. Administrators have been notified.");
			}
		}

	    //private ChartViewModel GetTestChartViewModel()
	    //{
	    //    return new ChartViewModel
	    //    {
	    //        AvailableCurveId = new []{"Valid", "Test"},
	    //        LeafInputId = 2147483647,
	    //        LeafInputIdentifier = "Chart Tests"
	    //    };
	    //}

	    public ActionResult ChartCurve(int leafInputId, string curveId, bool base64 = false)
	    {
			var leafOutputFile = DataService.GetLeafOutput_ChartFile(leafInputId);

	        if (leafOutputFile == null)
	        {
	            var logger = LogManager.GetLogger(GetType());
	            logger.Warn($"Invalid LeafInputID {leafInputId}");
                return new HttpStatusCodeResult(HttpStatusCode.NotFound, $"Invalid LeafInputId {leafInputId}.");
	        }

	        CurveData curveData;
		    try
		    {
			    curveData = GetCurveData(leafOutputFile.FileContents.Contents, curveId);
		    }
		    catch (ParseException e)
		    {
				return new HttpStatusCodeResult(HttpStatusCode.NotFound, $"Parse exception in {leafOutputFile.Filename}. {e.Message}" );
			}
		    catch (Exception)
		    {
				return new HttpStatusCodeResult(HttpStatusCode.NotFound, $"Unhandled exception thrown while parsing {leafOutputFile.Filename}.");
			}

			if (curveData == null || curveData.ParamSets.Count == 0)
			    return new HttpStatusCodeResult(HttpStatusCode.NotFound, $"No curve data was loaded from {leafOutputFile.Filename}.");

		    var charts = GetChartBitmaps(curveData).ToList();

		    var combinedChart = CombineBitmaps(charts);

		    foreach (var chart in charts) chart.Dispose(); // cleanup

			using (var ms = new MemoryStream())
			{
                combinedChart.Save(ms, ImageFormat.Png);
			    ms.Seek(0, SeekOrigin.Begin);
			    var base64String = Convert.ToBase64String(ms.ToArray());
                if (base64)
			        return Content(base64String, "image/png");
                else
			        return File(ms.ToArray(), "image/png", curveId.FilterValidFilename() + ".png");
			}
		}

		private CurveData GetCurveData(byte[] fileContents, string curveId)
		{
			try
			{
				LeafGasComparison[] leafGasComparisons;
				using (var parser = new LeafGasComparisonParser(fileContents))
					leafGasComparisons = parser.Parse(curveId);
				return CurveDataConverter.Convert(leafGasComparisons).FirstOrDefault();
			}
			catch (Exception e)
			{
				LogManager.GetLogger(GetType()).Warn(e);
				throw;
			}
		}

		private string[] GetCurveIds(byte[] fileContents)
		{
			using (var parser = new LeafGasComparisonParser(fileContents))
				return parser.ExtractCurveIds();
		}

		private IEnumerable<Bitmap> GetChartBitmaps(CurveData curveData)
		{
			var charts = LeafGasCharter.ProduceCharts(curveData, 1.5);
			
			foreach (var chart in charts)
			{
				// http://stackoverflow.com/a/336396/99492
				var ms = new MemoryStream(); // this gets attached to the bmp, dispose of it later
				chart.SaveImage(ms, ChartImageFormat.Bmp);
				ms.Seek(0, SeekOrigin.Begin);
				yield return new Bitmap(ms);
			}
		}

		private Bitmap CombineBitmaps(IList<Bitmap> bitmaps, int columnCount = 2)
		{
			if (!bitmaps.Any())
				return null;
			// bitmaps assumed to have same dimensions, use the first one to define that
			var cellWidth = bitmaps[0].Width;
			var cellHeight = bitmaps[0].Height;
			var width = cellWidth * columnCount;
			var height = cellHeight * bitmaps.Count / columnCount;

			var combinedBitmap = new Bitmap(width, height);
			using (var g = Graphics.FromImage(combinedBitmap))
			{
				var currentCol = 0;
				var currentRow = 0;
				foreach (var image in bitmaps)
				{
					g.DrawImage(image, currentCol * cellWidth, currentRow * cellHeight);

					currentCol = (currentCol + 1)%columnCount;
					currentRow += currentCol == 0 ? 1 : 0;
				}
			}
			return combinedBitmap;
		}

		public ActionResult ChartSample()
		{
			var chart = new Chart
			{
				BackColor = Color.FromArgb(255, 255, 255),
				Width = Unit.Pixel(250),
				Height = Unit.Pixel(2500)
			};

			var series = new Series
			{
				ChartArea = "ca1",
				ChartType = SeriesChartType.Line
			};

			//series.Font = new Font("Verdana", 8.25f, FontStyle.Regular);

			var myRandom = new Random();

			for (int i = 0; i < 100; i++)
			{
				var dp = new DataPoint();
				dp.AxisLabel = String.Format("{0}-{1}", i, Guid.NewGuid().ToString().Substring(0, 4));
				dp.YValues = new double[] { myRandom.Next(5, 100) };
				series.Points.Add(dp);
			}


			chart.Series.Add(series);

			var area = new ChartArea("ca1");
			area.Area3DStyle.Enable3D = false;
			area.AxisX.Interval = 1;
			//area.BackColor = Color.Transparent;
			//var labelStyle = new LabelStyle();
			//labelStyle.Enabled = true;
			//labelStyle.Font = new Font("Arial", 3f);
			area.AxisX.LabelStyle.Font = new Font("Verdana", 8.25f, FontStyle.Underline);//Why does it recognize the style but not the font!!!???

			chart.ChartAreas.Add(area);

			using (var ms = new MemoryStream())
			{
				chart.SaveImage(ms, ChartImageFormat.Png);
				ms.Seek(0, SeekOrigin.Begin);

				return File(ms.ToArray(), "image/png", "mychart.png");
			}
		}
	}
}