using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.ServiceModel;

using BeWo.Data;
using BeWo.Data.Access;
using BeWo.Data.Entities;
using BeWo.Report;
using BeWo.Report.DefaultReports;
using BeWo.Report.ReportObjects;
using BeWo.Service.DCEntityMapper;
using BeWo.Service.Plugins;

using BS.Shared;
using BS.Shared.Core;
using BS.Shared.DataContracts;
using BS.Shared.DataContracts.Compact;

using DevExpress.XtraPrinting;
using DevExpress.XtraReports.UI;

using ReportingService.ServiceContracts;

namespace ReportingService.ServiceImplementations
{
	[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Single)]
	public class ReportServiceImp : IReportService
	{
		public MemoryStream CreateKalenderReport(IEnumerable<long> appointmentOids, long? employeeOid, IEnumerable<SchedulerAppointmentDC> serientermine, IEnumerable<DateTime> selectedDates, IEnumerable<SchedulerAppointmentDC> mehrtaegigeTermine)
		{
			return CreateReportStream(new DailyAppointmentReportKalender(), KalenderRO.Create(appointmentOids, employeeOid, serientermine, selectedDates, mehrtaegigeTermine));
		}

        public MemoryStream CreateKassenbuchReport(DateTime zeitraum, BargeldkassenDC bargeldkasse, long objectOid, TableID objectTid)
		{
            var beguenstigter = LoadToBargeldkasseRelatedObject(objectTid, objectOid);

            return CreateReportStream(new KassenbuchReport(), KassenbuchRO.Create(zeitraum, bargeldkasse, beguenstigter));
		}

        public MemoryStream CreateKassenbelegReport(BargeldtransaktionDC transaktion, long objectOid, TableID objectTid)
        {
            var beguenstigter = LoadToBargeldkasseRelatedObject(objectTid, objectOid);

            return CreateReportStream(new KassenbelegReport(), KassenbelegRO.Create(transaktion, beguenstigter));
		}

		public MemoryStream CreateServiceRecordListReport(long employeeOid, long customerOid, long supportConceptOid, long costbearer2SupportConceptOid, bool isLimited, DateTimeSpan limitingDateTimeSpan)
		{
			var reportCreator = GetReportCreator();

			return CreateReportStream(reportCreator.CreateServiceRecordListReport(employeeOid, customerOid, supportConceptOid, costbearer2SupportConceptOid, isLimited, limitingDateTimeSpan));
		}

		public Dictionary<CompactEmployeeDC, decimal> GetMitarbeiterverfuegbarkeiten(List<CompactEmployeeDC> mitarbeiter, DateTime startDatum, DateTime endDatum)
		{
			var herausgefilterteMitarbeiter = DAOFactory.SearchDAO.FilterEmployeesWithAppointments(mitarbeiter.Select(ma => ma.EmployeeOid).ToList(), startDatum, endDatum);
			mitarbeiter = mitarbeiter.Where(ma => !herausgefilterteMitarbeiter.Contains(ma.EmployeeOid)).ToList();

			var berechnungsBeginn = new DateTime(startDatum.Year, startDatum.Month, 1).AddMonths(-1);
			var berechnungsEnde = new DateTime(startDatum.Year, startDatum.Month, 1);

			var msb = new MitarbeiterstundenkontoBerechnung();
			var reportObject = MitarbeiterstundenkontoRO.Create(null, null, berechnungsBeginn, berechnungsEnde, mitarbeiter.Select(ma => ma.EmployeeOid));
			msb.CreateReport(reportObject);

			return reportObject.EmployeeDetailList
				.Where(employeeDetail => employeeDetail.Employee.Oid != null)
				.ToDictionary(employeeDetail => MapperFactory.CompactEmployeeDC_Employee.MapToNewDC(employeeDetail.Employee), 
							  employeeDetail => employeeDetail.UeberstundenGesamt == null ? 0 : employeeDetail.UeberstundenGesamt.Value);
		}

		private static MemoryStream CreateReportStream(XtraReport pReport)
		{
			pReport.CreateDocument();

			var resultStream = new MemoryStream();
			var repxStream = new MemoryStream();

			pReport.PrintingSystem.SaveDocument(repxStream, new NativeFormatOptions { Compressed = true });

			var bytesToWrite = repxStream.ToArray();

			resultStream.Write(bytesToWrite, 0, bytesToWrite.Length);

			resultStream.Position = 0L;

			return resultStream;
		}

		private static MemoryStream CreateReportStream<T, U>(T pReport, U pRO)
			where T : XtraReport, IBeWoReport<U>
			where U : AbstractBeWoReportObject<U>
		{
			var report = pReport;

			report.SetReportDataSource(pRO);
			report.CreateDocument();

			var resultStream = new MemoryStream();
			var repxStream = new MemoryStream();

			report.PrintingSystem.SaveDocument(repxStream, new NativeFormatOptions { Compressed = true });

			var bytesToWrite = repxStream.ToArray();

			resultStream.Write(bytesToWrite, 0, bytesToWrite.Length);

			resultStream.Position = 0L;

			return resultStream;
		}

		private static AbstractReportCreator GetReportCreator()
		{
			var derTenant = MultitenancyOperationContextExt.Current.Tenant;
			AbstractReportCreator creator = null;
#if DEBUG
			//Kundennummern zum Testen nur noch in PluginLoader eintragen:
			derTenant = PluginLoader.Tenant;


			var customerDllPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"bin\" + derTenant + "_reports.dll");
#else
			var customerDllPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"Customer\" + derTenant + "_reports.dll");
#endif
			if (File.Exists(customerDllPath))
			{
				var allTypes = Assembly.LoadFrom(customerDllPath).GetTypes();

				var reportCreator = allTypes.FirstOrDefault(i => typeof(AbstractReportCreator).IsAssignableFrom(i));
				if (reportCreator != null)
				{
					creator = Activator.CreateInstance(reportCreator) as AbstractReportCreator;
				}

			}

			if (creator == null)
				creator = new DefaultReportCreator();

			creator.Tenant = derTenant;
			creator.ReportPath = customerDllPath;

#if DEBUG
			creator.TempPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"demo\temp");
#else

            creator.TempPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, derTenant + @"\temp");
#endif

			return creator;
		}

	    private static BeWoEntityBase LoadToBargeldkasseRelatedObject(TableID pObjectTid, long pObjectOid)
	    {
	        switch (pObjectTid)
	        {
	            case TableID.Customer:
	                return DAOFactory.GenericDAO.LoadByID<Customer>(pObjectOid);
                default:
	                return null;
	        }
	    }
	} 
}