/*
 i-net software provides programming examples for illustration only, without warranty
 either expressed or implied, including, but not limited to, the implied warranties
 of merchantability and/or fitness for a particular purpose. This programming example
 assumes that you are familiar with the programming language being demonstrated and
 the tools used to create and debug procedures. i-net software support professionals
 can help explain the functionality of a particular procedure, but they will not modify
 these examples to provide added functionality or construct procedures to meet your
 specific needs.
  
 © i-net software 1998-2013

*/
using System;
using System.Drawing;
using System.Drawing.Printing;
using System.Windows.Forms;
using System.Threading;
using Inet.Viewer.Resources;
using Inet.Viewer.WinForms;

namespace Inet.Viewer.Data
{
    /// <summary>
    /// Progress implementation for printing a report.
    /// </summary>
    public class PrintProgress : Progress
    {
        private PrintDocument printDoc;
        private int currentPage;

        private ReportDataCache reportDataCache;
        private int pageCount;
        private PrinterSettings printerSettings;

        /// <summary>
        /// Creates a print progress for the specified render data.
        /// </summary>
        /// <param name="renderData">the report to print</param>
        /// <param name="printerSettings">printing settings</param>
        /// <param name="errorDelegate">a delegate which will be called on failures during the progress</param>
        public PrintProgress(IRenderData renderData, PrinterSettings printerSettings, Action<Exception> errorDelegate) :
            this(new ReportDataCache(renderData), printerSettings, errorDelegate)
        {
        }

        /// <summary>
        /// Creates a print progress for the specified ReportDataCache instance.
        /// </summary>
        /// <param name="reportDataCache">the report to print as ReportDataCache instance</param>
        /// <param name="printerSettings">printing settings</param>
        /// <param name="errorDelegate">a delegate which will be called on failures during the progress</param>
        internal PrintProgress(ReportDataCache reportDataCache, PrinterSettings printerSettings, Action<Exception> errorDelegate) :
            base(errorDelegate, ProgressType.Print)
        {
            this.reportDataCache = reportDataCache;
            this.printerSettings = printerSettings;
            printDoc = new PrintDocument();
            printDoc.PrinterSettings = printerSettings;
            printDoc.QueryPageSettings += new QueryPageSettingsEventHandler(printDoc_QueryPageSettings);
            printDoc.PrintPage += new PrintPageEventHandler(printDoc_PrintPage);
        }

        /// <summary>
        /// Gets or sets the report info instance. When the report and the page info instances are set both, no
        /// additional request to the report data cache is needed before printing.
        /// </summary>
        internal ReportInfo ReportInfo
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the page info instance. When the report and the page info instances are set both, no
        /// additional request to the report data cache is needed before printing.
        /// </summary>
        internal PageInfo PageInfo
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the current page of the viewer. Used when the user selects "Current Page" in the 
        /// printing dialog.
        /// </summary>
        internal int CurrentViewPage
        {
            set;
            get;
        }

        /// <inheritdoc />
        protected override void Run()
        {
            try
            {
                pageCount = reportDataCache.PageCount();
                PrintRange rangeType = printerSettings.PrintRange;
                if (rangeType == PrintRange.SomePages)
                {
                    currentPage = printerSettings.FromPage;
                }
                else if (rangeType == PrintRange.CurrentPage)
                {
                    currentPage = CurrentViewPage;
                }
                else
                {
                    currentPage = 1;
                }

                LoadMetaDataIfRequired();
                printDoc.DocumentName = ReportInfo.Title;
                printDoc.Print();
                Status = ProgressStatus.Completed;
            }
            catch (Exception e)
            {
                ShowError(e);
            }
        }
 
        /// <summary>
        /// Called before printing a page. Sets the page settings.
        /// </summary>
        /// <param name="sender">not used</param>
        /// <param name="e">event args which include page settings</param>
        private void printDoc_QueryPageSettings(object sender, QueryPageSettingsEventArgs e)
        {
            e.PageSettings.Margins = PageInfo.PrintMargins;
        }

        /// <summary>
        /// Called when printing the next page. Does the actual output on the printer graphics.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="ev"></param>
        private void printDoc_PrintPage(object sender, PrintPageEventArgs ev)
        {
            Graphics2DPainter painter = new Graphics2DPainter(true);
            PrinterPageView pageView = new PrinterPageView(ErrorDelegate, reportDataCache, ev);
            byte[] pageData = reportDataCache.PageData(currentPage, false);
            pageView.Page = currentPage;
            PageLoader pLoader = new PageLoader(painter, pageView);
            pLoader.PaintPage(pageData);

            currentPage++;
            PrintRange rangeType = ev.PageSettings.PrinterSettings.PrintRange;
            if (rangeType == PrintRange.SomePages)
            {
                ev.HasMorePages = currentPage <= Math.Min( pageCount, ev.PageSettings.PrinterSettings.ToPage);
            }
            else if (rangeType == PrintRange.CurrentPage)
            {
                ev.HasMorePages = false;
            }
            else
            {
                ev.HasMorePages = currentPage <= pageCount;
            }

            ev.Cancel = Status == ProgressStatus.Canceled;
        }

        /// <inheritdoc/>
        public override string Name
        {
            get { return strings.Print; }
        }

        /// <inheritdoc/>
        public override void Cancel()
        {
            Status = ProgressStatus.Canceled;
        }

        /// <summary>
        /// Updates the default page settings of the printer settings. This will change the PrinterSettings 
        /// instance specified via the constructor when creating this progress. 
        /// If no meta data (ReportInfo / PageInfo) was set manually, the required properties are loaded 
        /// directly from the underlying IRenderData instance. 
        /// Since this method may invoke blocking I/O operations, it should not be called form a UI thread.
        /// </summary>
        public void UpdatePageSettings()
        {
            LoadMetaDataIfRequired();
            printerSettings.DefaultPageSettings.Landscape = PageInfo.PageAlign == (int)PageOrientation.Landscape;
            printerSettings.DefaultPageSettings.Margins = PageInfo.PrintMargins;
        }

        /// <summary>
        /// Loads the meta data if not set yet.
        /// </summary>
        private void LoadMetaDataIfRequired()
        {
            if (ReportInfo == null || PageInfo == null)
            {
                // meta data not set yet => parse the first page
                PageMetaReceiver pageMetaReceiver = new PageMetaReceiver();
                PageLoader loader = new PageLoader(null, pageMetaReceiver);
                loader.Data = reportDataCache.PageData(1, false);
                loader.ReadTokens();
                if (pageMetaReceiver.Error != null)
                {
                    ShowError(pageMetaReceiver.Error);
                    return;
                }
                ReportInfo = pageMetaReceiver.ReportInfo;
                PageInfo = pageMetaReceiver.PageInfo;
            }
        }
    }
}