using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using foxit;
using foxit.pdf;
using foxit.pdf.interform;
using foxit.addon.xfa;
using PDFReader_Demo_CSharp.Common;
using System.Windows.Forms;


namespace PDFReader_Demo_CSharp.UI
{
    public class CPDFReaderDoc : IDisposable
    {
        private const int default_page_space_ = 6;
        private bool is_init_pages_ = false;
        private bool is_load_document_ = false;
        public PDFDoc pdf_doc_ = null;
        private XFADoc xfa_doc_ = null;
        private foxit.pdf.interform.Form pdf_form_ = null;
        private Filler form_filler_ = null;
        DocProviderCallback xfa_doc_provider_ = null;
        FillerAssistCallback form_filler_assist_ = null;

        private string file_path_;
        private string password_;

        private Dictionary<int, CReader_Page> reader_page_list_ = new Dictionary<int,CReader_Page>();

        private int page_space_ = default_page_space_;
        private int total_page_count_ = 0;
        private int page_shadow_width_ = 4;
        private float real_height_ = 0.0f;
        private float real_width = 0.0f;
        private int current_page_index_ = 0;
        private bool is_dynamic_xfa_ = false;
        private bool is_modified_ = false;
        private WeakReference view_weakref_ = null;

        public CPDFReaderDoc()
        {
        }

        public void Dispose()
        {
            this.Dispose(true);
        }

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

            Release();
        }

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

        public void SetPageView(MainForm view)
        {
            view_weakref_ = new WeakReference(view);
        }

        public MainForm GetView()
        {
            return view_weakref_ != null ? (view_weakref_.IsAlive ? view_weakref_.Target as MainForm : null):null;
        }

        public bool LoadPDFDocument(string input_file_path)
        {
            file_path_ = input_file_path;

            pdf_doc_ = new PDFDoc(input_file_path);
            foxit.common.ErrorCode error_code = foxit.common.ErrorCode.e_ErrSuccess;
            error_code = pdf_doc_.Load(System.Text.Encoding.Default.GetBytes(password_ ?? ""));
            while (error_code == foxit.common.ErrorCode.e_ErrPassword)
            {
                PasswordDlg password_dlg = new PasswordDlg();
                DialogResult dlg_result = password_dlg.ShowDialog();

                if (password_dlg.password_ != String.Empty)
                {
                    error_code = pdf_doc_.Load(System.Text.Encoding.Default.GetBytes(password_dlg.password_));
                    if (error_code != foxit.common.ErrorCode.e_ErrPassword)
                    {
                        if (error_code == foxit.common.ErrorCode.e_ErrSuccess)
                            password_ = password_dlg.password_;

                        break;
                    }
                    else
                        file_path_ = input_file_path;
                }

                if (DialogResult.Cancel == dlg_result && password_dlg.press_key_ != 13)
                {
                    if (pdf_doc_ != null) {
                        pdf_doc_.Dispose();
                        pdf_doc_ = null;
                    }
                    return false;
                }
                MessageBox.Show("Invalid password, please enter password again!");
            }

            if (error_code != foxit.common.ErrorCode.e_ErrSuccess)
            {
                if (pdf_doc_ != null)
                {
                    pdf_doc_.Dispose();
                    pdf_doc_ = null;
                }

                string error_message = "Fail to load PDF document!";
                switch (error_code)
                {
                    case foxit.common.ErrorCode.e_ErrInvalidLicense:
                        error_message += "\r\nError: Foxit PDF SDK library has not been loaded successfully.";
                        break;
                    default:
                        error_message += "\r\nErrorCode: " + error_code.ToString();
                        break;
                }
                MessageBox.Show(error_message);
                return false;
            }

            PDFPage page = FoxitUtil.LoadPage(pdf_doc_, current_page_index_);
            page.Dispose();

            is_load_document_ = true;
            bool has_xfa_right = foxit.common.Library.HasModuleLicenseRight(foxit.common.ModuleName.e_ModuleNameXFA);

            try {
                bool is_xfa = pdf_doc_.IsXFA();
                XFADoc.Type xfa_doc_type = (XFADoc.Type)(-1);
                if (is_xfa)
                {
                    if (!has_xfa_right)
                    {
                        if (pdf_doc_ != null)
                        {
                            pdf_doc_.Dispose();
                            pdf_doc_ = null;
                        }
                        
                        is_load_document_ = false;
                        string error_message = "You are not authorized to use \"XFA\" add-on module, please contact us for upgrading your license.";
                        MessageBox.Show(error_message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        return false;
                    }
                    xfa_doc_provider_ = new XFADocProvider(this);
                    xfa_doc_ = new XFADoc(pdf_doc_, xfa_doc_provider_);
                    xfa_doc_.StartLoad(null);
                    xfa_doc_type = xfa_doc_.GetType();
                }

                if ((is_xfa && xfa_doc_type == XFADoc.Type.e_Static) || !is_xfa)
                {
                    if (form_filler_assist_ == null)
                        form_filler_assist_ = new FormFillerAssist(this);
                    if (pdf_doc_.HasForm())
                    {
                        pdf_form_ = new foxit.pdf.interform.Form(pdf_doc_);
                        form_filler_ = new Filler(pdf_form_, form_filler_assist_);
                        form_filler_.HighlightFormFields(true);
                    }
                    pdf_doc_.DoJSOpenAction();
                }
                else
                {
                    is_dynamic_xfa_ = true;
                }
            } catch (PDFException e) {
                if (e.GetErrorCode() != foxit.common.ErrorCode.e_ErrInvalidLicense)
                    MessageBox.Show(e.Message);
            }
            return true;
        }

        bool ReLoadDocument()
        {
            is_load_document_ = false;
            pdf_doc_ = new PDFDoc(file_path_);
            foxit.common.ErrorCode error_code = foxit.common.ErrorCode.e_ErrSuccess;
            error_code = pdf_doc_.Load(System.Text.Encoding.Default.GetBytes(password_));

            if (error_code != foxit.common.ErrorCode.e_ErrSuccess)
            {
                if (pdf_doc_ != null)
                {
                    pdf_doc_.Dispose();
                    pdf_doc_ = null;
                }

                string error_message = "Fail to load PDF document!";
                switch (error_code)
                {
                    case foxit.common.ErrorCode.e_ErrInvalidLicense:
                        error_message += "\r\nError: Foxit PDF SDK library has not been loaded successfully.";
                        break;
                    default:
                        error_message += "\r\nErrorCode: " + error_code.ToString();
                        break;
                }
                MessageBox.Show(error_message);
                return false;
            }

            pdf_doc_.StartLoad(System.Text.Encoding.Default.GetBytes(file_path_), false, null);
            PDFPage page = FoxitUtil.LoadPage(pdf_doc_, current_page_index_);
            page.Dispose();

            is_load_document_ = true;

            try
            {
                bool is_xfa = pdf_doc_.IsXFA();
                XFADoc.Type xfa_doc_type = (XFADoc.Type)(-1);
                if (is_xfa)
                {
                    xfa_doc_provider_ = new DocProviderCallback();
                    xfa_doc_ = new XFADoc(pdf_doc_, xfa_doc_provider_);
                    xfa_doc_.StartLoad(null);
                    xfa_doc_type = xfa_doc_.GetType();
                }

                if ((is_xfa && xfa_doc_type == XFADoc.Type.e_Static) || !is_xfa)
                {
                    if (form_filler_assist_ == null)
                        form_filler_assist_ = new FormFillerAssist(this);
                    if (pdf_doc_.HasForm())
                    {
                        pdf_form_ = new foxit.pdf.interform.Form(pdf_doc_);
                        form_filler_ = new Filler(pdf_form_, form_filler_assist_);
                        form_filler_.HighlightFormFields(true);
                    }
                    pdf_doc_.DoJSOpenAction();
                }
                else
                {
                    is_dynamic_xfa_ = true;
                }
            }
            catch (PDFException e) {
                if (e.GetErrorCode() != foxit.common.ErrorCode.e_ErrInvalidLicense)
                    MessageBox.Show(e.Message);
            }
            return true;
        }

        void Release()
        {
            if (form_filler_assist_ != null)
            {
                form_filler_assist_.Dispose();
                form_filler_assist_ = null;
            }

            if (form_filler_ != null)
            {
                form_filler_.Dispose();
                form_filler_ = null;
            }

            if (pdf_form_ != null)
            {
                pdf_form_.Dispose();
                pdf_form_ = null;
            }

            for (int i = 0; i < reader_page_list_.Count; i++)
            {
                CReader_Page readerpage = reader_page_list_[i];
                readerpage.Dispose(true);
            }

            reader_page_list_.Clear();

            if (xfa_doc_ != null)
            {
                xfa_doc_.Dispose();
                xfa_doc_ = null;
            }

            if (pdf_doc_ != null)
            {
                pdf_doc_.Dispose();
                pdf_doc_ = null;
            }
        }

        public bool UpdatePDFPages(int logpixel_x, int logpixel_y, foxit.common.Rotation rotate, bool need_update_page_list)
        {
            real_height_ = 0.0f;
            real_width = 0.0f;
            if (!is_init_pages_ || need_update_page_list)
            {
                for (int i = 0; i < reader_page_list_.Count; i++)
                {
                    CReader_Page readerpage = reader_page_list_[i];
                    readerpage.Dispose(true);
                }

                reader_page_list_.Clear();
                real_height_ = 0;
                real_width = 0;
                if (is_dynamic_xfa_)
                    total_page_count_ = xfa_doc_.GetPageCount();
                else
                    total_page_count_ = pdf_doc_.GetPageCount();
            }

            for (int i = 0; i < total_page_count_; i++)
            {
                CReader_Page reader_page = (is_init_pages_ && !need_update_page_list) ? reader_page_list_[i] : new CReader_Page();
                PDFPage pdf_page = null;
                XFAPage xfa_page = null;
                float page_width = 0;
                float page_height = 0;

                if (is_dynamic_xfa_)
                {
                    xfa_page = xfa_doc_.GetPage(i);
                    page_width = xfa_page.GetWidth();
                    page_height = xfa_page.GetHeight();
                }
                else
                {
                    pdf_page = pdf_doc_.GetPage(i);
                    if (pdf_page.IsEmpty())
                    {
                        page_width = 0;
                        page_height = 0;
                    }
                    else
                    {
                        page_width = pdf_page.GetWidth();
                        page_height = pdf_page.GetHeight();
                    }
                }

                float width = page_width / 72.0f * logpixel_x + 0.5f;
                float height = page_height / 72.0f * logpixel_y + 0.5f;
                if (rotate == foxit.common.Rotation.e_Rotation90 || rotate == foxit.common.Rotation.e_Rotation270)
                {
                    real_height_ = real_height_ + width;
                    if (height > real_width)
                        real_width = height;
                }
                else
                {
                    real_height_ = real_height_ + height;
                    if (width > real_width)
                        real_width = width;
                }
                reader_page.SetPageHeight(height);
                reader_page.SetPageWidth(width);
                reader_page.SetPageTotalHeightWithOutSpace(real_height_);
                reader_page.SetPDFPage(pdf_page);
                reader_page.SetXFAPage(xfa_page);

                reader_page.SetPageIndex(i);
                if (!is_init_pages_ || need_update_page_list)
                {
                    reader_page_list_.Add(i, reader_page);
                }
            }
            is_init_pages_ = true;
            return true;
        }

        public void FindVisiblePage(float start_pos, float end_pos, ref int start_page_index, ref int end_page_index, float zoom_factor)
        {
            for (int i = 0; i < total_page_count_; i++)
            {
                CReader_Page reader_page = reader_page_list_[i];
                float page_total_height = reader_page.GetPageTotalHeightWithSpace(zoom_factor);
                if (page_total_height - start_pos > 1)
                {
                    start_page_index = reader_page.GetPageIndex();
                    if (i != 0)
                    {
                        CReader_Page temp_reader_page = reader_page_list_[i - 1];
                        start_pos = temp_reader_page.GetPageTotalHeightWithSpace(zoom_factor);
                    }
                    else
                        start_pos = 0;
                    for (int j = i; j < total_page_count_; j++)
                    {
                        CReader_Page temp_reader_page = reader_page_list_[j];
                        float temp_page_total_height = temp_reader_page.GetPageTotalHeightWithSpace(zoom_factor);
                        if (temp_page_total_height + page_space_ > end_pos)
                        {
                            end_page_index = temp_reader_page.GetPageIndex();
                            break;
                        }
                        else
                        {
                            end_page_index = total_page_count_ - 1;
                        }
                    }
                    break;
                }
            }

            if (end_page_index < start_page_index)
            {
                end_page_index = total_page_count_ - 1;
            }
        }

        public PDFDoc GetPDFDocument()
        {
            return pdf_doc_;
        }

        public bool HasLoadDocument()
        {
            return is_load_document_;
        }

        public string GetFilePath()
        {
            return file_path_;
        }

        public void UpdateCurPage(int index)
        {
            current_page_index_ = index;

            if (GetView() != null)
                GetView().UpdateCurPage(index);
        }

        public int GetCurPage()
        {
            return current_page_index_;
        }

        public int GetPageCount()
        {
            return total_page_count_;
        }

        public bool IsDynamicXFA()
        {
            return is_dynamic_xfa_;
        }

        public Filler GetFormFiller()
        {
            return form_filler_;
        }

        public XFADoc GetXFADoc()
        {
            return xfa_doc_;
        }

        public foxit.pdf.interform.Form GetForm()
        {
            return pdf_form_;
        }

        public void UpdatePageCount()
        {
            if (is_dynamic_xfa_)
                total_page_count_ = GetXFADoc().GetPageCount();
            else
                total_page_count_ = GetPDFDocument().GetPageCount();
        }

        public CReader_Page GetPage(int page_index)
        {
            if (page_index < 0 || page_index >= total_page_count_)
                return null;
            return reader_page_list_[page_index];
        }

        public bool GetCharIndexByPoint(foxit.common.fxcrt.PointF page_point, int page_index, ref int char_index)
        {
            CReader_Page page = GetPage(page_index);
            char_index = page != null ? page.GetCharIndexByPoint(page_point) : -1;

            return (-1 == char_index) ? false : true;
        }

        public float GetRealHeight(float zoom)
        {
            return real_height_ * zoom + (total_page_count_ + 1) * page_space_;
        }

        public float GetRealWidth(float zoom)
        {
            return real_width * zoom + 2 * page_space_;
        }

        public int GetPageShadowWidth()
        {
            return page_shadow_width_;
        }

        public int GetPageSpace()
        {
            return page_space_;
        }

        public bool IsModified()
        {
            return is_modified_;
        }

        public void SetModifiedFlag(bool is_modified = true)
        {
            if (is_modified != is_modified_)
            {
                is_modified_ = is_modified;
            }
        }

        public void Save()
        {

        }

        public void SaveAs()
        {

        }

    }
}