using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
namespace WpfBanner
{
    /// <summary>
    /// Adapted from: https://rachel53461.wordpress.com/2011/09/17/wpf-grids-rowcolumn-count-properties/
    /// </summary>
    public static class GridHelpers
    {
        #region RowCount Property
        /// <summary>
        /// Adds the specified number of Rows to RowDefinitions.
        /// Default Height is Auto
        /// </summary>
        public static readonly DependencyProperty RowCountProperty =
            DependencyProperty.RegisterAttached(
                "RowCount", typeof(int), typeof(GridHelpers),
                new PropertyMetadata(-1, RowCountChanged));
        public static int GetRowCount(DependencyObject obj)
            => (int)obj.GetValue(RowCountProperty);
        public static void SetRowCount(DependencyObject obj, int value)
            => obj.SetValue(RowCountProperty, value);
        public static void RowCountChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            if (!(obj is Grid grid) || (int)e.NewValue < 0)
                return;
            grid.RowDefinitions.Clear();
            for (int i = 0; i < (int)e.NewValue; i++)
                grid.RowDefinitions.Add(new RowDefinition() { Height = GridLength.Auto });
            SetStarRows(grid);
            RegisterForLayoutUpdate(grid);
        }
        #endregion
        #region ColumnCount Property
        /// <summary>
        /// Adds the specified number of Columns to ColumnDefinitions.
        /// Default Width is Auto
        /// </summary>
        public static readonly DependencyProperty ColumnCountProperty =
            DependencyProperty.RegisterAttached(
                "ColumnCount", typeof(int), typeof(GridHelpers),
                new PropertyMetadata(-1, ColumnCountChanged));
        public static int GetColumnCount(DependencyObject obj)
            => (int)obj.GetValue(ColumnCountProperty);
        public static void SetColumnCount(DependencyObject obj, int value)
            => obj.SetValue(ColumnCountProperty, value);
        public static void ColumnCountChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            if (!(obj is Grid grid) || (int)e.NewValue < 0)
                return;
            grid.ColumnDefinitions.Clear();
            for (int i = 0; i < (int)e.NewValue; i++)
                grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = GridLength.Auto });
            SetStarColumns(grid);
            RegisterForLayoutUpdate(grid);
        }
        #endregion
        #region StarRows Property
        /// <summary>
        /// Makes the specified Row's Height equal to Star.
        /// Can set on multiple Rows
        /// </summary>
        public static readonly DependencyProperty StarRowsProperty =
            DependencyProperty.RegisterAttached(
                "StarRows", typeof(string), typeof(GridHelpers),
                new PropertyMetadata(string.Empty, StarRowsChanged));
        public static string GetStarRows(DependencyObject obj)
            => (string)obj.GetValue(StarRowsProperty);
        public static void SetStarRows(DependencyObject obj, string value)
            => obj.SetValue(StarRowsProperty, value);
        public static void StarRowsChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            if (!(obj is Grid grid) || string.IsNullOrEmpty(e.NewValue.ToString()))
                return;
            SetStarRows(grid);
            RegisterForLayoutUpdate(grid);
        }
        #endregion
        #region StarColumns Property
        /// <summary>
        /// Makes the specified Column's Width equal to Star.
        /// Can set on multiple Columns
        /// </summary>
        public static readonly DependencyProperty StarColumnsProperty =
            DependencyProperty.RegisterAttached(
                "StarColumns", typeof(string), typeof(GridHelpers),
                new PropertyMetadata(string.Empty, StarColumnsChanged));
        public static string GetStarColumns(DependencyObject obj)
            => (string)obj.GetValue(StarColumnsProperty);
        public static void SetStarColumns(DependencyObject obj, string value)
            => obj.SetValue(StarColumnsProperty, value);
        public static void StarColumnsChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            if (!(obj is Grid grid) || string.IsNullOrEmpty(e.NewValue.ToString()))
                return;
            SetStarColumns(grid);
            RegisterForLayoutUpdate(grid);
        }
        #endregion
        static void SetStarColumns(Grid grid)
        {
            string[] starColumns = GetStarColumns(grid).Split(',');
            for (int i = 0; i < grid.ColumnDefinitions.Count; i++)
            {
                if (starColumns.Contains(i.ToString()))
                    grid.ColumnDefinitions[i].Width = new GridLength(1, GridUnitType.Star);
            }
        }
        static void SetStarRows(Grid grid)
        {
            string[] starRows = GetStarRows(grid).Split(',');
            for (int i = 0; i < grid.RowDefinitions.Count; i++)
            {
                if (starRows.Contains(i.ToString()))
                    grid.RowDefinitions[i].Height = new GridLength(1, GridUnitType.Star);
            }
        }
        static HashSet<Grid> _registered = new HashSet<Grid>();
        static void RegisterForLayoutUpdate(Grid grid)
        {
            if (_registered.Contains(grid))
                return;
            _registered.Add(grid);
            grid.LayoutUpdated += (o, e) =>
            {
                int columnCount = grid.ColumnDefinitions.Count;
                int count = 0;
                foreach (var presenter in grid.Children.OfType<ContentPresenter>())
                {
                    Grid.SetColumn(presenter, count % columnCount);
                    Grid.SetRow(presenter, count / columnCount);
                    count++;
                }
            };
        }
    }
}