using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using LeafWeb.Core.Entities;
using MlkPwgen;

namespace LeafWeb.Core.DAL
{
	public class DataService : IDisposable
	{
		public static void RegisterInitializer()
		{
			Database.SetInitializer(new LeafWebInitializer());
			new LeafWebContext().Database.Initialize(true);
		}

		private readonly LeafWebContext _db = new LeafWebContext();

		public void Dispose()
		{
			_db.Dispose();
		}

		private void RemoveCollectionFromDbSet<T>(ICollection<T> collection, IDbSet<T> set) where T : class
		{
			if (collection == null) return;
			foreach (var entity in collection.ToArray())
				set.Remove(entity);
		}

		#region Fluxnet Sites

		public IQueryable<FluxnetSite> GetFluxnetSites()
		{
			return _db.FluxnetSites;
		}

		public IQueryable<FluxnetSite> GetFluxnetSitesAutocomplete(string term)
		{
			return 
				from fs in GetFluxnetSites()
				where fs.FluxnetId.StartsWith(term)
				|| fs.SiteName.Contains(term)
				select fs;
		}
		#endregion

		#region LeafInput

		public IQueryable<LeafInput> GetLeafInputs()
		{
			return _db.LeafInputs;
		}

		public IQueryable<LeafInput> GetLeafInputsOrdered()
		{
			return _db.LeafInputs
				// first by in-progress items
				.OrderByDescending(li => 
					li.CurrentStatus == LeafInputStatusType.Running ||
					li.CurrentStatus == LeafInputStatusType.Starting ||
					li.CurrentStatus == LeafInputStatusType.Finishing ||
					li.CurrentStatus == LeafInputStatusType.Cancelling ||
					li.CurrentStatus == LeafInputStatusType.CancelPending ||
                    li.CurrentStatus == LeafInputStatusType.Unresponsive)
				// then by pending, by priority
				.ThenByDescending(li => 
					li.CurrentStatus == LeafInputStatusType.Pending 
					? (int)li.PendingPriority
					: int.MinValue)
				// then by pending, rest by the order they're added in, ascending
				.ThenBy(li => 
					li.CurrentStatus == LeafInputStatusType.Pending 
					? li.Id
					: int.MaxValue)
				// then the rest descending by the order last status change
				.ThenByDescending(li => 
					(from s in li.StatusHistory
					select s).Max(s => s.DateTime));
		}

		public IEnumerable<LeafInput> GetLeafInputRecentlyCompleted(int count)
		{
			return
				_db.LeafInputs
					.Where(li => li.CurrentStatus == LeafInputStatusType.Complete)
					.Take(count)
					.ToList()
					.Where(li => li.OutputErrorMessage == null);
		}

		public LeafInput GetLeafInput(int id)
		{
			return _db.LeafInputs.FirstOrDefault(li => li.Id == id);
		}

		public void DeleteLeafInput(LeafInput leafInput)
		{
			RemoveCollectionFromDbSet(leafInput.InputFiles, _db.LeafInputFiles);

			if (leafInput.OutputFiles != null)
				foreach (var outputFile in leafInput.OutputFiles.ToArray())
					if (outputFile.FileContents != null)
						_db.LeafOutputFileContents.Remove(outputFile.FileContents);
			RemoveCollectionFromDbSet(leafInput.OutputFiles, _db.LeafOutputFiles);

			RemoveCollectionFromDbSet(leafInput.StatusHistory, _db.LeafInputStatus);
			// Data
			if (leafInput.LeafInputData != null)
				foreach (var leafInputData in leafInput.LeafInputData.ToArray())
					RemoveLeafInputDataNoUpdate(leafInputData);
			_db.LeafInputs.Remove(leafInput);
			_db.SaveChanges();
		}

		private void RemoveLeafInputDataNoUpdate(LeafInputData leafInputData)
		{
			if (leafInputData.Data != null)
				foreach (var curve in leafInputData.Data.ToArray())
					_db.LeafInputDataCurves.Remove(curve);
			if (leafInputData.Photosynthetic != null)
				_db.LeafInputDataPhotosynthetic.Remove(leafInputData.Photosynthetic);
			if (leafInputData.Site != null)
				_db.LeafInputDataSite.Remove(leafInputData.Site);
			_db.LeafInputData.Remove(leafInputData);
		}

		public LeafOutputFile GetLeafOutput_ChartFile(int leafInputId)
		{
			return GetLeafOutput_FilenameLike(leafInputId, LeafOutputFile.Filename_LeafChart);
		}

		private LeafOutputFile GetLeafOutput_FilenameLike(int leafInputId, string filename)
		{
			var leafOutputChartFile =
				from leafInput in _db.LeafInputs
				where leafInput.Id == leafInputId
				from leafOutput in leafInput.OutputFiles
				where leafOutput.Filename.Contains(filename)
				select leafOutput;

			return leafOutputChartFile.FirstOrDefault();
		}

		public LeafInput GetLeafInput(string uniqueToken)
		{
			return _db.LeafInputs.FirstOrDefault(li => li.UniqueToken == uniqueToken);
		}

		public IQueryable<LeafInput> GetLeafInputs(params LeafInputStatusType[] statuses)
		{
			return
				from file in GetLeafInputs()
				where statuses.Contains(file.CurrentStatus)
				select file;
		}

		public void AddLeafInput(LeafInput leafInput)
		{
			leafInput.Added = DateTime.Now;
			leafInput.UniqueToken = PasswordGenerator.Generate(12);
			_db.LeafInputs.Add(leafInput);
			SetLeafInputStatusNoUpdate(leafInput, LeafInputStatusType.Pending, "LeafInput added");
			_db.SaveChanges();
		}

		private void SetLeafInputStatusNoUpdate(LeafInput leafInputFile, LeafInputStatusType status, string description = null, string details = null)
		{
			leafInputFile.CurrentStatus = status;
			var leafInputFileStatus = new LeafInputStatus
			{
				Status = status,
				DateTime = DateTime.Now,
				Description = description,
				Details = details,
				LeafInput = leafInputFile
			};
			if (leafInputFile.StatusHistory == null)
				leafInputFile.StatusHistory = new List<LeafInputStatus>();
			leafInputFile.StatusHistory.Add(leafInputFileStatus);
		}

		public void SetLeafInputStatus(LeafInput leafInput, LeafInputStatusType status, string description = null, string details = null)
		{
			SetLeafInputStatusNoUpdate(leafInput, status, description, details);
			UpdateLeafInput(leafInput);
		}

		public void UpdateLeafInput(LeafInput leafInput)
		{
			_db.Entry(leafInput).State = EntityState.Modified;
			_db.SaveChanges();
		}

		#endregion

		#region LeafInputFile

		public IQueryable<LeafInputFile> GetLeafInputFiles()
		{
			return _db.LeafInputFiles;
		}

		public LeafInputFile GetLeafInputFile(int id)
		{
			return _db.LeafInputFiles.Find(id);
		}
		
		public void UpdateLeafInputFile(LeafInputFile leafInputFile)
		{
			_db.Entry(leafInputFile).State = EntityState.Modified;
			_db.SaveChanges();
		}


		#endregion

		#region Photosynthesis Types

		public IQueryable<PhotosynthesisType> GetPhotosynthesisTypes()
		{
			return _db.PhotosynthesisTypes.OrderBy(pt => pt.SortOrder);
		}

		public PhotosynthesisType GetPhotosynthesisType(string id)
		{
			return _db.PhotosynthesisTypes.Find(id);
		}

		#endregion

		#region LeafOutputFile

		public void AddLeafOutputFile(LeafOutputFile leafOutput)
		{
			_db.LeafOutputFiles.Add(leafOutput);
			_db.SaveChanges();
		}

		public IQueryable<LeafOutputFile> GetLeafOutputFiles()
		{
			return _db.LeafOutputFiles;
		}

		public LeafOutputFile GetLeafOutputFile(int id)
		{
			return _db.LeafOutputFiles.Find(id);
		}

		#endregion

	}
}
