using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;
using System.Diagnostics;
using System.Threading;
using System.Reflection;

using pdftron;
using pdftron.PDF;
using pdftron.Common;

namespace PDFViewWPFTestCS
{
    /// <summary>
    /// Interaction logic for ThumbnailsControl.xaml
    /// </summary>
    public partial class ThumbnailsControl : UserControl
    {
        
        public PDFViewWPF _pdfviewer;
        private PDFDoc _doc;
        public bool isInitialized = false;
        private const int THUMBNAIL_WIDTH = 150;
        private const int THUMBNAIL_HEIGHT = 150;
        private bool isSelectionChangeProgrammatic = false;

        List<object> TimerInfo = new List<object>();

        List<int> PageThumbnailsToRequest = new List<int>();
        List<int> VisiblePageIndexes = new List<int>();

        public ThumbnailsControl()
        {
            InitializeComponent();
        }

        private void ThumbnailListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (e.AddedItems.Count > 0)
            {
                ListBoxItem item = e.AddedItems[0] as ListBoxItem;
                ThumbnailListBox.ScrollIntoView(ThumbnailListBox.SelectedItem);
                // update current page number
                if (!isSelectionChangeProgrammatic)
                {
                    _pdfviewer.SetCurrentPage((int)item.Tag + 1);
                }
            }
        }

        public void Stop()
        {
            ThumbnailListBox.Items.Clear();
            VisiblePageIndexes.Clear();
            _pdfviewer.CancelAllThumbRequests();
        }

        public void Reactivate()
        {
            PopulateThumbnailList(_pdfviewer);
            ItemsInViewHitTest();
        }

        public void PopulateThumbnailList(PDFViewWPF viewer)
        {
            _doc = _pdfviewer.GetDoc();
            ThumbnailListBox.Items.Clear();
            VisiblePageIndexes.Clear();
            _pdfviewer.CancelAllThumbRequests();

            for (int i = 0; i < viewer.GetPageCount(); ++i)
            {
                System.Windows.Controls.Image img = new System.Windows.Controls.Image()
                {
                    Margin = new Thickness(0, 0, 5, 0),
                    Stretch = Stretch.Uniform,
                };
                Border border = new Border()
                {
                    BorderBrush = Brushes.Black,
                    Child = img,                    
                };
                Viewbox viewBox = new Viewbox()
                {
                    Margin = new Thickness(0, 5, 0, 0),
                    Stretch = System.Windows.Media.Stretch.Uniform,
                    Width = THUMBNAIL_WIDTH,
                    Height = THUMBNAIL_HEIGHT,
                    Child = border,
                };
                TextBlock text = new TextBlock()
                {
                    HorizontalAlignment = System.Windows.HorizontalAlignment.Center,
                    Text = (i + 1).ToString(),
                    Foreground = Brushes.Black
                };
                StackPanel panel = new StackPanel();
                panel.Children.Add(viewBox);
                panel.Children.Add(text);                        
                       
                ListBoxItem item = new ListBoxItem();
                item.Content = panel;
                item.Tag = i;

                ThumbnailListBox.Items.Add(item);
            }            
        }

        private void ItemsInViewHitTest()
        {
            try
            {
                ScrollViewer sv = GetScrollHost(ThumbnailListBox);
                if (sv == null) return;
                if (VisualTreeHelper.GetParent(this) == null)
                    return;
                List<int> pagesOnScreen = new List<int>();
                PageThumbnailsToRequest.Clear();

                for(int i=0;i<ThumbnailListBox.Items.Count;++i)
                {
                    ListBoxItem item = ThumbnailListBox.Items[i] as ListBoxItem;
                    ListBoxItem listboxitem = ThumbnailListBox.ItemContainerGenerator.ContainerFromIndex(i) as ListBoxItem;
                    System.Windows.Controls.Image img = GetImageElement(item);
                    if(ViewportHelper.IsInViewport(sv,listboxitem))
                    {
                        if (img.Source == null && !VisiblePageIndexes.Contains(i))
                        {
                            VisiblePageIndexes.Add(i);
                            _pdfviewer.GetThumbAsync(i + 1);
                            System.Diagnostics.Debug.WriteLine("Page {0} is visible, asking for thumb", (i + 1));
                        }
                        else if (img.Source == null)
                        {
                            System.Diagnostics.Debug.WriteLine("Page {0} is visible, but we're already waiting for thumb", (i + 1));
                        }
                    }
                    else
                    {
                        if (VisiblePageIndexes.Contains(i))
                        {
                            System.Windows.Controls.Image image = GetImageElement(ThumbnailListBox.Items[i] as ListBoxItem);
                            if (image.Source != null)
                            {
                                image.Source = null;
                                (image.Parent as Border).BorderBrush = Brushes.Transparent;
                                System.Diagnostics.Debug.WriteLine("Page {0} is out of range, removed thumb", (i + 1));
                            }
                            else
                            {
                                System.Diagnostics.Debug.WriteLine("Page {0} is out of range, but had no thumb", (i + 1));
                            }
                            VisiblePageIndexes.Remove(i);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.Assert(true, "ItemsInViewHitTest threw an exception", ex.StackTrace);
            }
        }

        private void _pdfviewer_CurrentPageNumberChanged(PDFViewWPF viewer, int currentPage, int totalPages)
        {
            isSelectionChangeProgrammatic = true;
            UpdateSelectedIndex();
            isSelectionChangeProgrammatic = false;
        }

        void _pdfviewer_OnThumbnailGenerated(int pageNumber, byte[] thumb, int w, int h)
        {
            try
            {
                if (ThumbnailListBox.Items.IsEmpty)
                {
                    return;
                }
                ScrollViewer sv = GetScrollHost(ThumbnailListBox);

                int index = pageNumber - 1;
                ListBoxItem item = ThumbnailListBox.Items[index] as ListBoxItem;
                ListBoxItem listboxitem = ThumbnailListBox.ItemContainerGenerator.ContainerFromIndex(index) as ListBoxItem;
                if (ViewportHelper.IsInViewport(sv, listboxitem))
                {
                    System.Diagnostics.Debug.WriteLine("Got thumbnail for page {0}. It is visible, so adding thumb", pageNumber);
                    PixelFormat fmt = PixelFormats.Bgra32;
                    BitmapSource bps = BitmapSource.Create(w, h, 96.0, 96.0, fmt, null, thumb, (w * fmt.BitsPerPixel + 7) / 8);

                    System.Windows.Controls.Image image = GetImageElement(ThumbnailListBox.Items[index] as ListBoxItem);
                    image.Source = bps;
                    (image.Parent as Border).BorderThickness = new Thickness(5);
                    (image.Parent as Border).BorderBrush = Brushes.Black;
                }
                else
                {
                    System.Diagnostics.Debug.WriteLine("Got thumbnail for page {0}. It is NOT visible, so ignoring thumb", pageNumber);
                }
            }
            catch (Exception e) 
            {
                System.Diagnostics.Debug.WriteLine(e.ToString());
            }
           
            
        }

        private void UpdateSelectedIndex()
        {
            if (ThumbnailListBox.Items.Count == 0)
                ThumbnailListBox.SelectedIndex = -1;
            else if (ThumbnailListBox.Items.Count < _pdfviewer.GetCurrentPage())
                this.ThumbnailListBox.SelectedIndex = 0;
            else if (ThumbnailListBox.SelectedIndex != _pdfviewer.GetCurrentPage() - 1)
                ThumbnailListBox.SelectedIndex = _pdfviewer.GetCurrentPage() - 1;
        }

        public void LoadThumbnails(PDFViewWPF view)
        {
            _pdfviewer = view;
            if (!isInitialized)
            {
                this.UpdateThumbnailList();
                isInitialized = true;
                _pdfviewer.CurrentPageNumberChanged+= _pdfviewer_CurrentPageNumberChanged;
                _pdfviewer.OnThumbnailGenerated += _pdfviewer_OnThumbnailGenerated;
            }
            if (_pdfviewer == null || _doc == null)
            {
                return;
            }
            ThumbnailListBox.ScrollIntoView(ThumbnailListBox.SelectedItem);
            ItemsInViewHitTest();
        }

        void thumbnailScrollViewer_ScrollChanged(object sender, ScrollChangedEventArgs e)
        {
            ItemsInViewHitTest();
        }

        private void UpdateThumbnailList()
        {
            if (_pdfviewer == null) return;
            _doc = _pdfviewer.GetDoc();
            ThumbnailListBox.Items.Clear();
            if (_doc == null || _doc.GetPageCount() == 0)
                return;
            PopulateThumbnailList(_pdfviewer);
        }

        private System.Windows.Controls.Image GetImageElement(ListBoxItem item)
        {
            Viewbox viewBox = (item.Content as StackPanel).Children[0] as Viewbox;
            System.Windows.Controls.Image img = (viewBox.Child as Border).Child as System.Windows.Controls.Image;
            return img;
        }

        private StackPanel GetPanel(ListBoxItem item)
        {
            StackPanel panel = item.Content as StackPanel;
            return panel;
        }

        private ScrollViewer GetScrollHost(ListBox listBox)
        {
            var x = listBox.Template.FindName("PART_scrollviewer", listBox);
            ScrollViewer viewer = x as ScrollViewer;
            if (viewer != null)
                return viewer;
            return null;
        }


    }

    public static class ViewportHelper
    {
        public static bool IsInViewport(ScrollViewer sv, Control item)
        {
            if (item == null) return false;
            ItemsControl itemsControl = null;
            if (item is ListBoxItem)
                itemsControl = ItemsControl.ItemsControlFromItemContainer(item) as ListBox;
            else
                throw new NotSupportedException(item.GetType().Name);
            ScrollContentPresenter scrollContentPresenter = (ScrollContentPresenter)sv.Template.FindName("PART_ScrollContentPresenter", sv);
            MethodInfo isInViewportMethod = sv.GetType().GetMethod("IsInViewport", BindingFlags.NonPublic | BindingFlags.Instance);
            return (bool)isInViewportMethod.Invoke(sv, new object[] { scrollContentPresenter, item });
        }
    }
}
