///////////////////////////////////////////////////////////////////////////////
//
// (C) 2020 ICE TEA GROUP LLC - ALL RIGHTS RESERVED
//
// 
//
// ALL INFORMATION CONTAINED HEREIN IS, AND REMAINS
// THE PROPERTY OF ICE TEA GROUP LLC AND ITS SUPPLIERS, IF ANY.
// THE INTELLECTUAL PROPERTY AND TECHNICAL CONCEPTS CONTAINED
// HEREIN ARE PROPRIETARY TO ICE TEA GROUP LLC AND ITS SUPPLIERS
// AND MAY BE COVERED BY U.S. AND FOREIGN PATENTS, PATENT IN PROCESS, AND
// ARE PROTECTED BY TRADE SECRET OR COPYRIGHT LAW.
//
// DISSEMINATION OF THIS INFORMATION OR REPRODUCTION OF THIS MATERIAL
// IS STRICTLY FORBIDDEN UNLESS PRIOR WRITTEN PERMISSION IS OBTAINED
// FROM ICE TEA GROUP LLC.
//
///////////////////////////////////////////////////////////////////////////////

using System;
using System.ComponentModel;
using System.Drawing.Printing;
using System.IO;
using System.Net.Mime;
using Wisej.Core;

namespace Wisej.Web.Ext.PrintPreview
{
	/// <summary>
	/// Extends the <see cref="PdfViewer"/> to use the built-in pdfjs and
	/// to manage temp PDF files generated by the <see cref="Document"/>.
	/// </summary>
	internal class PrintPreviewPdf : PdfViewer, IWisejHandler
	{
		private const string PDF_VIEWER_VERSION = "2_6_347";
		private const string PDF_PRINTERNAME = "Microsoft Print to PDF";

		#region Constructors

		/// <summary>
		/// Initializes a new instance of <see cref="PrintPreviewPdf"/>.
		/// </summary>
		public PrintPreviewPdf()
		{
			this.ViewerType = PdfViewerType.Custom;
			this.ViewerURL = $"resource.wx/Wisej.Web.Ext.PrintPreview/JavaScript/pdfjs-{PDF_VIEWER_VERSION}/web/viewer.html?file=[source]#locale={Application.CurrentCulture.Name}";
		}

		#endregion

		#region Properties

		/// <summary>
		/// Returns or sets the document to preview.
		/// </summary>
		/// <exception cref="InvalidPrinterException">
		/// If the printer driver "Microsoft Print to PDF" is not found.
		/// </exception>
		[Browsable(false)]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		public PrintDocument Document
		{
			get { return this._document; }
			set
			{
				if (this._document != value)
				{
					this._document = value;

					DeleteTempPdf();

					if (this._document != null)
					{
						EnsurePrinterDriver();
						this.PdfSource = this.GetPostbackURL($"v={this._document.GetHashCode()}");
					}
				}
			}
		}
		private PrintDocument _document;

		#endregion

		#region Implementation

		// Throws an exception if the PDF driver is not installed.
		internal void EnsurePrinterDriver()
		{
			var printers = PrinterSettings.InstalledPrinters;
			foreach (string name in printers)
			{
				if (name == PDF_PRINTERNAME)
					return;
			}

			throw new InvalidPrinterException(this.Document.PrinterSettings);
		}

		// Creates the temporary PDF to preview.
		private void EnsureTempPdf()
		{
			if (!String.IsNullOrEmpty(this.tempPdf))
				return;

			this.tempPdf = Path.GetTempFileName();

			AppDomain.CurrentDomain.ProcessExit += this.CurrentDomain_ProcessExit;

			// perform printing.

			if (this._document != null)
			{
				this._document.PrintController = new StandardPrintController();
				this._document.PrinterSettings.PrintToFile = true;
				this._document.PrinterSettings.PrintFileName = this.tempPdf;
				this._document.PrinterSettings.PrinterName = PDF_PRINTERNAME;
				this._document.Print();

				if (this.IsDisposed || this.Disposing)
					DeleteTempPdf();
			}
		}
		private string tempPdf;

		// Deletes the temporary PDF when the process (app pool) is terminated.
		private void CurrentDomain_ProcessExit(object sender, EventArgs e)
		{
			DeleteTempPdf();
		}

		// Deletes the temporary PDF associated with this previewer.
		private void DeleteTempPdf()
		{
			if (!String.IsNullOrEmpty(this.tempPdf))
			{
				try
				{
					if (File.Exists(this.tempPdf))
					{
						File.Delete(this.tempPdf);
						this.tempPdf = null;
					}
				}
				catch { }
			}
		}

		/// <summary>
		/// Deletes the temporary PDF when this previewer is closed.
		/// </summary>
		/// <param name="disposing"></param>
		protected override void Dispose(bool disposing)
		{
			AppDomain.CurrentDomain.ProcessExit -= this.CurrentDomain_ProcessExit;

			DeleteTempPdf();

			base.Dispose(disposing);
		}

		#endregion

		#region IWisejHandler

		bool IWisejHandler.Compress
		{
			get { return false; }
		}

		void IWisejHandler.ProcessRequest(HttpContext context)
		{
			EnsureTempPdf();

			var response = context.Response;
			var fileName = this.Document.DocumentName;
			var filePath = this.Document.PrinterSettings.PrintFileName;

			if (!File.Exists(filePath))
			{
				response.StatusCode = 404;
				return;
			}

			if (String.IsNullOrEmpty(fileName))
				fileName = "Document";

			response.ContentType = "application/pdf";
			response.AppendHeader("Access-Control-Allow-Origin", "*");
			response.AppendHeader("Content-Disposition", new ContentDisposition() { DispositionType = "inline", FileName = Path.ChangeExtension(fileName, ".pdf") }.ToString());
			response.TransmitFile(filePath);
		}

		#endregion
	}
}