using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using ExcelLibrary.SpreadSheet;
using MileageTraker.Web.Models;

namespace MileageTraker.Web.Utility
{
	public static class ExcelWriter
	{
		public static void WriteXls<T>(IEnumerable<T> items, string filename, string worksheetTitle, string worksheetName)
		{
			using (var fileStream = new FileStream(filename, FileMode.Create))
				WriteXls(items, fileStream, worksheetTitle, worksheetName);
		}

		public static byte[] WriteXls<T>(IEnumerable<T> items, string worksheetTitle, string worksheetName)
		{
			using (var stream = new MemoryStream())
			{
				WriteXls(items, stream, worksheetTitle, worksheetName);
				return stream.ToArray();
			}
		}

		public static void WriteXls<T>(IEnumerable<T> items, Stream stream, string worksheetTitle, string worksheetName)
		{
			var workbook = new Workbook();
			var worksheet = new Worksheet(worksheetName);

			// write worksheet header
			worksheet.Cells[0, 3] = new Cell(worksheetTitle);
			
			// write column headers
			var properties =
				typeof(T).GetProperties()
					.Where(p => !p.PropertyType.IsCollection())
					.Where(p => !p.Name.Contains("PreviousLog"))
					.ToList();

			properties.Zip(
				CustomExtensions.GetNumbers(), 
				(p, c) => 
					worksheet.Cells[2, c] = new Cell(p.GetName())
				).ToList();

			// write the data
			items.Zip(
				CustomExtensions.GetNumbers().Skip(3),
				(item, r) =>
					properties.Zip(CustomExtensions.GetNumbers(),
					(p, c) => {
						var value = p.GetValue(item);
						
						string formatString = null;
						
						if (value is decimal) // assume that it's currency
							formatString = "#,##0.00";
						
						if (value is DateTime && p.Name == "Date")
							formatString = @"YYYY\-MM\-DD";
						else if (value is DateTime)
							formatString = @"YYYY\-MM\-DD hh:mm:ss";

						int intValue; // write int-looking values as numbers
						if (value is string && int.TryParse((string)value, out intValue))
							value = intValue;

						double doubleValue; // write double-looking values as numbers
						if (value is string && double.TryParse((string)value, out doubleValue))
							value = doubleValue;

						if (value is MileageLogTypeWrapper)
							value = ((MileageLogTypeWrapper) value).Enum.GetDisplayName();

						var cell = formatString != null ? new Cell(value,formatString) : new Cell(value);
						worksheet.Cells[r, c] = cell;
						
						return (string)null;
					}).ToList()).ToList();

			PadWorksheet(worksheet);

			workbook.Worksheets.Add(worksheet);
			workbook.Save(stream);
		}

		private static void PadWorksheet(Worksheet worksheet)
		{
			Func<int> totalCells = () => (worksheet.Cells.LastRowIndex + 1)*(worksheet.Cells.LastColIndex + 1);
			for (var r = worksheet.Cells.LastRowIndex + 1; totalCells() < 320; r++)
			{
				for (var c = 0; c < worksheet.Cells.LastColIndex; c++)
				{
					worksheet.Cells[r, c] = new Cell("");
				}
			}
		}
	}
}