using System;
using System.Collections.Generic;

using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.VisualTree;

namespace SourceGit.Views
{
    public class ChangeTreeNodeToggleButton : ToggleButton
    {
        protected override Type StyleKeyOverride => typeof(ToggleButton);

        protected override void OnPointerPressed(PointerPressedEventArgs e)
        {
            if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed &&
                DataContext is ViewModels.ChangeTreeNode { IsFolder: true } node)
            {
                var tree = this.FindAncestorOfType<ChangeCollectionView>();
                tree?.ToggleNodeIsExpanded(node);
            }

            e.Handled = true;
        }
    }

    public class ChangeCollectionContainer : ListBox
    {
        protected override Type StyleKeyOverride => typeof(ListBox);

        protected override void OnKeyDown(KeyEventArgs e)
        {
            if (e.Key != Key.Space)
                base.OnKeyDown(e);
        }
    }

    public partial class ChangeCollectionView : UserControl
    {
        public static readonly StyledProperty<bool> IsUnstagedChangeProperty =
            AvaloniaProperty.Register<ChangeCollectionView, bool>(nameof(IsUnstagedChange));

        public bool IsUnstagedChange
        {
            get => GetValue(IsUnstagedChangeProperty);
            set => SetValue(IsUnstagedChangeProperty, value);
        }

        public static readonly StyledProperty<SelectionMode> SelectionModeProperty =
            AvaloniaProperty.Register<ChangeCollectionView, SelectionMode>(nameof(SelectionMode));

        public SelectionMode SelectionMode
        {
            get => GetValue(SelectionModeProperty);
            set => SetValue(SelectionModeProperty, value);
        }

        public static readonly StyledProperty<Models.ChangeViewMode> ViewModeProperty =
            AvaloniaProperty.Register<ChangeCollectionView, Models.ChangeViewMode>(nameof(ViewMode), Models.ChangeViewMode.Tree);

        public Models.ChangeViewMode ViewMode
        {
            get => GetValue(ViewModeProperty);
            set => SetValue(ViewModeProperty, value);
        }

        public static readonly StyledProperty<List<Models.Change>> ChangesProperty =
            AvaloniaProperty.Register<ChangeCollectionView, List<Models.Change>>(nameof(Changes));

        public List<Models.Change> Changes
        {
            get => GetValue(ChangesProperty);
            set => SetValue(ChangesProperty, value);
        }

        public static readonly StyledProperty<List<Models.Change>> SelectedChangesProperty =
            AvaloniaProperty.Register<ChangeCollectionView, List<Models.Change>>(nameof(SelectedChanges));

        public List<Models.Change> SelectedChanges
        {
            get => GetValue(SelectedChangesProperty);
            set => SetValue(SelectedChangesProperty, value);
        }

        public static readonly RoutedEvent<RoutedEventArgs> ChangeDoubleTappedEvent =
            RoutedEvent.Register<ChangeCollectionView, RoutedEventArgs>(nameof(ChangeDoubleTapped), RoutingStrategies.Tunnel | RoutingStrategies.Bubble);

        public event EventHandler<RoutedEventArgs> ChangeDoubleTapped
        {
            add { AddHandler(ChangeDoubleTappedEvent, value); }
            remove { RemoveHandler(ChangeDoubleTappedEvent, value); }
        }

        public ChangeCollectionView()
        {
            InitializeComponent();
        }

        public void ToggleNodeIsExpanded(ViewModels.ChangeTreeNode node)
        {
            if (Content is ViewModels.ChangeCollectionAsTree tree)
            {
                node.IsExpanded = !node.IsExpanded;

                var depth = node.Depth;
                var idx = tree.Rows.IndexOf(node);
                if (idx == -1)
                    return;

                if (node.IsExpanded)
                {
                    var subrows = new List<ViewModels.ChangeTreeNode>();
                    MakeTreeRows(subrows, node.Children);
                    tree.Rows.InsertRange(idx + 1, subrows);
                }
                else
                {
                    var removeCount = 0;
                    for (int i = idx + 1; i < tree.Rows.Count; i++)
                    {
                        var row = tree.Rows[i];
                        if (row.Depth <= depth)
                            break;

                        removeCount++;
                    }
                    tree.Rows.RemoveRange(idx + 1, removeCount);
                }
            }
        }

        protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
        {
            base.OnPropertyChanged(change);

            if (change.Property == ViewModeProperty)
                UpdateDataSource(false);
            else if (change.Property == ChangesProperty)
                UpdateDataSource(true);
            else if (change.Property == SelectedChangesProperty)
                UpdateSelection();
        }

        private void OnRowDoubleTapped(object sender, TappedEventArgs e)
        {
            var grid = sender as Grid;
            if (grid?.DataContext is ViewModels.ChangeTreeNode node)
            {
                if (node.IsFolder)
                {
                    var posX = e.GetPosition(this).X;
                    if (posX < node.Depth * 16 + 16)
                        return;

                    ToggleNodeIsExpanded(node);
                }
                else
                {
                    RaiseEvent(new RoutedEventArgs(ChangeDoubleTappedEvent));
                }
            }
            else if (grid?.DataContext is Models.Change)
            {
                RaiseEvent(new RoutedEventArgs(ChangeDoubleTappedEvent));
            }
        }

        private void OnRowSelectionChanged(object sender, SelectionChangedEventArgs _)
        {
            if (_disableSelectionChangingEvent)
                return;

            _disableSelectionChangingEvent = true;
            var selected = new List<Models.Change>();
            if (sender is ListBox { SelectedItems: not null } list)
            {
                foreach (var item in list.SelectedItems)
                {
                    if (item is Models.Change c)
                        selected.Add(c);
                    else if (item is ViewModels.ChangeTreeNode node)
                        CollectChangesInNode(selected, node);
                }
            }

            TrySetSelected(selected);
            _disableSelectionChangingEvent = false;
        }

        private void MakeTreeRows(List<ViewModels.ChangeTreeNode> rows, List<ViewModels.ChangeTreeNode> nodes)
        {
            foreach (var node in nodes)
            {
                rows.Add(node);

                if (!node.IsExpanded || !node.IsFolder)
                    continue;

                MakeTreeRows(rows, node.Children);
            }
        }

        private void UpdateDataSource(bool disableEvents)
        {
            _disableSelectionChangingEvent = disableEvents;

            var changes = Changes;
            if (changes == null || changes.Count == 0)
            {
                Content = null;
                _disableSelectionChangingEvent = false;
                return;
            }

            var selected = SelectedChanges ?? [];
            if (ViewMode == Models.ChangeViewMode.Tree)
            {
                HashSet<string> oldFolded = new HashSet<string>();
                if (Content is ViewModels.ChangeCollectionAsTree oldTree)
                {
                    foreach (var row in oldTree.Rows)
                    {
                        if (row.IsFolder && !row.IsExpanded)
                            oldFolded.Add(row.FullPath);
                    }
                }

                var tree = new ViewModels.ChangeCollectionAsTree();
                tree.Tree = ViewModels.ChangeTreeNode.Build(changes, oldFolded);

                var rows = new List<ViewModels.ChangeTreeNode>();
                MakeTreeRows(rows, tree.Tree);
                tree.Rows.AddRange(rows);

                if (selected.Count > 0)
                {
                    var sets = new HashSet<Models.Change>();
                    foreach (var c in selected)
                        sets.Add(c);

                    var nodes = new List<ViewModels.ChangeTreeNode>();
                    foreach (var row in tree.Rows)
                    {
                        if (row.Change != null && sets.Contains(row.Change))
                            nodes.Add(row);
                    }

                    tree.SelectedRows.AddRange(nodes);
                }

                Content = tree;
            }
            else if (ViewMode == Models.ChangeViewMode.Grid)
            {
                var grid = new ViewModels.ChangeCollectionAsGrid();
                grid.Changes.AddRange(changes);
                if (selected.Count > 0)
                    grid.SelectedChanges.AddRange(selected);
                Content = grid;
            }
            else
            {
                var list = new ViewModels.ChangeCollectionAsList();
                list.Changes.AddRange(changes);
                if (selected.Count > 0)
                    list.SelectedChanges.AddRange(selected);
                Content = list;
            }

            _disableSelectionChangingEvent = false;
        }

        private void UpdateSelection()
        {
            if (_disableSelectionChangingEvent)
                return;

            _disableSelectionChangingEvent = true;

            var selected = SelectedChanges ?? [];
            if (Content is ViewModels.ChangeCollectionAsTree tree)
            {
                tree.SelectedRows.Clear();

                if (selected.Count > 0)
                {
                    var sets = new HashSet<Models.Change>();
                    foreach (var c in selected)
                        sets.Add(c);

                    var nodes = new List<ViewModels.ChangeTreeNode>();
                    foreach (var row in tree.Rows)
                    {
                        if (row.Change != null && sets.Contains(row.Change))
                            nodes.Add(row);
                    }

                    tree.SelectedRows.AddRange(nodes);
                }
            }
            else if (Content is ViewModels.ChangeCollectionAsGrid grid)
            {
                grid.SelectedChanges.Clear();
                if (selected.Count > 0)
                    grid.SelectedChanges.AddRange(selected);
            }
            else if (Content is ViewModels.ChangeCollectionAsList list)
            {
                list.SelectedChanges.Clear();
                if (selected.Count > 0)
                    list.SelectedChanges.AddRange(selected);
            }

            _disableSelectionChangingEvent = false;
        }

        private void CollectChangesInNode(List<Models.Change> outs, ViewModels.ChangeTreeNode node)
        {
            if (node.IsFolder)
            {
                foreach (var child in node.Children)
                    CollectChangesInNode(outs, child);
            }
            else if (!outs.Contains(node.Change))
            {
                outs.Add(node.Change);
            }
        }

        private void TrySetSelected(List<Models.Change> changes)
        {
            var old = SelectedChanges;
            if (old == null && changes.Count == 0)
                return;

            if (old != null && old.Count == changes.Count)
            {
                bool allEquals = true;
                foreach (var c in old)
                {
                    if (!changes.Contains(c))
                    {
                        allEquals = false;
                        break;
                    }
                }

                if (allEquals)
                    return;
            }

            _disableSelectionChangingEvent = true;
            SetCurrentValue(SelectedChangesProperty, changes);
            _disableSelectionChangingEvent = false;
        }

        private bool _disableSelectionChangingEvent = false;
    }
}
