using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security;
using System.Web;
using BeWo.Report.DefaultReports;
using BS.Shared;
using BeWo.Data;
using BeWo.Data.Access;
using BeWo.Data.Entities;
using BeWo.Report;
using BeWo.Report.ReportObjects;
using BeWo.Service.DCEntityMapper;

using BS.Shared.Core;
using BS.Shared.DataContracts;
using DevExpress.XtraReports.UI;

namespace BeWo.Report
{
	public class DefaultReportCreator : AbstractReportCreator
	{
		protected IBeWoReport<T> FindReportImp<T>()
		{
			return FindReportImp<T>(null, null);
		}

		protected IBeWoReport<T> FindReportImp<T>(string specificReportId)
		{
			return this.FindReportImp<T>(specificReportId, null);
		}

		protected IBeWoReport<T> FindReportImp<T>(string specificReportId, string typename)
		{
			IBeWoReport<T> result = null;

			Type[] lAllReportTypes = null;

			if (Tenant != null && File.Exists(ReportPath))
			{
				lAllReportTypes = Assembly.LoadFrom(ReportPath).GetTypes();
				result = FindReportImp<T>(lAllReportTypes, specificReportId, typename, true);
			}

			
			if (result == null)
			{
				lAllReportTypes = typeof(IBeWoReport<T>).Assembly.GetTypes();
				result = FindReportImp<T>(lAllReportTypes, specificReportId, typename, false);
			}

			return result;
		}

		protected IBeWoReport<T> FindReportImp<T>(Type[] lAllReportTypes, string specificReportId, string typename, bool doNotUseReportsWithIdentifier)
		{
			IBeWoReport<T> result = null;

			if (!string.IsNullOrEmpty(specificReportId) && !string.IsNullOrEmpty(typename))
			{
				var cbSpecific = lAllReportTypes
					.Where(i => typeof(IBeWoReport<T>).IsAssignableFrom(i))
					.FirstOrDefault(t => t.GetCustomAttributes(typeof(IDSpecificClassAttribute), true)
											 .ToList()
											 .Exists(i =>
											 {
												 var attr = (IDSpecificClassAttribute)i;
												 if (attr.Identifiers != null)
													 return attr.Identifiers.Contains(specificReportId);
												 if (!String.IsNullOrEmpty(attr.Identifier))
													 return attr.Identifier.Equals(specificReportId);
												 return false;

											 }
											 ) && t.FullName == typename);
				if (cbSpecific != null)
					result = Activator.CreateInstance(cbSpecific) as IBeWoReport<T>;
			}

			if (!string.IsNullOrEmpty(typename))
			{
				var cbSpecific = lAllReportTypes.FirstOrDefault(t => t.FullName == typename);
				if (cbSpecific != null)
					result = Activator.CreateInstance(cbSpecific) as IBeWoReport<T>;
			}

			if (!string.IsNullOrEmpty(specificReportId))
			{
				var cbSpecific = lAllReportTypes
					.Where(i => typeof(IBeWoReport<T>).IsAssignableFrom(i))
					.FirstOrDefault(t => t.GetCustomAttributes(typeof(IDSpecificClassAttribute), true)
											 .ToList()
											 .Exists(i =>
											 {
												 var attr = (IDSpecificClassAttribute)i;
												 if (attr.Identifiers != null)
													 return attr.Identifiers.Contains(specificReportId);
												 if (!String.IsNullOrEmpty(attr.Identifier))
													 return attr.Identifier.Equals(specificReportId);
												 return false;

											 }
											 ));
				if (cbSpecific != null)
					result = Activator.CreateInstance(cbSpecific) as IBeWoReport<T>;
			}

			if (result == null)
			{
				foreach (var iType in lAllReportTypes)
					if (typeof(IBeWoReport<T>).IsAssignableFrom(iType))
						if (iType.Name == specificReportId)
							result = Activator.CreateInstance(iType) as IBeWoReport<T>;
			}

			if (result == null)
			{
				var list = lAllReportTypes.Where(t => typeof(IBeWoReport<T>).IsAssignableFrom(t)).ToList();

				if (list.Count == 1)
				{
					var type = list[0];
					if (doNotUseReportsWithIdentifier)
					{
						var attrs = type.GetCustomAttributes(typeof(IDSpecificClassAttribute), true);
						if (attrs.Length == 0)
							return Activator.CreateInstance(type) as IBeWoReport<T>;
					}
					else
					{
						result = Activator.CreateInstance(type) as IBeWoReport<T>;	
					}
					
				}
				else if (list.Count > 1)
				{
					foreach (var type in list)
					{
						var attrs = type.GetCustomAttributes(typeof (IDSpecificClassAttribute), true);
						if (attrs.Length == 0)
							return Activator.CreateInstance(type) as IBeWoReport<T>;
					}
					if (!doNotUseReportsWithIdentifier)
					{
						result = Activator.CreateInstance(list[0]) as IBeWoReport<T>;
					}
				}

			}

			return result;
		}

		public override XtraReport CreateCustomerDataReport(long customerOid)
		{
			var lCustomerDataReport = FindReportImp<CustomerDataRO>();
			lCustomerDataReport.SetReportDataSource(CustomerDataRO.Create(MapperFactory.CustomerDC_Customer.MapToNewDC(DAOFactory.GenericDAO.LoadByID<Customer>(customerOid))));
			return lCustomerDataReport as XtraReport;
		}

		public override XtraReport CreateCustomerInvoiceReport(long invoiceOid)
		{

			IBeWoReport<InvoiceCustomerRO> lInvoiceCustomerReport = FindReportImp<InvoiceCustomerRO>();
			lInvoiceCustomerReport.SetReportDataSource(InvoiceCustomerRO.Create(MapperFactory.InvoiceBaseDC_InvoiceBase.MapToNewDC(DAOFactory.GenericDAO.LoadByID<InvoiceBase>(invoiceOid))));
			return lInvoiceCustomerReport as XtraReport;
		}

		public override XtraReport CreateAssessmentSheetReport(long customerOid, int month, int year)
		{
			IBeWoReport<AssessmentSheetRO> report = FindReportImp<AssessmentSheetRO>();
			report.SetReportDataSource(AssessmentSheetRO.Create(DAOFactory.GenericDAO.LoadByID<Customer>(customerOid), new DateTime(year, month, 1)));
			return report as XtraReport;
		}

		public override XtraReport CreateFlsReport(String flsReportId, String subType)
		{
			string flsReportPath = Path.Combine(TempPath, flsReportId + ".xml");
			var lFLSAnalysisDataReportDC = Utils.XMLDeserialize<FLSAnalysisDataReportDC>(flsReportPath);


			IBeWoReport<FLSReportRO> lFLSReportObject = null;

			if (!String.IsNullOrEmpty(subType))
				lFLSReportObject = this.FindReportImp<FLSReportRO>(subType);

			if (lFLSReportObject == null)
				lFLSReportObject = this.FindReportImp<FLSReportRO>(lFLSAnalysisDataReportDC.ShowGroups
																							? "FLSAuswertung"
																							: "Leistungsdokumentation");

			lFLSReportObject.SetReportDataSource(FLSReportRO.Create(lFLSAnalysisDataReportDC));
			return lFLSReportObject as XtraReport;
		}

		public override XtraReport CreateQueryReport(long queryOid, String queryReportId)
		{
			var query = DAOFactory.GenericDAO.LoadByID<Query>(queryOid);

			if (query == null)
				return new XtraReport();
			
			var path = Path.Combine(TempPath, queryReportId + ".xml");

			var table = Utils.XMLDeserialize<DataTable>(path);

			var queryReport = FindReportImp<QueryRO>(null, query.ReportTypeName);
			queryReport.SetReportDataSource(QueryRO.Create(query, table));

			return queryReport as XtraReport;
		}

		public override XtraReport CreateServiceInvoiceReport(String dcIds, long? invoiceBaseOid)
		{
			if (String.IsNullOrEmpty(dcIds) && invoiceBaseOid.HasValue)
			{
				var serviceInvoice = DAOFactory.SearchDAO.GetServiceInvoiceByInvoiceBaseOid(invoiceBaseOid.Value);
				dcIds = String.Format("{0}", serviceInvoice.Oid);
			}

			if (!string.IsNullOrEmpty(dcIds))
			{
				var serviceInvoiceOid2CostbearerId = new Dictionary<long, string>();
				var dcList = new List<ServiceInvoiceDC>();

				var dcidStrs = dcIds.Split(';');
				foreach (var oidStr in dcidStrs)
				{
					var oid = long.Parse(oidStr);
					var invoice = DAOFactory.GenericDAO.LoadByID<ServiceInvoice>(oid);
					var dc = MapperFactory.ServiceInvoiceDC_ServiceInvoice.MapToNewDC(invoice);
					dcList.Add(dc);

					if (serviceInvoiceOid2CostbearerId.ContainsKey(oid))
						continue;

					if (!String.IsNullOrEmpty(invoice.InvoiceBase.InvoiceId))
						serviceInvoiceOid2CostbearerId.Add(oid, invoice.InvoiceBase.InvoiceId);
					else
					{
						foreach (var period in invoice.ServiceInvoicePeriodList)
						{
							try
							{
								if (period.SupportConceptApprovalPeriod != null)
								{
									AddCostBearerIdToDict(oid, serviceInvoiceOid2CostbearerId,
										period.SupportConceptApprovalPeriod.CostBearer2SupportConcept);

								}
								foreach (var item in period.InvoiceItemList)
								{
									if (item.SupportConceptApprovalPeriodOid.HasValue)
									{
										var approvalPeriod =
											DAOFactory.GenericDAO.LoadByID<SupportConceptApprovalPeriod>(item.SupportConceptApprovalPeriodOid.Value);
										if (approvalPeriod.CostBearer2SupportConcept != null)
										{
											AddCostBearerIdToDict(oid, serviceInvoiceOid2CostbearerId, approvalPeriod.CostBearer2SupportConcept);
										}
									}
								}
							}
							catch (Exception)
							{
								//ignore
							}
							
						}
					}
				}

				IBeWoReport<ServiceInvoiceRO> allReports = null;

				foreach (var iDC in dcList)
				{
					String reportId = null;
					if (serviceInvoiceOid2CostbearerId.ContainsKey(iDC.ServiceInvoiceOid.Value))
						reportId = serviceInvoiceOid2CostbearerId[iDC.ServiceInvoiceOid.Value];

					IBeWoReport<ServiceInvoiceRO> singleReport = FindReportImp<ServiceInvoiceRO>(reportId);

					singleReport.SetReportDataSource(ServiceInvoiceRO.Create(iDC));
					((XtraReport)singleReport).CreateDocument();
					if (allReports == null)
						allReports = singleReport;
					else
						((XtraReport)allReports).Pages.AddRange(((XtraReport)singleReport).Pages);
				}

				return allReports as XtraReport;
			}
			return new XtraReport();
		}

		private void AddCostBearerIdToDict(long serviceInvoiceOid, Dictionary<long, string> serviceInvoiceOid2CostbearerId, CostBearer2SupportConcept c2s)
		{
			if (c2s != null && c2s.CostBearer != null)
			{
				var id = c2s.CostBearer.ID;
				if (String.IsNullOrEmpty(id) && c2s.CostBearer.IsSingleInvoicePerCustomer)
					id = "Sammelrechnung";
				if (!serviceInvoiceOid2CostbearerId.ContainsKey(serviceInvoiceOid))
					serviceInvoiceOid2CostbearerId.Add(serviceInvoiceOid, id);
			}
		}

		public override XtraReport CreateServiceOverviewAllCustomersReport(int month, int year, long orgaOid, long empOid, long? serviceCategoryOid, int startDay, int endDay)
		{
			List<ServicesOverviewRO> lROs = ServicesOverviewRO.Create(month, year, orgaOid, empOid, startDay, endDay, serviceCategoryOid);

			if (lROs.Count > 0)
			{
				String reportId = null;
				if (orgaOid > 0)
				{
					var organisation = DAOFactory.GenericDAO.LoadByID<Organisation>(orgaOid);
					if (organisation != null)
						reportId = organisation.Name;
				}
				var lServiceOverviewAllCustomersReport = this.FindReportImp<ServicesOverviewRO>(reportId);
				lServiceOverviewAllCustomersReport.SetReportDataSource(lROs[0]);
				(lServiceOverviewAllCustomersReport as XtraReport).CreateDocument();
				var Report = lServiceOverviewAllCustomersReport as XtraReport;

				for (int i = 1; i < lROs.Count; i++)
				{
					var lNext = FindReportImp<ServicesOverviewRO>(reportId);
					lNext.SetReportDataSource(lROs[i]);
					(lNext as XtraReport).CreateDocument();
					Report.Pages.AddRange((lNext as XtraReport).Pages);
				}

				return lServiceOverviewAllCustomersReport as XtraReport;
			}
			return new XtraReport();
		}

		public override XtraReport CreateServiceOverviewSingleCustomerReport(long customerOid, int month, int year, long orgaOid, long empOid, long? serviceCategoryOid, int startDay, int endDay)
		{
			String reportId = null;
			if (orgaOid > 0)
			{
				var organisation = DAOFactory.GenericDAO.LoadByID<Organisation>(orgaOid);
				if (organisation != null)
					reportId = organisation.Name;
			}

			var lServiceOverviewSingleCustomerReport = this.FindReportImp<ServicesOverviewRO>(reportId);
			lServiceOverviewSingleCustomerReport.SetReportDataSource(ServicesOverviewRO.Create(customerOid, month, year, false, orgaOid, empOid, startDay, endDay, serviceCategoryOid));

			return lServiceOverviewSingleCustomerReport as XtraReport;
		}

		public override XtraReport CreateServiceRecordListReport(long employeeOid, long customerOid, long supportConceptOid, long costbearer2SupportConceptOid, bool isLimitedToAdditionalServices, DateTimeSpan limitingTimeSpan)
		{
		
			IBeWoReport<ServiceRecordListRO> lServiceRecordListReport = FindReportImp<ServiceRecordListRO>();
			lServiceRecordListReport.SetReportDataSource(ServiceRecordListRO.Create(employeeOid, customerOid, supportConceptOid, costbearer2SupportConceptOid, isLimitedToAdditionalServices, limitingTimeSpan));

			return lServiceRecordListReport as XtraReport;
		}

		public override XtraReport CreateSettlementReport(String dcId, long? invoiceBaseOid)
		{
			Settlement2DC lSettlementDC;

			long result;

			if (string.IsNullOrEmpty(dcId) && invoiceBaseOid.HasValue)
			{
				lSettlementDC = MapperFactory.SettlementDC_SettlementInvoice.MapToNewDC(DAOFactory.SearchDAO.GetSettlementInvoiceByInvoiceBaseOid(invoiceBaseOid.Value));
			}
			else if (Int64.TryParse(dcId, out result))
			{
				lSettlementDC = MapperFactory.SettlementDC_SettlementInvoice.MapToNewDC(DAOFactory.GenericDAO.LoadByID<SettlementInvoice>(result)); // = Utils.XMLDeserialize<SettlementDC>(lPath);
			}
			else
			{
				string lPath = Path.Combine(TempPath, dcId);
				lSettlementDC = Utils.XMLDeserialize<Settlement2DC>(lPath);
			}
			String reportId = null;
			if (lSettlementDC.CostBearer2SupportConceptOid.HasValue)
			{
				CostBearer2SupportConcept c2s = DAOFactory.GenericDAO.LoadByID<CostBearer2SupportConcept>(lSettlementDC.CostBearer2SupportConceptOid.Value);
				reportId = c2s.CostBearer.ID;
			}

			if (lSettlementDC.DifferentHourlyRateCount > 0)
			{
				IBeWoReport<SettlementRO> lSettlementReport = FindReportImp<SettlementRO>(reportId);
				lSettlementReport.SetReportDataSource(SettlementRO.Create(lSettlementDC));

				return lSettlementReport as XtraReport;
			}
			else
			{
				IBeWoReport<Settlement2RO> lSettlementReport = FindReportImp<Settlement2RO>(reportId);
				lSettlementReport.SetReportDataSource(Settlement2RO.Create(lSettlementDC));

				return lSettlementReport as XtraReport;
			}
		}

		public override XtraReport CreateSupportConceptReport(long employeeOid, bool onlyMySupportConcepts, bool archivedSupportConcepts, DateTime? appointedDate)
		{
			var lSupportConceptReport = FindReportImp<SupportConceptListRO>();
			lSupportConceptReport.SetReportDataSource(SupportConceptListRO.Create(employeeOid, onlyMySupportConcepts, archivedSupportConcepts, appointedDate));

			return lSupportConceptReport as XtraReport;
		}

		public override XtraReport CreateAdditionalServiceBookingReport(long addServiceRegionOid, Monat month, int year)
		{
			var report = FindReportImp<AdditionalServiceBookingRO>();
			var x =
				AdditionalServiceBookingRO.Create(
					DAOFactory.GenericDAO.LoadByID<AdditionalServiceRegion>(addServiceRegionOid), month, year);
			report.SetReportDataSource(x);

			return report as XtraReport;
		}

		public override XtraReport CreateEmployeeHourReport(long? employeeOid, long? teamOid, DateTime startDate, DateTime endDate, bool splitTeams)
		{
			IBeWoReport<MitarbeiterstundenkontoRO> report;
			if (splitTeams)
				report = FindReportImp<MitarbeiterstundenkontoRO>("Teamstundenkonto");
			else
				report = FindReportImp<MitarbeiterstundenkontoRO>("Mitarbeiterstundenkonto");
			var x =
				MitarbeiterstundenkontoRO.Create(employeeOid, teamOid, startDate, endDate, null);
			report.SetReportDataSource(x);

			return report as XtraReport;
		}

		public override XtraReport CreateAuslastungReport(long? employeeOid, long? teamOid, DateTime startDate, DateTime endDate, bool splitTeams)
		{
			IBeWoReport<MitarbeiterauslastungRO> report;
			if (splitTeams)
				report = FindReportImp<MitarbeiterauslastungRO>("Teamauslastung");
			else
				report = FindReportImp<MitarbeiterauslastungRO>("Mitarbeiterauslastung");

			var x = MitarbeiterauslastungRO.Create(employeeOid, teamOid, startDate, endDate);
			report.SetReportDataSource(x);

			return report as XtraReport;
		}

		public override XtraReport CreateMediVerordListenReport(long? mvlOid)
		{
			var report = FindReportImp<MedVerListRO>();
			
			report.SetReportDataSource(MedVerListRO.Create(mvlOid));

			return report as XtraReport;
		}

		public override XtraReport CreateMediVerordListenMZfReport(long? mvlOid, bool mitZusatzfeldern)
		{
			var report = FindReportImp<MedVerListRO>();

			report.SetReportDataSource(MedVerListRO.Create(mvlOid, mitZusatzfeldern));

			return report as XtraReport;
		}

		public override XtraReport CreateKalenderReport(long? employeeOid, AppointmentViewType viewType, DateTime rangeStartDate, DateTime rangeEndDate, List<DateTime> datesList, List<UserRightType> userRights)
		{
			var report = new DailyAppointmentReportKalender();

			//switch (viewType)
			//{
			//	case AppointmentViewType.Week:
			//		var r = new WeeklyAppointmentReport();
			//		r.SetReportDataSource(KalenderRO.Create(employeeOid, viewType, rangeStartDate, rangeEndDate, datesList));
			//		return r;
			//}
			//throw new NotImplementedException("Wird umgebaut");
			//report.SetReportDataSource(KalenderRO.Create(employeeOid, viewType, rangeStartDate, rangeEndDate, datesList, userRights));

			return report;
		}

		public override XtraReport CreateFinanceReport(DateTime? start, DateTime? end)
		{
			var report = FindReportImp<FinanceRO>();

			report.SetReportDataSource(FinanceRO.Create(start, end));

			return report as XtraReport;
		}

        public override XtraReport CreateJahresberichtReport(int year, long? cbOid, long? teamOid, long? betreuungsartOid)
        {
            var report = FindReportImp<JahresReportRO>();

            report.SetReportDataSource(JahresReportRO.Create(year, cbOid, teamOid, betreuungsartOid));

            return report as XtraReport;
        }
	}
}