﻿// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

extern alias slowautomation;

using System.Collections.Generic;
using System.Linq;
using System.Windows;
using slowautomation::System.Windows.Automation;
using System.Windows.Automation.Peers;
using System.Windows.Controls;
using System.Windows.Input;
using Roslyn.Utilities;

namespace Microsoft.VisualStudio.LanguageServices.Implementation.Utilities
{
    internal class AutomationDelegatingListView : ListView
    {
        protected override bool IsItemItsOwnContainerOverride(object item)
            => item is AutomationDelegatingListViewItem;

        protected override DependencyObject GetContainerForItemOverride()
            => new AutomationDelegatingListViewItem();

        protected override AutomationPeer OnCreateAutomationPeer()
            => new AutomationDelegatingListViewAutomationPeer(this);

        protected override void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs e)
        {
            base.OnGotKeyboardFocus(e);
            if (this.SelectedIndex == -1)
            {
                this.SelectedIndex = 0;
            }
        }
    }

    internal class AutomationDelegatingListViewAutomationPeer : FrameworkElementAutomationPeer
    {
        public AutomationDelegatingListViewAutomationPeer(AutomationDelegatingListView listView)
            : base(listView)
        {
        }

        protected override List<AutomationPeer>? GetChildrenCore()
        {
            List<AutomationPeer>? results = null;
            var peersToProcess = new Queue<AutomationPeer>(base.GetChildrenCore() ?? SpecializedCollections.EmptyEnumerable<AutomationPeer>());
            while (peersToProcess.Count > 0)
            {
                var peer = peersToProcess.Dequeue();
                if (peer is ListBoxItemWrapperAutomationPeer itemWrapperAutomationPeer)
                {
                    results ??= new List<AutomationPeer>();
                    results.Add(itemWrapperAutomationPeer);
                }
                else
                {
                    foreach (var childPeer in peer.GetChildren() ?? SpecializedCollections.EmptyEnumerable<AutomationPeer>())
                    {
                        peersToProcess.Enqueue(childPeer);
                    }
                }
            }

            return results;
        }

        protected override AutomationControlType GetAutomationControlTypeCore()
            => AutomationControlType.List;
    }

    internal class AutomationDelegatingListViewItem : ListViewItem
    {
        protected override AutomationPeer OnCreateAutomationPeer()
            => new AutomationDelegatingListViewItemAutomationPeer(this);
    }

    internal class AutomationDelegatingListViewItemAutomationPeer : ListBoxItemWrapperAutomationPeer
    {
        private readonly CheckBoxAutomationPeer? checkBoxItem;
        private readonly RadioButtonAutomationPeer? radioButtonItem;
        private readonly TextBlockAutomationPeer? textBlockItem;

        public AutomationDelegatingListViewItemAutomationPeer(AutomationDelegatingListViewItem listViewItem)
            : base(listViewItem)
        {
            checkBoxItem = this.GetChildren()?.OfType<CheckBoxAutomationPeer>().SingleOrDefault();
            if (checkBoxItem != null)
            {
                var toggleButton = ((CheckBox)checkBoxItem.Owner);
                toggleButton.Checked += Checkbox_CheckChanged;
                toggleButton.Unchecked += Checkbox_CheckChanged;
                return;
            }

            radioButtonItem = this.GetChildren()?.OfType<RadioButtonAutomationPeer>().SingleOrDefault();
            if (radioButtonItem != null)
            {
                var toggleButton = ((RadioButton)radioButtonItem.Owner);
                toggleButton.Checked += RadioButton_CheckChanged;
                toggleButton.Unchecked += RadioButton_CheckChanged;
                return;
            }

            textBlockItem = this.GetChildren()?.OfType<TextBlockAutomationPeer>().FirstOrDefault();
        }

        private void Checkbox_CheckChanged(object sender, RoutedEventArgs e)
        {
            var checkBox = (CheckBox)sender;
            RaisePropertyChangedEvent(
                TogglePatternIdentifiers.ToggleStateProperty,
                oldValue: ConvertToToggleState(!checkBox.IsChecked),
                newValue: ConvertToToggleState(checkBox.IsChecked));
        }

        private void RadioButton_CheckChanged(object sender, RoutedEventArgs e)
        {
            // RadioButtonAutomationPeer sets oldValue and newValue to true, so we do the same here
            // See http://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Automation/Peers/RadioButtonAutomationPeer.cs,114
            RaisePropertyChangedEvent(
                SelectionItemPatternIdentifiers.IsSelectedProperty,
                oldValue: true,
                newValue: true);
        }

        private static ToggleState ConvertToToggleState(bool? value)
        {
            switch (value)
            {
                case true: return ToggleState.On;
                case false: return ToggleState.Off;
                default: return ToggleState.Indeterminate;
            }
        }

        protected override AutomationControlType GetAutomationControlTypeCore()
        {
            if (checkBoxItem != null)
            {
                return AutomationControlType.CheckBox;
            }
            else if (radioButtonItem != null)
            {
                return AutomationControlType.RadioButton;
            }
            else
            {
                return AutomationControlType.Text;
            }
        }

        public override object? GetPattern(PatternInterface patternInterface)
        {
            var automationPeer = GetAutomationPeer();
            return automationPeer != null
                ? automationPeer.GetPattern(patternInterface)
                : base.GetPattern(patternInterface);
        }

        protected override string GetNameCore()
            => GetAutomationPeer()?.GetName() ?? string.Empty;

        private AutomationPeer? GetAutomationPeer()
            => checkBoxItem ?? radioButtonItem ?? (AutomationPeer?)textBlockItem;
    }
}
