using ComDocumentAIKit;
using ComPDFKit.PDFDocument;
using ComPDFKit.PDFPage;
using ComPDFKitViewer;
using ComPDFKitViewer.PdfViewer;
using PDF_Office.EventAggregators;
using PDF_Office.Helper;
using PDF_Office.Model;
using Prism.Commands;
using Prism.Events;
using Prism.Mvvm;
using Prism.Regions;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Threading;

namespace PDF_Office.ViewModels.Scan
{
    class ScanViwerViewModel : BindableBase, INavigationAware
    {
        private CPDFViewer PDFViewer;
        private ViewContentViewModel viewContentViewModel;

        private WriteableBitmap bgImage;

        public WriteableBitmap BgImage
        {
            get { return bgImage; }
            set
            {
                SetProperty(ref bgImage, value);
            }
        }

        private List<KeyValuePair<Rect, string>> textRectList;

        public List<KeyValuePair<Rect, string>> TextRectList
        {
            get { return textRectList; }
            set
            {
                SetProperty(ref textRectList, value);
            }
        }

        private bool isEnhanced;
        /// <summary>
        /// 当前是否为增强扫描状态
        /// </summary>
        public bool IsEnhanced
        {
            get { return isEnhanced; }
            set
            {
                SetProperty(ref isEnhanced, value);
            }
        }

        private bool isShowRect;

        public bool IsShowRect
        {
            get { return isShowRect; }
            set
            {
                SetProperty(ref isShowRect, value);
            }
        }

        private COCRLanguage language = COCRLanguage.English;

        /// <summary>
        /// 增强扫描结果路径
        /// </summary>
        public List<string> EnhancedFilePathList;

        /// <summary>
        /// OCR结果路径
        /// </summary>
        private Dictionary<int, List<KeyValuePair<Rect, string>>> cacahe;

        private Visibility showTooltip = Visibility.Collapsed;

        public Visibility ShowTooltip
        {
            get { return showTooltip; }
            set
            {
                SetProperty(ref showTooltip, value);
            }
        }

        public DelegateCommand CloseTooltipCommand { get; set; }
        public DelegateCommand CancelEnhancedCommand { get; set; }

        public bool IsNavigationTarget(NavigationContext navigationContext)
        {
            return true;
        }

        public void OnNavigatedFrom(NavigationContext navigationContext)
        {
            return;
        }

        public void OnNavigatedTo(NavigationContext navigationContext)
        {
            navigationContext.Parameters.TryGetValue<ViewContentViewModel>(ParameterNames.ViewContentViewModel, out viewContentViewModel);
            if (viewContentViewModel == null)
            {
                return;
            }

            navigationContext.Parameters.TryGetValue<CPDFViewer>(ParameterNames.PDFViewer, out PDFViewer);
            if (PDFViewer == null)
            {
                return;
            }
            else
            {
                PDFViewer.InfoChanged += PDFViewer_InfoChanged;

                PDFViewer.ChangeViewMode(ViewMode.Single);
                //第一次进入获取当前预览显示内容
                CPDFPage pdfPage = PDFViewer.Document.PageAtIndex(PDFViewer.CurrentIndex);
                float zoom = (float)(DpiHelpers.Dpi / 72D);
                int renderWidth = (int)(pdfPage.PageSize.Width * zoom);
                int renderHeight = (int)(pdfPage.PageSize.Height * zoom);
                byte[] renderData = new byte[renderWidth * renderHeight * 4];
                pdfPage.RenderPageBitmapWithMatrix(zoom, new Rect(0, 0, renderWidth, renderHeight), 0xFFFFFFFF, renderData, 0);
                BgImage = new WriteableBitmap(renderWidth, renderHeight, DpiHelpers.Dpi, DpiHelpers.Dpi, PixelFormats.Bgra32, null);
                BgImage.WritePixels(new Int32Rect(0, 0, renderWidth, renderHeight), renderData, BgImage.BackBufferStride, 0);
            }
        }

        private void PDFViewer_InfoChanged(object sender, KeyValuePair<string, object> e)
        {
            if (e.Key == "PageNum")
            {
                RenderData renderData = e.Value as RenderData;
                if (renderData != null)
                {
                    if (IsEnhanced)
                    {
                        BitmapImage image = new BitmapImage(new Uri(EnhancedFilePathList[PDFViewer.CurrentIndex]));
                        BgImage = new WriteableBitmap(image);
                    }
                    else
                    {
                        CPDFPage pdfPage = PDFViewer.Document.PageAtIndex(renderData.PageIndex - 1);
                        float zoom = (float)(DpiHelpers.Dpi / 72D);
                        int renderWidth = (int)(pdfPage.PageSize.Width * zoom);
                        int renderHeight = (int)(pdfPage.PageSize.Height * zoom);
                        byte[] Data = new byte[renderWidth * renderHeight * 4];
                        pdfPage.RenderPageBitmapWithMatrix(zoom, new Rect(0, 0, renderWidth, renderHeight), 0xFFFFFFFF, Data, 0);
                        BgImage = new WriteableBitmap(renderWidth, renderHeight, DpiHelpers.Dpi, DpiHelpers.Dpi, PixelFormats.Bgra32, null);
                        BgImage.WritePixels(new Int32Rect(0, 0, renderWidth, renderHeight), Data, BgImage.BackBufferStride, 0);
                    }
                    if (cacahe.Count > 0)
                    {
                        if (cacahe.ContainsKey(renderData.PageIndex - 1))
                        {
                            TextRectList = cacahe[renderData.PageIndex - 1];
                        }
                        else
                        {
                            TextRectList = null;
                        }
                    }
                    ClearDraw();
                }
            }
        }

        private void ClearDraw()
        {
            //笨办法,有好方案随时能换,目前是以触发属性改变事件的方式激活清空
            IsShowRect = !IsShowRect;
            IsShowRect = !IsShowRect;
        }

        public ScanViwerViewModel(IEventAggregator eventAggregator)
        {
            CloseTooltipCommand = new DelegateCommand(CloseTooltip);
            CancelEnhancedCommand = new DelegateCommand(CancelEnhanced);
            EnhancedFilePathList = new List<string>();
            eventAggregator.GetEvent<ScanEvent>().Subscribe(ChangeScanMode, e => e.Unicode == App.mainWindowViewModel.SelectedItem.Unicode);
            cacahe = new Dictionary<int, List<KeyValuePair<Rect, string>>>();
        }

        /// <summary>
        /// 区域识别
        /// </summary>
        public void AreaProcess(Rect AreaRect, Rect ImageRect)
        {
            COCREngine imEngine = new COCREngine(App.modelFolderPath);

            string path = App.CachePath.MergeFilePath;
            string name = Guid.NewGuid().ToString();
            path = Path.Combine(path, name);

            Directory.CreateDirectory(path);

            CPDFDocument CurrentDoc = PDFViewer.Document;

            CErrorCode error = imEngine.SetModel(language);

            cacahe.Remove(PDFViewer.CurrentIndex);
            string pageImagePath = Path.Combine(path, "Area");
            Directory.CreateDirectory(pageImagePath);
            pageImagePath = Path.Combine(pageImagePath, PDFViewer.CurrentIndex.ToString());

            if (IsEnhanced)
            {
                BitmapImage image = new BitmapImage(new Uri(EnhancedFilePathList[PDFViewer.CurrentIndex]));
                BgImage = new WriteableBitmap(image);
                double zoomWidth = AreaRect.Width / ImageRect.Width;
                double zoomHeight = AreaRect.Height / ImageRect.Height;
                double zoomX = (AreaRect.X - ImageRect.X) / ImageRect.Width;
                double zoomY = (AreaRect.Y - ImageRect.Y) / ImageRect.Height;
                Int32Rect rect = new Int32Rect((int)(BgImage.Width*zoomX), (int)(BgImage.Height*zoomY), (int)(BgImage.Width*zoomWidth), (int)(BgImage.Height*zoomHeight));
                byte[] renderData = new byte[(int)(BgImage.Width * BgImage.Height * 4)];

                BgImage.CopyPixels(rect, renderData, BgImage.BackBufferStride, 0);

                WriteableBitmap bitmap = new WriteableBitmap(rect.Width, rect.Height, DpiHelpers.Dpi, DpiHelpers.Dpi, PixelFormats.Bgra32, null);
                bitmap.WritePixels(new Int32Rect(0, 0, rect.Width, rect.Height), renderData, BgImage.BackBufferStride, 0);
                BitmapEncoder encoder = new PngBitmapEncoder();
                encoder.Frames.Add(BitmapFrame.Create(bitmap));

                using (FileStream imageStream = File.Create(pageImagePath))
                {
                    encoder.Save(imageStream);
                    imageStream.Flush();
                }
                error = imEngine.Process(pageImagePath);

                if (imEngine.OCRResultList == null)
                    return;

                List<KeyValuePair<Rect, string>> RectList = new List<KeyValuePair<Rect, string>>();
                foreach (COCRResult ocrResult in imEngine.OCRResultList)
                {
                    List<Point> rectPoints = new List<Point>();
                    for (int j = 0; j < ocrResult.position.Length; j += 2)
                    {
                        rectPoints.Add(new Point(ocrResult.position[j], ocrResult.position[j + 1]));
                    }
                    int left = (int)rectPoints.AsEnumerable().Min(x => x.X);
                    int right = (int)rectPoints.AsEnumerable().Max(x => x.X);
                    int top = (int)rectPoints.AsEnumerable().Min(x => x.Y);
                    int bottom = (int)rectPoints.AsEnumerable().Max(x => x.Y);

                    RectList.Add(new KeyValuePair<Rect, string>(new Rect(left + BgImage.Width * zoomX, top + BgImage.Height * zoomY, right - left, bottom - top), ocrResult.text));
                }
                cacahe.Add(PDFViewer.CurrentIndex, RectList);

                if (cacahe.Count > 0)
                {
                    if (cacahe.ContainsKey(PDFViewer.CurrentIndex))
                    {
                        TextRectList = cacahe[PDFViewer.CurrentIndex];
                    }
                    else
                    {
                        TextRectList = null;
                    }
                }
            }
            else
            {
                CPDFPage pdfPage = CurrentDoc.PageAtIndex(PDFViewer.CurrentIndex);
                double zoomWidth = AreaRect.Width / ImageRect.Width;
                double zoomHeight = AreaRect.Height / ImageRect.Height;
                double zoomX = (AreaRect.X- ImageRect.X) / ImageRect.Width;
                double zoomY = (AreaRect.Y- ImageRect.Y) / ImageRect.Height;

                float zoom = (float)(DpiHelpers.Dpi / 72D);
                int renderWidth = (int)(pdfPage.PageSize.Width* zoomWidth * zoom);
                int renderHeight = (int)(pdfPage.PageSize.Height* zoomHeight * zoom);
                int renderX = (int)(pdfPage.PageSize.Width* zoomX * zoom);
                int renderY = (int)(pdfPage.PageSize.Height* zoomY * zoom);
                byte[] renderData = new byte[renderWidth * renderHeight * 4];
                pdfPage.RenderPageBitmapWithMatrix(zoom, new Rect(renderX, renderY, renderWidth, renderHeight), 0xFFFFFFFF, renderData, 0);
                WriteableBitmap bitmap = new WriteableBitmap(renderWidth, renderHeight, DpiHelpers.Dpi, DpiHelpers.Dpi, PixelFormats.Bgra32, null);
                bitmap.WritePixels(new Int32Rect(0, 0, renderWidth, renderHeight), renderData, bitmap.BackBufferStride, 0);
                BitmapEncoder encoder = new PngBitmapEncoder();
                encoder.Frames.Add(BitmapFrame.Create(bitmap));

                using (FileStream imageStream = File.Create(pageImagePath))
                {
                    encoder.Save(imageStream);
                    imageStream.Flush();
                }

                error = imEngine.Process(pageImagePath);

                if (imEngine.OCRResultList == null)
                    return;

                List<KeyValuePair<Rect, string>> RectList = new List<KeyValuePair<Rect, string>>();
                foreach (COCRResult ocrResult in imEngine.OCRResultList)
                {
                    List<Point> rectPoints = new List<Point>();
                    for (int j = 0; j < ocrResult.position.Length; j += 2)
                    {
                        rectPoints.Add(new Point(ocrResult.position[j], ocrResult.position[j + 1]));
                    }
                    int left = (int)rectPoints.AsEnumerable().Min(x => x.X);
                    int right = (int)rectPoints.AsEnumerable().Max(x => x.X);
                    int top = (int)rectPoints.AsEnumerable().Min(x => x.Y);
                    int bottom = (int)rectPoints.AsEnumerable().Max(x => x.Y);

                    RectList.Add(new KeyValuePair<Rect, string>(new Rect(left+ renderX, top+ renderY, right - left, bottom - top), ocrResult.text));
                }
                cacahe.Add(PDFViewer.CurrentIndex, RectList);

                if (cacahe.Count > 0)
                {
                    if (cacahe.ContainsKey(PDFViewer.CurrentIndex))
                    {
                        TextRectList = cacahe[PDFViewer.CurrentIndex];
                    }
                    else
                    {
                        TextRectList = null;
                    }
                }
            }
            ClearDraw();
        }

        private void CancelEnhanced()
        {
            ShowTooltip = Visibility.Collapsed;
            IsEnhanced = false;
            CPDFPage pdfPage = PDFViewer.Document.PageAtIndex(PDFViewer.CurrentIndex);
            float zoom = (float)(DpiHelpers.Dpi / 72D);
            int renderWidth = (int)(pdfPage.PageSize.Width * zoom);
            int renderHeight = (int)(pdfPage.PageSize.Height * zoom);
            byte[] Data = new byte[renderWidth * renderHeight * 4];
            pdfPage.RenderPageBitmapWithMatrix(zoom, new Rect(0, 0, renderWidth, renderHeight), 0xFFFFFFFF, Data, 0);
            BgImage = new WriteableBitmap(renderWidth, renderHeight, DpiHelpers.Dpi, DpiHelpers.Dpi, PixelFormats.Bgra32, null);
            BgImage.WritePixels(new Int32Rect(0, 0, renderWidth, renderHeight), Data, BgImage.BackBufferStride, 0);

            if (cacahe.Count > 0)
            {
                if (cacahe.ContainsKey(PDFViewer.CurrentIndex))
                {
                    TextRectList = cacahe[PDFViewer.CurrentIndex];
                }
                else
                {
                    TextRectList = null;
                }
            }
        }
        private void CloseTooltip()
        {
            ShowTooltip = Visibility.Collapsed;
        }

        /// <summary>
        /// 根据Vm事件通知处理OCR与区域识别或者增强扫描事件
        /// </summary>
        /// <param name="e"></param>
        private void ChangeScanMode(ScanEventArgs e)
        {
            IsShowRect = false;
            switch (e.Mode)
            {
                case ScanMode.Unknown:
                    break;
                case ScanMode.Enhanced:
                    EnhancedProcess(e);
                    IsEnhanced = true;
                    break;
                case ScanMode.OCR:
                    if (IsEnhanced)
                    {
                        EnhancedOCRProcess(e);
                    }
                    else
                    {
                        OCRProcess(e);
                    }
                    break;
                case ScanMode.Area:
                    language = (COCRLanguage)e.ScanLanguage;
                    IsShowRect = true;
                    break;
                default:
                    break;
            }
        }

        /// <summary>
        /// 增强扫描
        /// </summary>
        private void EnhancedProcess(ScanEventArgs args)
        {
            CPDFDocument CurrentDoc = PDFViewer.Document;
            string path = App.CachePath.MergeFilePath;
            string name = Guid.NewGuid().ToString();
            path = Path.Combine(path, name);
            string EnhancePath = Path.Combine(path, "Enhance");

            Directory.CreateDirectory(path);
            Directory.CreateDirectory(EnhancePath);

            Task.Run(() =>
            {
                CIMEngine imEngine = new CIMEngine(App.modelFolderPath);
                CErrorCode error = imEngine.SetModel();

                //显示进度条
                App.mainWindowViewModel.IsProcessVisible = Visibility.Visible;
                App.mainWindowViewModel.MaxValue = CurrentDoc.PageCount;

                for (int i = 0; i < CurrentDoc.PageCount; i++)
                {
                    //刷新进度
                    App.Current.Dispatcher.Invoke(async () =>
                    {
                        App.mainWindowViewModel.Value = i;
                        await Task.Delay(5);
                    });

                    string pageImagePath = Path.Combine(path, i.ToString());
                    string pageEnhancePath = Path.Combine(EnhancePath, i.ToString() + ".png");
                    try
                    {
                        CPDFPage pdfPage = CurrentDoc.PageAtIndex(i);
                        float zoom = (float)(DpiHelpers.Dpi / 72D);
                        int renderWidth = (int)(pdfPage.PageSize.Width * zoom);
                        int renderHeight = (int)(pdfPage.PageSize.Height * zoom);
                        byte[] renderData = new byte[renderWidth * renderHeight * 4];
                        pdfPage.RenderPageBitmapWithMatrix(zoom, new Rect(0, 0, renderWidth, renderHeight), 0xFFFFFFFF, renderData, 0);
                        WriteableBitmap bitmap = new WriteableBitmap(renderWidth, renderHeight, DpiHelpers.Dpi, DpiHelpers.Dpi, PixelFormats.Bgra32, null);
                        bitmap.WritePixels(new Int32Rect(0, 0, renderWidth, renderHeight), renderData, bitmap.BackBufferStride, 0);
                        BitmapEncoder encoder = new PngBitmapEncoder();
                        encoder.Frames.Add(BitmapFrame.Create(bitmap));

                        using (FileStream imageStream = File.Create(pageImagePath))
                        {
                            encoder.Save(imageStream);
                            imageStream.Flush();
                        }

                        //File.Create(pageEnhancePath);
                        error = imEngine.Process(pageImagePath, pageEnhancePath);
                        EnhancedFilePathList.Add(pageEnhancePath);
                    }
                    catch
                    {

                    }
                }

                Application.Current.Dispatcher.Invoke(() =>
                {
                    BitmapImage image = new BitmapImage(new Uri(EnhancedFilePathList[PDFViewer.CurrentIndex]));
                    BgImage = new WriteableBitmap(image);
                });
                imEngine.Release();
                ShowTooltip = Visibility.Visible;

                //隐藏进度条
                App.mainWindowViewModel.IsProcessVisible = Visibility.Collapsed;
                App.mainWindowViewModel.MaxValue = 0;
                App.mainWindowViewModel.Value = 0;
            });
        }

        /// <summary>
        /// 使用当前文档OCR
        /// </summary>
        /// <param name="args"></param>
        private void OCRProcess(ScanEventArgs args)
        {
            Task.Run(() =>
            {
                COCREngine imEngine = new COCREngine(App.modelFolderPath);

                string path = App.CachePath.MergeFilePath;
                string name = Guid.NewGuid().ToString();
                path = Path.Combine(path, name);

                Directory.CreateDirectory(path);

                CPDFDocument CurrentDoc = PDFViewer.Document;

                CErrorCode error = imEngine.SetModel((COCRLanguage)args.ScanLanguage);

                cacahe.Clear();

                //显示进度条
                App.mainWindowViewModel.IsProcessVisible = Visibility.Visible;
                App.mainWindowViewModel.MaxValue = args.PageRange.Count;

                for (int i = 0; i < args.PageRange.Count; i++)
                {
                    //刷新进度
                    App.Current.Dispatcher.Invoke(async () =>
                    {
                        App.mainWindowViewModel.Value = i;
                        await Task.Delay(5);
                    });

                    string pageImagePath = Path.Combine(path, args.PageRange[i].ToString());

                    CPDFPage pdfPage = CurrentDoc.PageAtIndex(args.PageRange[i]);
                    float zoom = (float)(DpiHelpers.Dpi / 72D);
                    int renderWidth = (int)(pdfPage.PageSize.Width * zoom);
                    int renderHeight = (int)(pdfPage.PageSize.Height * zoom);
                    byte[] renderData = new byte[renderWidth * renderHeight * 4];
                    pdfPage.RenderPageBitmapWithMatrix(zoom, new Rect(0, 0, renderWidth, renderHeight), 0xFFFFFFFF, renderData, 0);
                    WriteableBitmap bitmap = new WriteableBitmap(renderWidth, renderHeight, DpiHelpers.Dpi, DpiHelpers.Dpi, PixelFormats.Bgra32, null);
                    bitmap.WritePixels(new Int32Rect(0, 0, renderWidth, renderHeight), renderData, bitmap.BackBufferStride, 0);
                    BitmapEncoder encoder = new PngBitmapEncoder();
                    encoder.Frames.Add(BitmapFrame.Create(bitmap));
                    using (FileStream imageStream = File.Create(pageImagePath))
                    {
                        encoder.Save(imageStream);
                        imageStream.Flush();
                    }

                    error = imEngine.Process(pageImagePath);

                    if (imEngine.OCRResultList == null)
                    {
                        App.mainWindowViewModel.IsProcessVisible = Visibility.Collapsed;
                        App.mainWindowViewModel.MaxValue = 0;
                        App.mainWindowViewModel.Value = 0;
                        return;
                    }

                    List<KeyValuePair<Rect, string>> RectList = new List<KeyValuePair<Rect, string>>();
                    foreach (COCRResult ocrResult in imEngine.OCRResultList)
                    {
                        List<Point> rectPoints = new List<Point>();
                        for (int j = 0; j < ocrResult.position.Length; j += 2)
                        {
                            rectPoints.Add(new Point(ocrResult.position[j], ocrResult.position[j + 1]));
                        }
                        int left = (int)rectPoints.AsEnumerable().Min(x => x.X);
                        int right = (int)rectPoints.AsEnumerable().Max(x => x.X);
                        int top = (int)rectPoints.AsEnumerable().Min(x => x.Y);
                        int bottom = (int)rectPoints.AsEnumerable().Max(x => x.Y);

                        RectList.Add(new KeyValuePair<Rect, string>(new Rect(left, top, right - left, bottom - top), ocrResult.text));
                    }
                    cacahe.Add(args.PageRange[i], RectList);
                }
                if (cacahe.Count > 0)
                {
                    if (cacahe.ContainsKey(PDFViewer.CurrentIndex))
                    {
                        TextRectList = cacahe[PDFViewer.CurrentIndex];
                    }
                    else
                    {
                        TextRectList = null;
                    }
                }
                imEngine.Release();
                //隐藏进度条
                App.mainWindowViewModel.IsProcessVisible = Visibility.Collapsed;
                App.mainWindowViewModel.MaxValue = 0;
                App.mainWindowViewModel.Value = 0;
            });
        }

        /// <summary>
        /// 使用增强扫描结果进行OCR
        /// </summary>
        /// <param name="args"></param>
        private void EnhancedOCRProcess(ScanEventArgs args)
        {
            Task.Run(() =>
            {
                COCREngine imEngine = new COCREngine(App.modelFolderPath);

                CErrorCode error = imEngine.SetModel((COCRLanguage)args.ScanLanguage);

                cacahe.Clear();
                for (int i = 0; i < args.PageRange.Count; i++)
                {
                    error = imEngine.Process(EnhancedFilePathList[args.PageRange[i]]);

                    if (imEngine.OCRResultList == null)
                        return;

                    List<KeyValuePair<Rect, string>> RectList = new List<KeyValuePair<Rect, string>>();
                    foreach (COCRResult ocrResult in imEngine.OCRResultList)
                    {
                        List<Point> rectPoints = new List<Point>();
                        for (int j = 0; j < ocrResult.position.Length; j += 2)
                        {
                            rectPoints.Add(new Point(ocrResult.position[j], ocrResult.position[j + 1]));
                        }
                        int left = (int)rectPoints.AsEnumerable().Min(x => x.X);
                        int right = (int)rectPoints.AsEnumerable().Max(x => x.X);
                        int top = (int)rectPoints.AsEnumerable().Min(x => x.Y);
                        int bottom = (int)rectPoints.AsEnumerable().Max(x => x.Y);

                        RectList.Add(new KeyValuePair<Rect, string>(new Rect(left, top, right - left, bottom - top), ocrResult.text));
                    }
                    cacahe.Add(args.PageRange[i], RectList);
                }
                if (cacahe.Count > 0)
                {
                    if (cacahe.ContainsKey(PDFViewer.CurrentIndex))
                    {
                        TextRectList = cacahe[PDFViewer.CurrentIndex];
                    }
                    else
                    {
                        TextRectList = null;
                    }
                }
                imEngine.Release();
            });
        }

    }
}