using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using foxit;
using foxit.pdf;
using foxit.addon.xfa;
using foxit.pdf.annots;
using foxit.pdf.actions;
using PDFReader_Demo_CSharp.Common;
using foxit.pdf.interform;

namespace PDFReader_Demo_CSharp.UI
{
    public struct TextRectInfo
    {
        public foxit.common.fxcrt.RectF text_rect{ get; set; }
        public foxit.common.Rotation baseline_rotation{ get; set; }
    };

    public class CReader_Page
    {
        private const int default_page_space_ = 6;
        private int index_;
        private float height_;
        private float width_;
        private float total_height_;
        private bool has_transparency_;

        private PDFPage pdf_page_;
        private XFAPage xfa_page_;


        public CReader_Page()
        {
            index_ = 0;
            height_ = 0;
            width_ = 0;
            total_height_ = 0;
            has_transparency_ = false;
        }

        public virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                GC.SuppressFinalize(this);
            }

            Release();
        }

        ~CReader_Page()
        {
            this.Dispose(false);
        }

        private void Release()
        {
            if (pdf_page_ != null)
                pdf_page_.Dispose();

            if (xfa_page_ != null)
                xfa_page_.Dispose();
        }

        public PDFPage GetPDFPage()
        {
            return pdf_page_;
        }

        public XFAPage GetXFAPage()
        {
            return xfa_page_;
        }

        public void SetPDFPage(PDFPage pdf_page)
        {
            if (pdf_page_ != null) pdf_page_.Dispose();
            pdf_page_ = pdf_page;
        }

        public void SetXFAPage(XFAPage xfa_page)
        {
            if (xfa_page_ != null) xfa_page_.Dispose();
            xfa_page_ = xfa_page;
        }

        public bool ParsePage(bool is_reparse = false)
        {
            if (pdf_page_ == null || pdf_page_.IsEmpty())
                return false;
            if (pdf_page_.IsParsed() && !is_reparse)
                return true;

            pdf_page_.StartParse((int)PDFPage.ParseFlags.e_ParsePageNormal, null, is_reparse);
            has_transparency_ = pdf_page_.HasTransparency();
            return true;
        }

        public int GetPageIndex()
        {
            return index_;
        }

        public void SetPageIndex(int index)
        {
            index_ = index;
        }

        public void SetPageHeight(float height)
        {
            height_ = height;
        }

        public void SetPageWidth(float width)
        {
            width_ = width;
        }

        public void SetPageTotalHeightWithOutSpace(float total_height)
        {
            total_height_ = total_height;
        }

        public float GetPageTotalHeightWithSpace(float zoom)
        {
            return total_height_ * zoom + (index_ + 1) * default_page_space_;
        }

        public float GetPageHeight()
        {
            return height_;
        }

        public float GetPageWidth()
        {
            return width_;
        }

        public bool IsHasTransparency()
        {
            return has_transparency_;
        }

        public bool AddAnnot(Annot.Type annot_type, List<TextRectInfo> text_rectinfo)
        {
            if (pdf_page_ == null || pdf_page_.IsEmpty())
                return false;

            Annot new_annot = pdf_page_.AddAnnot(annot_type, new foxit.common.fxcrt.RectF(0, 0, 0, 0));
            new_annot.SetFlags((int)(Annot.Flags.e_FlagPrint | Annot.Flags.e_FlagNoRotate));

            foxit.common.fxcrt.FloatArray dash_pattern = new foxit.common.fxcrt.FloatArray();
            dash_pattern.Add(1.0f);
            dash_pattern.Add(1.0f);
            BorderInfo border = new BorderInfo();
            border.Set(3.0f, BorderInfo.Style.e_Dashed, -1, 5, dash_pattern);
            new_annot.SetBorderInfo(border);
            new_annot.SetBorderColor(0xFF0000);

            string annot_type_string = "";
            switch (annot_type)
            {
                case Annot.Type.e_Underline:
                    annot_type_string = "UnderLine";
                    SetTextMarkupProperties((TextMarkup)new_annot, text_rectinfo);
                    new_annot.SetBorderColor(0x00FF00);
                    break;
                case Annot.Type.e_Highlight:
                    annot_type_string = "Highlight";
                    SetTextMarkupProperties((TextMarkup)new_annot, text_rectinfo);
                    ((Highlight)new_annot).SetBorderColor(0x00FFF0);
                    ((Highlight)new_annot).SetOpacity(0.5f);
                    break;
            }

            new_annot.SetContent(annot_type_string);

            foxit.common.DateTime date_time = new foxit.common.DateTime(2017, 9, 15, 17, 20, 20, 0, 8, 0);
            new_annot.SetModifiedDateTime(date_time);
            if (new_annot.IsMarkup())
            {
                Markup markup_annot = (Markup)new_annot;
                markup_annot.SetTitle(annot_type_string);
                markup_annot.SetSubject(annot_type_string);
                markup_annot.SetOpacity(1.0f);

                markup_annot.SetCreationDateTime(date_time);
            }

            new_annot.ResetAppearanceStream();

            return true;
        }

        public bool AddAnnot(Annot.Type annot_type, foxit.common.fxcrt.RectF annot_rect)
        {
            if (pdf_page_ == null || pdf_page_.IsEmpty()) return false;

            Annot new_annot = pdf_page_.AddAnnot(annot_type, annot_rect);
            new_annot.SetFlags((int)(Annot.Flags.e_FlagPrint | Annot.Flags.e_FlagNoRotate));

            foxit.common.fxcrt.FloatArray dash_pattern = new foxit.common.fxcrt.FloatArray();
            dash_pattern.Add(1.0f);
            dash_pattern.Add(1.0f);
            BorderInfo border = new BorderInfo(3.0f, BorderInfo.Style.e_Dashed, -1, 5, dash_pattern);
            new_annot.SetBorderInfo(border);
            new_annot.SetBorderColor(0xFF0000);

            string annot_type_string;
            switch (annot_type)
            {
                case Annot.Type.e_FreeText:
                    annot_type_string = "FreeText";
                    SetFreeTextProperties((FreeText)new_annot, annot_rect);
                    break;
                case Annot.Type.e_Link:
                    annot_type_string = "Link";
                    SetLinkAnnotProperties((Link)new_annot);
                    break;
                case Annot.Type.e_Note:
                    annot_type_string = "Note";
                    ((Note)new_annot).SetIconName("Comment");
                    break;
                default:
                    annot_type_string = "Annot";
                    break;
            }

            new_annot.SetContent(annot_type_string);

            foxit.common.DateTime date_time = new foxit.common.DateTime(2017, 9, 15, 17, 20, 20, 0, 8, 0);
            new_annot.SetModifiedDateTime(date_time);
            if (new_annot.IsMarkup())
            {
                Markup markup_annot = (Markup)new_annot;
                markup_annot.SetTitle(annot_type_string);
                markup_annot.SetSubject(annot_type_string);
                markup_annot.SetOpacity(1.0f);

                markup_annot.SetCreationDateTime(date_time);
            }

            new_annot.ResetAppearanceStream();

            return true;
        }

        void SetTextMarkupProperties(TextMarkup textmark_annot, List<TextRectInfo> text_rectinfo)
        {
            if (textmark_annot.IsEmpty())
                return;

            int info_count = text_rectinfo.Count();
            QuadPointsArray quadpoints_array = new QuadPointsArray();
            for (int i = 0; i < info_count; i++)
            {
                foxit.common.fxcrt.RectF single_rect = text_rectinfo[i].text_rect;
                QuadPoints quadpoints = new QuadPoints();
                if (text_rectinfo[i].baseline_rotation == foxit.common.Rotation.e_Rotation0
                   || text_rectinfo[i].baseline_rotation == foxit.common.Rotation.e_Rotation180)
                {
                    // As rotation = 0, or 180, to draw in the bottom of piece.
                    quadpoints.first = new foxit.common.fxcrt.PointF(single_rect.left, single_rect.top);
                    quadpoints.second = new foxit.common.fxcrt.PointF(single_rect.right, single_rect.top);
                    quadpoints.third = new foxit.common.fxcrt.PointF(single_rect.left, single_rect.bottom);
                    quadpoints.fourth = new foxit.common.fxcrt.PointF(single_rect.right, single_rect.bottom);
                }
                else
                {
                    // As rotation = 90, or 270, to draw in the left side of piece.
                    quadpoints.first = new foxit.common.fxcrt.PointF(single_rect.right, single_rect.top);
                    quadpoints.second = new foxit.common.fxcrt.PointF(single_rect.right, single_rect.bottom);
                    quadpoints.third = new foxit.common.fxcrt.PointF(single_rect.left, single_rect.top);
                    quadpoints.fourth = new foxit.common.fxcrt.PointF(single_rect.left, single_rect.bottom);
                }
                quadpoints_array.Add(quadpoints);
            }
            textmark_annot.SetQuadPoints(quadpoints_array);
        }

        void SetFreeTextProperties(FreeText freetext_annot, foxit.common.fxcrt.RectF rectAnnot)
        {
            if (freetext_annot.IsEmpty())
                return;

            freetext_annot.SetFillColor(0xFFFFFF);
            freetext_annot.SetAlignment(foxit.common.Alignment.e_AlignmentLeft);
            freetext_annot.SetIntent("FreeTextCallout");

            float width = rectAnnot.Width();
            float height = rectAnnot.Height();

            freetext_annot.SetCalloutLineEndingStyle(Markup.EndingStyle.e_EndingStyleOpenArrow);
            foxit.common.fxcrt.PointFArray callout_points = new foxit.common.fxcrt.PointFArray();
            foxit.common.fxcrt.PointF point = new foxit.common.fxcrt.PointF(rectAnnot.left + 3, rectAnnot.bottom + 3);
            callout_points.Add(point);
            point = new foxit.common.fxcrt.PointF(rectAnnot.left + width / 4, rectAnnot.top - height / 4);
            callout_points.Add(point);
            point = new foxit.common.fxcrt.PointF(rectAnnot.left + width / 2, rectAnnot.top - height / 4);
            callout_points.Add(point);
            freetext_annot.SetCalloutLinePoints(callout_points);

            foxit.common.Font new_font = new foxit.common.Font("SimSun", (int)foxit.common.Font.Styles.e_StyleFixedPitch, foxit.common.Font.Charset.e_CharsetGB2312, 400);
            DefaultAppearance default_appearance = new DefaultAppearance((int)(DefaultAppearance.DefAPFlags.e_FlagFont | DefaultAppearance.DefAPFlags.e_FlagFontSize | DefaultAppearance.DefAPFlags.e_FlagTextColor),
                                                new_font, 12, 0x00ff00);

            freetext_annot.SetDefaultAppearance(default_appearance);
        }

        void SetLinkAnnotProperties(Link link_annot)
        {
            if (link_annot.IsEmpty())
                return;

            link_annot.SetHighlightingMode(Annot.HighlightingMode.e_HighlightingOutline);

            URIAction new_action = (URIAction)foxit.pdf.actions.Action.Create(link_annot.GetPage().GetDocument(), foxit.pdf.actions.Action.Type.e_TypeURI);
            new_action.SetURI("www.google.com");
            new_action.SetTrackPositionFlag(true);
            link_annot.SetAction(new_action);
        }

        public Annot GetAnnotAtPoint(System.Drawing.Point point, foxit.common.fxcrt.Matrix2D matrix)
        {
            foxit.common.fxcrt.PointF point_annot = new foxit.common.fxcrt.PointF();
            point_annot.x = (float)(point.X);
            point_annot.y = (float)(point.Y);

            return pdf_page_.GetAnnotAtDevicePoint(point_annot, 10, matrix);
        }

        public Annot GetAnnotAtPoint(foxit.common.fxcrt.PointF point)
        {
            if (pdf_page_ == null || pdf_page_.IsEmpty()) return new Annot();
            return pdf_page_.GetAnnotAtPoint(point, 20);
        }

        public void GetPageRealWidthAndHeight(float scale, foxit.common.Rotation rotate, ref int real_width, ref int real_height)
        {
            System.Windows.Size size = new System.Windows.Size(GetPageWidth(), GetPageHeight());

            if (rotate == foxit.common.Rotation.e_Rotation0 || rotate == foxit.common.Rotation.e_Rotation180)
            {
                real_width = (int)(size.Width * scale + 0.5f);
                real_height = (int)(size.Height * scale + 0.5f);
            }
            else if (rotate == foxit.common.Rotation.e_Rotation90 || rotate == foxit.common.Rotation.e_Rotation270)
            {
                real_width = (int)(size.Height * scale + 0.5f);
                real_height = (int)(size.Width * scale + 0.5f);
            }
        }

        public System.Drawing.Bitmap GetRenderPageBuffer(float scale, foxit.common.Rotation rotate, bool is_xfa = false)
        {
            int width = 0, height = 0;

            GetPageRealWidthAndHeight(scale, rotate, ref width, ref height);

            try
            {
                System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
                if (pdf_page_ == null || pdf_page_.IsEmpty()) return bitmap;
                Graphics bitmap_graphics = Graphics.FromImage(bitmap);
                bitmap_graphics.Clear(Color.White);

                foxit.common.fxcrt.Matrix2D matrix = is_xfa ? xfa_page_.GetDisplayMatrix(0, 0, width, height, rotate) : pdf_page_.GetDisplayMatrix(0, 0, width, height, rotate);
                foxit.common.Bitmap _bitmap = new foxit.common.Bitmap(width, height, foxit.common.Bitmap.DIBFormat.e_DIBAbgr, IntPtr.Zero, 0);
                _bitmap.FillRect(0xFFFFFFFF, null);
                // Render page
                foxit.common.Renderer render = new foxit.common.Renderer(_bitmap, false);
                if (is_xfa)
                {
                    render.StartRenderXFAPage(xfa_page_, matrix, true, null);
                }
                else
                {
                    render.StartRender(pdf_page_, matrix, null);
                }                
                bitmap_graphics.Dispose();

                System.Drawing.Imaging.PixelFormat pixel_format = System.Drawing.Imaging.PixelFormat.Format32bppRgb;
                bitmap = new System.Drawing.Bitmap(_bitmap.GetWidth(), _bitmap.GetHeight(), pixel_format);
                System.Drawing.Imaging.BitmapData bitmap_data = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height),
                                                                                                                                System.Drawing.Imaging.ImageLockMode.ReadWrite, pixel_format);
                int buffer_size = _bitmap.GetPitch() * _bitmap.GetHeight();
                byte[] byte_string = new byte[buffer_size];
                System.Runtime.InteropServices.Marshal.Copy(_bitmap.GetBuffer(), byte_string, 0, buffer_size);
                System.Runtime.InteropServices.Marshal.Copy(byte_string, 0, bitmap_data.Scan0, buffer_size);
                bitmap.UnlockBits(bitmap_data);
                _bitmap.Dispose();
                return bitmap;
            }
            catch (System.Exception e)
            {
                System.Windows.Forms.MessageBox.Show(e.Message);
            }
            return null;
        }

        public System.Drawing.Bitmap GetRenderPageBuffer(CPDFReaderDoc reader_doc,int bitmap_width, int bitmap_height, int cx, int cy, int width, int height, foxit.common.Rotation rotate, bool is_xfa = false)
        {
            try
            {
                System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(bitmap_width, bitmap_height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
                if (pdf_page_ == null && xfa_page_ == null) return bitmap;
                if (pdf_page_ != null && pdf_page_.IsEmpty()) return bitmap;
                if (xfa_page_ != null && xfa_page_.IsEmpty()) return bitmap;
                foxit.common.Bitmap _bitmap = new foxit.common.Bitmap(bitmap_width, bitmap_height, foxit.common.Bitmap.DIBFormat.e_DIBAbgr, IntPtr.Zero, 0);

                foxit.common.fxcrt.Matrix2D matrix = is_xfa ? xfa_page_.GetDisplayMatrix(cx, cy, width, height, rotate) : pdf_page_.GetDisplayMatrix(cx, cy, width, height, rotate);

                // Render page
                foxit.common.Renderer render = new foxit.common.Renderer(_bitmap, false);
                if (is_xfa)
                {
                    _bitmap.FillRect(0xFFFFFFFF, null);
                    render.StartRenderXFAPage(xfa_page_, matrix, true, null);
                }
                else
                {
                    ParsePage();
                    if (pdf_page_.HasTransparency())
                        _bitmap.FillRect(0, null);
                    else
                      _bitmap.FillRect(0xFFFFFFFF, null);
                    render.StartRender(pdf_page_, matrix, null);

                    Filler formfiller = reader_doc.GetFormFiller();
                    if (formfiller!=null && !formfiller.IsEmpty())
                        formfiller.Render(pdf_page_, matrix, render);
                }
                render.Dispose();

                System.Drawing.Imaging.PixelFormat pixel_format = System.Drawing.Imaging.PixelFormat.Format32bppRgb;
                bitmap = new System.Drawing.Bitmap(_bitmap.GetWidth(), _bitmap.GetHeight(), pixel_format);
                System.Drawing.Imaging.BitmapData bitmap_data = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height), 
                                                                                                                                System.Drawing.Imaging.ImageLockMode.ReadWrite, pixel_format);
                int buffer_size = _bitmap.GetPitch() * _bitmap.GetHeight();
                byte[] byte_string = new byte[buffer_size];
                System.Runtime.InteropServices.Marshal.Copy(_bitmap.GetBuffer(), byte_string, 0, buffer_size);
                System.Runtime.InteropServices.Marshal.Copy(byte_string, 0, bitmap_data.Scan0, buffer_size);
                bitmap.UnlockBits(bitmap_data);
                _bitmap.Dispose();
                return bitmap;
            }
            catch (System.Exception e)
            {
                System.Windows.Forms.MessageBox.Show(e.Message);
            }
            return null;
        }

        public int GetCharIndexByPoint(foxit.common.fxcrt.PointF origin_page_point)
        {
            if (pdf_page_ == null)
                return -1;

            using (TextPage text_page = new foxit.pdf.TextPage(pdf_page_, (int)TextPage.TextParseFlags.e_ParseTextNormal))
            {
                return text_page != null ? text_page.GetIndexAtPos(origin_page_point.x, origin_page_point.y, 10.0f) : -1;
            }

                
        }

        public string GetChars(int start_char_index, int count)
        {
            using (TextPage text_page = new foxit.pdf.TextPage(pdf_page_, (int)TextPage.TextParseFlags.e_ParseTextNormal))
            {
                return text_page.GetChars(start_char_index, count);
            }
        }
    }
}
