using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Reflection;
using System.Windows;
using C1.C1Preview;
using C1.C1Schedule;
using C1.WPF.Schedule;

namespace Printing
{
	/// <summary>
	/// The <see cref="PrintContextType"/> defines the printing context for the <see cref="PrintStyle"/> object.
	/// </summary>
	[Flags]
	public enum PrintContextType
	{
		/// <summary>
		/// Document displays content of the <see cref="C1.C1Schedule.Appointment"/> object.
		/// </summary>
		Appointment = 0x01,
		/// <summary>
		/// Document displayes appointments for the specified date range.
		/// </summary>
		DateRange = 0x02
	}

	/// <summary>
	/// The object used to manage schedule printing.
	/// </summary>
	public class PrintInfo 
	{
		#region ** fields
		private C1Scheduler _schedule;
		private PrintPreviewWindow _preview = null;
		private List<Appointment> _currentAppointments = null;
		#endregion

		#region ** ctor
		/// <summary>
		/// Initializes a ne instance of the <see cref="PrintInfo"/> class.
		/// </summary>
		/// <param name="schedule"></param>
		public PrintInfo(C1Scheduler schedule)
		{
			HidePrivateAppointments = false;
			_schedule = schedule;
			PrintDocument = new C1PrintDocument();
			PrintStyles = new PrintStyleCollection();
			PrintStyles.LoadDefaults();
			PrintDocument.DocumentStarting += new EventHandler(_printDoc_DocumentStarting);
		}
		#endregion

		#region ** public properties
		/// <summary>
		/// Gets the <see cref="PrintStyleCollection"/> collection, containing all available styles of printing.
		/// </summary>
		public PrintStyleCollection PrintStyles
		{
			get;
			private set;
		}

		/// <summary>
		/// Gets the <see cref="C1PrintDocument"/> object.
		/// </summary>
		public C1PrintDocument PrintDocument
		{
			get;
			private set;
		}

		/// <summary>
		/// Gets or sets the <see cref="PrintPreviewWindow"/> object, used for previewing. 
		/// </summary>
		public PrintPreviewWindow PrintPreviewDialog
		{
			get
			{
				if (_preview == null)
				{
					_preview = new PrintPreviewWindow();
				}
				return _preview;
			}
			set
			{
				_preview = value;
			}
		}

		/// <summary>
		/// Gets a <see cref="PrintStyle"/> object which is currently selected for printing.
		/// </summary>
		[Browsable(false)]
		public PrintStyle CurrentStyle
		{
			get;
			internal set;
		}

		/// <summary>
		/// Gets or sets a <see cref="Boolean"/> value determining whether control should
		/// hide details of private appointments in resulting document.
		/// </summary>
		[DefaultValue(false)]
		public bool HidePrivateAppointments
		{
			get;
			set;
		}
		#endregion

		#region ** public methods

		/// <summary>
		/// Sends default printing style for the currently selected view to the printer.
		/// </summary>
		public void Print()
		{
			if (SetupPrintContext(null))
			{
				InternalPrint();
			}
		}

		/// <summary>
		/// Sends the specified printing style to the printer.
		/// </summary>
		/// <param name="style">The <see cref="PrintStyle"/> object to print.</param>
		public void Print(PrintStyle style)
		{
			Print(style, _schedule.VisibleDates[0], _schedule.VisibleDates[_schedule.VisibleDates.Count - 1]);
		}

		/// <summary>
		/// Sends the specified <see cref="Appointment"/> object to the printer.
		/// </summary>
		/// <param name="appointment">The <see cref="Appointment"/> object to print.</param>
		public void Print(Appointment appointment)
		{
			if (SetupPrintContext(appointment))
			{
				InternalPrint();
			}
		}

		/// <summary>
		/// Sends the specified printing style to the printer.
		/// </summary>
		/// <param name="style">The <see cref="PrintStyle"/> object to print.</param>
		/// <param name="start">The <see cref="DateTime"/> value specifying the beginning of the print range.</param>
		/// <param name="end">The <see cref="DateTime"/> value specifying the end of the print range.</param>
		public void Print(PrintStyle style, DateTime start, DateTime end)
		{
			if (style != null)
			{
				SetupPrintContext(style, start, end);
				InternalPrint();
			}
		}

		/// <summary>
		/// Opens a separate application window in which end users can preview 
		/// the output that would be generated by the print operation.
		/// </summary>
		public void Preview()
		{
			if (SetupPrintContext(null))
			{
				PreviewCurrentStyle();
			}
		}

		/// <summary>
		/// Opens a separate application window in which end users can preview 
		/// the output that would be generated by the print operation.
		/// </summary>
		/// <param name="style">The <see cref="PrintStyle"/> object to preview.</param>
		public void Preview(PrintStyle style)
		{
			Preview(style, _schedule.VisibleDates[0], _schedule.VisibleDates[_schedule.VisibleDates.Count - 1]);
		}

		/// <summary>
		/// Opens a separate application window in which end users can preview 
		/// the output that would be generated by the print operation.
		/// </summary>
		/// <param name="appointment">The <see cref="Appointment"/> object to preview.</param>
		public void Preview(Appointment appointment)
		{
			if (SetupPrintContext(appointment))
			{
				PreviewCurrentStyle();
			}
		}

		/// <summary>
		/// Opens a separate application window in which end users can preview 
		/// the output that would be generated by the print operation.
		/// </summary>
		/// <param name="style">The <see cref="PrintStyle"/> object to preview.</param>
		/// <param name="start">The <see cref="DateTime"/> value specifying the beginning of the print range.</param>
		/// <param name="end">The <see cref="DateTime"/> value specifying the end of the print range.</param>
		public void Preview(PrintStyle style, DateTime start, DateTime end)
		{
			if (style != null)
			{
				SetupPrintContext(style, start, end);
				PreviewCurrentStyle();
			}
		}

		/// <summary>
		/// Loads style definition to C1PrintDocument control.
		/// </summary>
		/// <param name="style">The <see cref="PrintStyle"/> for loading into print document object.</param>
		public void LoadStyle(PrintStyle style)
		{
			if (style.Load(PrintDocument))
			{
				CurrentStyle = style;
				AddScheduleReferences();
			}
		}
		#endregion

		#region ** private stuff
		// adds references to additional assemblies needed for document generation
        private void AddScheduleReferences()
        {
            AddExternalAssembly(Assembly.GetAssembly(typeof(C1.WPF.Schedule.C1Scheduler)).Location);
            AddExternalAssembly("WindowsBase.dll");
            string path = Assembly.GetAssembly(typeof(System.Windows.Media.Color)).Location;
            AddExternalAssembly(path);

            path = Assembly.GetAssembly(typeof(INotifyCollectionChanged)).Location;
            AddExternalAssembly(path);
        }
        /// <summary>
		/// Adds specified external assembly reference to the currently loaded document.
		/// </summary>
		/// <param name="AssemblyName">The <see cref="String"/> value specifying assembly name for adding.</param>
		private void AddExternalAssembly(string assemblyName)
		{
			StringCollection extAssemblies = PrintDocument.ScriptingOptions.ExternalAssemblies;
			if (extAssemblies != null && !extAssemblies.Contains(assemblyName))
			{
				extAssemblies.Add(assemblyName);
			}
		}		

		// shows PrintOptionsDialog and prints selected style if user clicks Ok button
		private void InternalPrint()
		{
			// setup print context
			PrintContextType context = PrintContextType.DateRange;
			if (_schedule.SelectedAppointment != null)
			{
				context |= PrintContextType.Appointment;
			}
			// show PrintOptionsDialog
			PrintOptionsDialog form = new PrintOptionsDialog(this, CurrentStyle, context);
			form.StartDate = _schedule.VisibleDates[0];
			form.EndDate = _schedule.VisibleDates[_schedule.VisibleDates.Count - 1];
			bool? result = form.ShowDialog();
			if (result.HasValue && result.Value)
			{
				// user clicks Ok
				// set PrintStyle properties according to user settings in a dialog
				CurrentStyle = form.SelectedStyle;
				HidePrivateAppointments = form.HidePrivateAppointments;
				SetupPrintContext(CurrentStyle, form.StartDate, form.EndDate);
				// print the document
				PrintDocument.Print(new System.Drawing.Printing.PrinterSettings());
				_currentAppointments = null;
			}
		}

		// initialize printing context for the specified appointment
		private bool SetupPrintContext(Appointment appointment)
		{
			if (appointment != null)
			{
				_currentAppointments = new List<Appointment>();
				_currentAppointments.Add(appointment);
			}
			else if ( _schedule.SelectedAppointment != null )
			{
				_currentAppointments = new List<Appointment>();
				_currentAppointments.Add(_schedule.SelectedAppointment);
			}
			if (PrintStyles.Contains("Memo") && 
				( _currentAppointments != null && _currentAppointments.Count > 0))
			{
				CurrentStyle = PrintStyles["Memo"];
			}
			else
			{
				int daysNumber = _schedule.VisibleDates.Count;
				if (daysNumber > 14 && PrintStyles.Contains("Month"))
				{
					CurrentStyle = PrintStyles["Month"];
				}
				else if (daysNumber == 7 && PrintStyles.Contains("Week"))
				{
					CurrentStyle = PrintStyles["Week"];
				}
				else if (PrintStyles.Contains("Daily"))
				{
					CurrentStyle = PrintStyles["Daily"];
				}
				else if (PrintStyles.Contains("Details"))
				{
					CurrentStyle = PrintStyles["Details"];
				}
				else
				{
					CurrentStyle = PrintStyles[0];
				}
			}
			if (CurrentStyle == null)
			{
				return false;
			}
			SetupPrintContext(CurrentStyle, _schedule.VisibleDates[0], _schedule.VisibleDates[_schedule.VisibleDates.Count - 1]);
			return true;
		}

		// on document starting show tag input form to the user and initialize document tags
		private void _printDoc_DocumentStarting(object sender, EventArgs e)
		{
			DateTime start = _schedule.VisibleDates[0].Date;
			DateTime end = _schedule.VisibleDates[_schedule.VisibleDates.Count - 1].Date;
			CalendarInfo calendarInfo = _schedule.CalendarHelper.Info;

			// Show tag input form to end-user.
			// This is a default form shown by the C1PrintDocument control. 
			// You can create your own form for this purpose, show it, and set C1PrintDocumentTags
			// according to the user's input.
			PrintDocument.EditTags();

			CurrentStyle.SetupTags(PrintDocument, _schedule.DataStorage.AppointmentStorage.Appointments,
				_currentAppointments, start, end, HidePrivateAppointments, calendarInfo);
		}

		// load specified PrintStyle into the PrintDocument control and initialize date range
		internal void SetupPrintContext(PrintStyle style, DateTime start, DateTime end)
		{
			LoadStyle(style);
			style.SetDateRangeTags(PrintDocument, start, end);
		}

		/// <summary>
		/// Shows current style in a print preview dialog.
		/// </summary>
		private void PreviewCurrentStyle()
		{
			// generate the document
			if (PrintPreviewDialog != null)
			{
				_preview.Document = PrintDocument.FixedDocumentSequence;
				_preview.ShowDialog();
				_preview.Activate();
				_preview = null;
			}
			_currentAppointments = null;
		}
		#endregion
	}
}
