using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using BeWo.Data.Access;
using BeWo.Data.Entities;
using BeWo.Service.DCEntityMapper;
using BS.Shared.DataContracts;
using BS.Shared.Extensions;

namespace BeWo.Report.ReportObjects
{
	public class KalenderRO : AbstractBeWoReportObject<KalenderRO>
	{
		public string                         PrintingEmployee     { get; set; }
		public List<SchedulerAppointment>     Appointments         { get; set; }
		public List<KalenderTag>              KalenderTageSortiert { get; set; }
		public Dictionary<int, KalenderWoche> Wochen               { get; set; }
		public String                         PrintingDate         { get { return DateTime.Now.ToShortDateString(); } }

		public static KalenderRO Create(IEnumerable<long> appointmentOids, long? employeeOid, IEnumerable<SchedulerAppointmentDC> serientermine, IEnumerable<DateTime> selectedDates, IEnumerable<SchedulerAppointmentDC> mehrtaegigeTermine)
		{
			var appointments = DAOFactory.GenericDAO.LoadByIDs<SchedulerAppointment>(appointmentOids);
			var employeeName = DAOFactory.GenericDAO.GetByID<Employee>(employeeOid.Value).Person.FirstNameLastName;
			var kalenderTage = new HashSet<KalenderTag>();
			var wochen       = new Dictionary<int, KalenderWoche>();

			var mapper   = new SchedulerAppointmentDC_SchedulerAppointment();
			var objekte  = mapper.MapToNewEntities(serientermine);
			var objekte2 = mapper.MapToNewEntities(mehrtaegigeTermine);

			appointments.AddRange(objekte2);
			appointments.AddRange(objekte);
			appointments = appointments.Where(w => (w.StartDate != null && selectedDates.Any(a => a.CompareShortDates(w.StartDate.Value)))).OrderBy(o => o.StartDate).ToList();

			// ganztägige Termine beachten
			foreach (var app in appointments)
			{
				var datum = app.StartDate.Value.ToString("dd. MMMM yyyy");
				
				if (!kalenderTage.Any(k => k.Datum.Equals(datum)))
				{
					kalenderTage.Add(new KalenderTag(new List<AppointmentStruct> {new AppointmentStruct {Appointment = app}}));
				}
				else
				{
					var kt = kalenderTage.First(f => f.Datum.Equals(datum));
					kt.Termine.Add(new AppointmentStruct { Appointment = app });
				}
			}

			Calendar c = new GregorianCalendar();

			foreach (var kTag in kalenderTage)
			{
				var wt = c.GetWeekOfYear(kTag.Date, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
				if (wochen.ContainsKey(wt))
				{
					wochen[wt].AddDayToWeek(kTag);
				}
				else
				{
					wochen.Add(wt, new KalenderWoche(wt, kTag));
				}
			}

			return new KalenderRO
			{
				Appointments         = appointments,
				PrintingEmployee     = employeeName,
				KalenderTageSortiert = kalenderTage.ToList().OrderBy(o=>o.Date).ToList(),
				Wochen               = wochen
			};
		}
	}

	public class KalenderWoche
	{
		public KalenderTag Montag { get; set; }
		public KalenderTag Dienstag { get; set; }
		public KalenderTag Mittwoch { get; set; }
		public KalenderTag Donnerstag { get; set; }
		public KalenderTag Freitag { get; set; }
		public KalenderTag Samstag { get; set; }
		public KalenderTag Sonntag { get; set; }

		public int Wochennummer { get; set; }

		public KalenderWoche(int wochennummer, KalenderTag kalenderTag)
		{
			Wochennummer = wochennummer;
			var eWt      = kalenderTag.Date.FirstDateOfWeek(wochennummer);
			Montag       = new KalenderTag(new List<AppointmentStruct>()) { Datum = eWt.ToShortDateString(), Date = eWt };
			Dienstag     = new KalenderTag(new List<AppointmentStruct>()) { Datum = eWt.AddDays(1).ToShortDateString(), Date = eWt.AddDays(1) };
			Mittwoch     = new KalenderTag(new List<AppointmentStruct>()) { Datum = eWt.AddDays(2).ToShortDateString(), Date = eWt.AddDays(2) };
			Donnerstag   = new KalenderTag(new List<AppointmentStruct>()) { Datum = eWt.AddDays(3).ToShortDateString(), Date = eWt.AddDays(3) };
			Freitag      = new KalenderTag(new List<AppointmentStruct>()) { Datum = eWt.AddDays(4).ToShortDateString(), Date = eWt.AddDays(4) };
			Samstag      = new KalenderTag(new List<AppointmentStruct>()) { Datum = eWt.AddDays(5).ToShortDateString(), Date = eWt.AddDays(5) };
			Sonntag      = new KalenderTag(new List<AppointmentStruct>()) { Datum = eWt.AddDays(6).ToShortDateString(), Date = eWt.AddDays(6) };

			AddDayToWeek(kalenderTag);
		}

		public void AddDayToWeek(KalenderTag day)
		{
			switch (day.Date.DayOfWeek)
			{
				case DayOfWeek.Monday:
					Montag = day;
					break;
				case DayOfWeek.Tuesday:
					Dienstag = day;
					break;
				case DayOfWeek.Wednesday:
					Mittwoch = day;
					break;
				case DayOfWeek.Thursday:
					Donnerstag = day;
					break;
				case DayOfWeek.Friday:
					Freitag = day;
					break;
				case DayOfWeek.Saturday:
					Samstag = day;
					break;
				case DayOfWeek.Sunday:
					Sonntag = day;
					break;
			}
		}
	}

	public class KalenderTag
	{
		public string                  Datum   { get; set; }
		public DateTime                Date    { get; set; }
		public List<AppointmentStruct> Termine { get; set; }

		public KalenderTag(List<AppointmentStruct> termine)
		{
			Termine = termine;
			Datum = termine.Count > 0 ? termine.First().Appointment.StartDate.Value.ToString("dd. MMMM yyyy") : string.Empty;
			if (termine.Count > 0)
			{
				var d = termine.First().Appointment.StartDate.Value;
				Date = new DateTime(d.Year, d.Month, d.Day);
			}
		}
	}

	public struct AppointmentStruct
	{
		public string VonBisString
		{
			get
			{
				var result = String.Empty;

				if (Appointment.AllDay)
				{
					result = "Ganztägig";
				}
				else
				{
					result = Appointment.StartDate.HasValue ? String.Format("{0} - {1}", Appointment.StartDate.Value.ToShortTimeString(), Appointment.EndDate.Value.ToShortTimeString()) : string.Empty;
				}

				return result;
			}
		}

		public string SubjectLocationString
		{
			get
			{
				if (!String.IsNullOrEmpty(Appointment.Location))
					return String.Format("{0} ({1})", Appointment.Subject, Appointment.Location);
				return Appointment.Subject;
			}
		}

		public SchedulerAppointment Appointment { get; set; }
	}
}