using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
namespace MECF.Framework.UI.Client.Ctrlib.Controls
{
    public class VirtualizingWrapPanel : VirtualizingPanel, IScrollInfo
    {
        private TranslateTransform trans = new TranslateTransform();
        public VirtualizingWrapPanel()
        {
            this.RenderTransform = trans;
        }
        #region DependencyProperties
        public static readonly DependencyProperty ChildWidthProperty = DependencyProperty.RegisterAttached("ChildWidth", typeof(double), typeof(VirtualizingWrapPanel), new FrameworkPropertyMetadata(200.0, FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsArrange));
        public static readonly DependencyProperty ChildHeightProperty = DependencyProperty.RegisterAttached("ChildHeight", typeof(double), typeof(VirtualizingWrapPanel), new FrameworkPropertyMetadata(200.0, FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsArrange));
        //鼠标每一次滚动 UI上的偏移
        public static readonly DependencyProperty ScrollOffsetProperty = DependencyProperty.RegisterAttached("ScrollOffset", typeof(int), typeof(VirtualizingWrapPanel), new PropertyMetadata(10));
        public int ScrollOffset
        {
            get { return Convert.ToInt32(GetValue(ScrollOffsetProperty)); }
            set { SetValue(ScrollOffsetProperty, value); }
        }
        public double ChildWidth
        {
            get => Convert.ToDouble(GetValue(ChildWidthProperty));
            set => SetValue(ChildWidthProperty, value);
        }
        public double ChildHeight
        {
            get => Convert.ToDouble(GetValue(ChildHeightProperty));
            set => SetValue(ChildHeightProperty, value);
        }
        public Orientation Orientation
        {
            get => (Orientation)(GetValue(OrientationProperty));
            set => SetValue(OrientationProperty, value);
        }
        public static readonly DependencyProperty OrientationProperty = DependencyProperty.RegisterAttached("Orientation", typeof(Orientation), typeof(VirtualizingWrapPanel), new FrameworkPropertyMetadata(Orientation.Horizontal, FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsArrange));
        #endregion
        int GetItemCount(DependencyObject element)
        {
            var itemsControl = ItemsControl.GetItemsOwner(element);
            return itemsControl.HasItems ? itemsControl.Items.Count : 0;
        }
        int CalculateChildrenPerRow(Size availableSize)
        {
            int childPerRow = 0;
            if (availableSize.Width == double.PositiveInfinity)
                childPerRow = this.Children.Count;
            else
            {
                childPerRow = Math.Max(1, Convert.ToInt32(Math.Floor(availableSize.Width / this.ChildWidth)));
            }
            return childPerRow;
        }
        int CalculateChildrenPerColumn(Size availableSize)
        {
            int childPerColumn = 0;
            if (availableSize.Height == double.PositiveInfinity)
                childPerColumn = this.Children.Count;
            else
            {
                childPerColumn = Math.Max(1, Convert.ToInt32(Math.Floor(availableSize.Height / this.ChildHeight)));
            }
            return childPerColumn;
        }
        /// 
        /// width不超过availableSize的情况下,自身实际需要的Size(高度可能会超出availableSize)
        /// 
        /// 
        /// 
        /// 
        Size CalculateExtent(Size availableSize, int itemsCount)
        {
            if (Orientation == Orientation.Horizontal)
            {
                int childPerRow = CalculateChildrenPerRow(availableSize);//现有宽度下 一行可以最多容纳多少个
                return new Size(childPerRow * this.ChildWidth, this.ChildHeight * Math.Ceiling(Convert.ToDouble(itemsCount) / childPerRow));
            }
            else if (Orientation == Orientation.Vertical)
            {
                int childPerColumn = CalculateChildrenPerColumn(availableSize);//现有高度下 一行可以最多容纳多少个
                return new Size(Math.Ceiling(Convert.ToDouble(itemsCount) / childPerColumn) * this.ChildWidth, this.ChildHeight * childPerColumn);
            }
            else
            {
                return availableSize;
            }
        }
        /// 
        /// 更新滚动条
        /// 
        /// 
        void UpdateScrollInfo(Size availableSize)
        {
            var extent = CalculateExtent(availableSize, GetItemCount(this));//extent 自己实际需要
            if (extent != this.extent)
            {
                this.extent = extent;
                this.ScrollOwner.InvalidateScrollInfo();
            }
            if (availableSize != this.viewPort)
            {
                this.viewPort = availableSize;
                this.ScrollOwner.InvalidateScrollInfo();
            }
        }
        /// 
        /// 获取所有item,在可视区域内第一个item和最后一个item的索引
        /// 
        /// 
        /// 
        void GetVisiableRange(ref int firstIndex, ref int lastIndex)
        {
            if (Orientation == Orientation.Horizontal)
            {
                int childPerRow = CalculateChildrenPerRow(this.extent);
                firstIndex = Convert.ToInt32(Math.Floor(this.offset.Y / this.ChildHeight)) * childPerRow;
                if (!double.IsInfinity(this.viewPort.Height))
                {
                    lastIndex = Convert.ToInt32(Math.Ceiling((this.offset.Y + this.viewPort.Height) / this.ChildHeight)) * childPerRow - 1;
                    int itemsCount = GetItemCount(this);
                    if (lastIndex >= itemsCount)
                        lastIndex = itemsCount - 1;
                }
            }
            else if (Orientation == Orientation.Vertical)
            {
                int childPerColumn = CalculateChildrenPerColumn(this.extent);
                firstIndex = Convert.ToInt32(Math.Ceiling(this.offset.X / this.ChildWidth)) * childPerColumn;
                if (!double.IsInfinity(this.viewPort.Width))
                {
                    lastIndex = Convert.ToInt32(Math.Ceiling((this.offset.X + this.viewPort.Width) / this.ChildWidth)) * childPerColumn - 1;
                    int itemsCount = GetItemCount(this);
                    if (lastIndex >= itemsCount)
                        lastIndex = itemsCount - 1;
                }
            }
        }
        /// 
        /// 将不在可视区域内的item 移除
        /// 
        /// 可视区域开始索引
        /// 可视区域结束索引
        void CleanUpItems(int startIndex, int endIndex)
        {
            var children = this.InternalChildren;
            var generator = this.ItemContainerGenerator;
            for (int i = children.Count - 1; i >= 0; i--)
            {
                var childGeneratorPosi = new GeneratorPosition(i, 0);
                int itemIndex = generator.IndexFromGeneratorPosition(childGeneratorPosi);
                if (itemIndex < startIndex || itemIndex > endIndex)
                {
                    generator.Remove(childGeneratorPosi, 1);
                    RemoveInternalChildRange(i, 1);
                }
            }
        }
        /// 
        /// scroll/availableSize/添加删除元素 改变都会触发  edit元素不会改变
        /// 
        /// 
        /// 
        protected override Size MeasureOverride(Size availableSize)
        {
            this.UpdateScrollInfo(availableSize);//availableSize更新后,更新滚动条
            int firstVisiableIndex = 0, lastVisiableIndex = 0;
            GetVisiableRange(ref firstVisiableIndex, ref lastVisiableIndex);//availableSize更新后,获取当前viewport内可放置的item的开始和结束索引  firstIdnex-lastIndex之间的item可能部分在viewport中也可能都不在viewport中。
            UIElementCollection children = this.InternalChildren;//因为配置了虚拟化,所以children的个数一直是viewport区域内的个数,如果没有虚拟化则是ItemSource的整个的个数
            IItemContainerGenerator generator = this.ItemContainerGenerator;
            //获得第一个可被显示的item的位置
            GeneratorPosition startPosi = generator.GeneratorPositionFromIndex(firstVisiableIndex);
            int childIndex = (startPosi.Offset == 0) ? startPosi.Index : startPosi.Index + 1;//startPosi在chilren中的索引
            using (generator.StartAt(startPosi, GeneratorDirection.Forward, true))
            {
                int itemIndex = firstVisiableIndex;
                while (itemIndex <= lastVisiableIndex)//生成lastVisiableIndex-firstVisiableIndex个item
                {
                    bool newlyRealized = false;
                    var child = generator.GenerateNext(out newlyRealized) as UIElement;
                    if (child == null) return availableSize;
                    if (newlyRealized)
                    {
                        if (childIndex >= children.Count)
                            base.AddInternalChild(child);
                        else
                        {
                            base.InsertInternalChild(childIndex, child);
                        }
                        generator.PrepareItemContainer(child);
                    }
                    else
                    {
                        //处理 正在显示的child被移除了这种情况
                        if (!child.Equals(children[childIndex]))
                        {
                            base.RemoveInternalChildRange(childIndex, 1);
                        }
                    }
                    child.Measure(new Size(this.ChildWidth, this.ChildHeight));
                    //child.DesiredSize;//child想要的size
                    itemIndex++;
                    childIndex++;
                }
            }
            CleanUpItems(firstVisiableIndex, lastVisiableIndex);
            return new Size(double.IsInfinity(availableSize.Width) ? 0 : availableSize.Width, double.IsInfinity(availableSize.Height) ? 0 : availableSize.Height);//自身想要的size
        }
        protected override Size ArrangeOverride(Size finalSize)
        {
            //Debug.WriteLine("----ArrangeOverride");
            var generator = this.ItemContainerGenerator;
            UpdateScrollInfo(finalSize);
            if (Orientation == Orientation.Horizontal)
            {
                int childPerRow = CalculateChildrenPerRow(finalSize);
                double availableItemWidth = finalSize.Width / childPerRow;
                for (int i = 0; i <= this.Children.Count - 1; i++)
                {
                    var child = this.Children[i];
                    int itemIndex = generator.IndexFromGeneratorPosition(new GeneratorPosition(i, 0));
                    int row = 0;
                    int column = 0;
                    row = itemIndex / childPerRow;//current row
                    column = itemIndex % childPerRow;
                    double xCorrdForItem = 0;
                    xCorrdForItem = column * availableItemWidth + (availableItemWidth - this.ChildWidth) / 2;
                    Rect rec = new Rect(xCorrdForItem, row * this.ChildHeight, this.ChildWidth, this.ChildHeight);
                    child.Arrange(rec);
                }
                return finalSize;
            }
            else if (Orientation == Orientation.Vertical)
            {
                int childPerColumn = CalculateChildrenPerColumn(finalSize);
                double availableItemHeight = finalSize.Height / childPerColumn;
                for (int i = 0; i <= this.Children.Count - 1; i++)
                {
                    var child = this.Children[i];
                    int itemIndex = generator.IndexFromGeneratorPosition(new GeneratorPosition(i, 0));
                    int row = 0;
                    int column = 0;
                    row = itemIndex % childPerColumn;
                    column = i / childPerColumn;
                    double xCorrdForItem = 0;
                    xCorrdForItem = row * availableItemHeight + (availableItemHeight - this.ChildHeight) / 2;
                    Rect rec = new Rect(column * this.ChildWidth, row * ChildHeight, this.ChildWidth, this.ChildHeight);
                    child.Arrange(rec);
                }
                return finalSize;
            }
            else { return finalSize; }
        }
        protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
        {
            base.OnRenderSizeChanged(sizeInfo);
            this.SetVerticalOffset(this.VerticalOffset);
        }
        protected override void OnClearChildren()
        {
            base.OnClearChildren();
            this.SetVerticalOffset(0);
        }
        protected override void BringIndexIntoView(int index)
        {
            if (index < 0 || index >= Children.Count)
                throw new ArgumentOutOfRangeException();
            if (Orientation == Orientation.Horizontal)
            {
                int row = index / CalculateChildrenPerRow(RenderSize);
                SetVerticalOffset(row * this.ChildHeight);
            }
            else if (Orientation == Orientation.Vertical)
            {
                int column = index / CalculateChildrenPerColumn(RenderSize);
                SetVerticalOffset(column * this.ChildWidth);
            }
        }
        #region IScrollInfo Interface
        public bool CanVerticallyScroll { get; set; }
        public bool CanHorizontallyScroll { get; set; }
        private Size extent = new Size(0, 0);
        public double ExtentWidth => this.extent.Width;
        public double ExtentHeight => this.extent.Height;
        private Size viewPort = new Size(0, 0);
        public double ViewportWidth => this.viewPort.Width;
        public double ViewportHeight => this.viewPort.Height;
        private Point offset;
        public double HorizontalOffset => this.offset.Y;
        public double VerticalOffset => this.offset.X;
        public ScrollViewer ScrollOwner { get; set; } = new ScrollViewer();
        public void LineDown()
        {
            this.SetVerticalOffset(this.HorizontalOffset + this.ScrollOffset);
        }
        public void LineLeft()
        {
            this.SetVerticalOffset(this.VerticalOffset - this.ChildWidth);
        }
        public void LineRight()
        {
            this.SetVerticalOffset(this.VerticalOffset + this.ChildWidth);
        }
        public void LineUp()
        {
            this.SetVerticalOffset(this.HorizontalOffset - this.ScrollOffset);
        }
        public Rect MakeVisible(Visual visual, Rect rectangle)
        {
            return new Rect();
        }
        public void MouseWheelDown()
        {
            this.SetVerticalOffset(this.VerticalOffset + this.ScrollOffset);
        }
        public void MouseWheelLeft()
        {
            throw new NotImplementedException();
        }
        public void MouseWheelRight()
        {
            throw new NotImplementedException();
        }
        public void MouseWheelUp()
        {
            this.SetVerticalOffset(this.VerticalOffset - this.ScrollOffset);
        }
        //临时借用到ScrollToLeftEnd
        public void PageDown()
        {
            this.SetVerticalOffset(this.extent.Width - this.viewPort.Width);
            //this.SetVerticalOffset(this.HorizontalOffset + this.viewPort.Height);
        }
        public void PageLeft()
        {
            this.SetVerticalOffset(this.VerticalOffset - this.viewPort.Width);
        }
        public void PageRight()
        {
            this.SetVerticalOffset(this.VerticalOffset + this.viewPort.Width);
        }
        //临时借用到ScrollToRightEnd
        public void PageUp()
        {
            this.SetVerticalOffset(0);
            //  this.SetVerticalOffset(this.HorizontalOffset - this.viewPort.Height);
        }
        public void ScrollToLeftEnd()
        {
            this.SetVerticalOffset(0);
        }
        public void SetHorizontalOffset(double offset)
        {
           
        }
        public void SetVerticalOffset(double offset)
        {
            if (Orientation == Orientation.Horizontal)
            {
                if (offset < 0 || this.viewPort.Height >= this.extent.Height)
                    offset = 0;
                else
                    if (offset + this.viewPort.Height >= this.extent.Height)
                    offset = this.extent.Height - this.viewPort.Height;
                this.offset.Y = offset;
                this.ScrollOwner?.InvalidateScrollInfo();
                this.trans.Y = -offset;
                this.InvalidateMeasure();
            }
            else if (Orientation == Orientation.Vertical)
            {
                if (offset < 0 || this.viewPort.Width >= this.extent.Width)
                    offset = 0;
                else
                       if (offset + this.viewPort.Width >= this.extent.Width)
                    offset = this.extent.Width - this.viewPort.Width;
                this.offset.X = offset;
                this.ScrollOwner?.InvalidateScrollInfo();
               // this.trans.X = -offset;
                this.InvalidateMeasure();
            }
            //接下来会触发MeasureOverride()
        }
        #endregion
    }
}