using System.Collections;
using Xamarin.Forms;

namespace Billing.UI
{
    public class GroupStackLayout : Layout<View>
    {
        public static readonly BindableProperty GroupHeaderTemplateProperty = BindableProperty.Create(nameof(GroupHeaderTemplate), typeof(DataTemplate), typeof(GroupStackLayout));
        public static readonly BindableProperty ItemTemplateProperty = BindableProperty.Create(nameof(ItemTemplate), typeof(DataTemplate), typeof(GroupStackLayout));
        public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create(nameof(ItemsSource), typeof(IList), typeof(GroupStackLayout), propertyChanged: OnItemsSourcePropertyChanged);
        public static readonly BindableProperty SpacingProperty = BindableProperty.Create(nameof(Spacing), typeof(double), typeof(GroupStackLayout), defaultValue: 4d);
        public static readonly BindableProperty RowHeightProperty = BindableProperty.Create(nameof(RowHeight), typeof(double), typeof(GroupStackLayout), defaultValue: 32d);
        public static readonly BindableProperty GroupHeightProperty = BindableProperty.Create(nameof(GroupHeight), typeof(double), typeof(GroupStackLayout), defaultValue: 24d);

        public DataTemplate GroupHeaderTemplate
        {
            get => (DataTemplate)GetValue(GroupHeaderTemplateProperty);
            set => SetValue(GroupHeaderTemplateProperty, value);
        }
        public DataTemplate ItemTemplate
        {
            get => (DataTemplate)GetValue(ItemTemplateProperty);
            set => SetValue(ItemTemplateProperty, value);
        }
        public IList ItemsSource
        {
            get => (IList)GetValue(ItemsSourceProperty);
            set => SetValue(ItemsSourceProperty, value);
        }
        public double Spacing
        {
            get => (double)GetValue(SpacingProperty);
            set => SetValue(SpacingProperty, value);
        }
        public double RowHeight
        {
            get => (double)GetValue(RowHeightProperty);
            set => SetValue(RowHeightProperty, value);
        }
        public double GroupHeight
        {
            get => (double)GetValue(GroupHeightProperty);
            set => SetValue(GroupHeightProperty, value);
        }

        private static void OnItemsSourcePropertyChanged(BindableObject obj, object old, object @new)
        {
            var stack = (GroupStackLayout)obj;
            stack.lastWidth = -1;
            if (@new == null)
            {
                //stack.cachedLayout.Clear();
                stack.Children.Clear();
                stack.InvalidateLayout();
            }
            else if (@new is IList list)
            {
                stack.freezed = true;
                //stack.cachedLayout.Clear();
                stack.Children.Clear();
                var groupTemplate = stack.GroupHeaderTemplate;
                var itemTemplate = stack.ItemTemplate;
                for (var i = 0; i < list.Count; i++)
                {
                    var item = list[i];
                    if (item is IList sublist)
                    {
                        if (groupTemplate != null)
                        {
                            var child = groupTemplate.CreateContent();
                            if (child is View view)
                            {
                                view.BindingContext = item;
                                stack.Children.Add(view);
                            }
                        }
                        foreach (var it in sublist)
                        {
                            var child = itemTemplate.CreateContent();
                            if (child is View view)
                            {
                                view.BindingContext = it;
                                stack.Children.Add(view);
                            }
                        }
                    }
                    else
                    {
                        var child = itemTemplate.CreateContent();
                        if (child is View view)
                        {
                            view.BindingContext = list[i];
                            stack.Children.Add(view);
                        }
                    }
                }
                stack.freezed = false;
                stack.UpdateChildrenLayout();
                stack.InvalidateLayout();
            }
        }

        //private readonly Dictionary<View, Rectangle> cachedLayout = new();

        private bool freezed;
        private double lastWidth = -1;
        private SizeRequest lastSizeRequest;

        public void Refresh(IList list)
        {
            OnItemsSourcePropertyChanged(this, null, list);
        }

        protected override void LayoutChildren(double x, double y, double width, double height)
        {
            if (freezed)
            {
                return;
            }
            var source = ItemsSource;
            if (source == null || source.Count <= 0)
            {
                return;
            }
            var padding = Padding;
            var spacing = Spacing;
            var lastHeight = padding.Top;
            var rowHeight = RowHeight;
            var groupHeight = GroupHeight;
            foreach (var item in Children)
            {
                //var measured = item.Measure(width, height, MeasureFlags.IncludeMargins);
                //var rect = new Rectangle(
                //    0, lastHeight, width,
                //    measured.Request.Height);
                //if (cachedLayout.TryGetValue(item, out var v))
                //{
                //    if (v != rect)
                //    {
                //        cachedLayout[item] = rect;
                //        item.Layout(rect);
                //    }
                //}
                //else
                //{
                //    cachedLayout.Add(item, rect);
                //    item.Layout(rect);
                //}
                double itemHeight;
                if (item.BindingContext is IList)
                {
                    itemHeight = groupHeight;
                }
                else
                {
                    itemHeight = rowHeight;
                }
                var rect = new Rectangle(padding.Left, lastHeight, width, itemHeight);
                //item.Layout(rect);
                LayoutChildIntoBoundingRegion(item, rect);
                lastHeight += itemHeight + spacing;
            }
        }

        protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint)
        {
            if (lastWidth == widthConstraint)
            {
                return lastSizeRequest;
            }
            lastWidth = widthConstraint;
            var spacing = Spacing;
            var lastHeight = 0d;
            var rowHeight = RowHeight;
            var groupHeight = GroupHeight;
            foreach (var item in Children)
            {
                //var measured = item.Measure(widthConstraint, heightConstraint, MeasureFlags.IncludeMargins);
                //lastHeight += measured.Request.Height + spacing;
                if (item.BindingContext is IList)
                {
                    lastHeight += groupHeight + spacing;
                }
                else
                {
                    lastHeight += rowHeight + spacing;
                }
            }
            var padding = Padding;
            lastSizeRequest = new SizeRequest(new Size(widthConstraint, lastHeight + padding.Top + padding.Bottom));
            return lastSizeRequest;
        }
    }
}
