﻿// 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.

#nullable disable

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Options;
using Roslyn.Utilities;

namespace Microsoft.VisualStudio.LanguageServices.Implementation.Options
{
    internal partial class InternalOptionsControl : AbstractOptionPageControl
    {
        private readonly IEnumerable<IOption2> _options;

        public InternalOptionsControl(IEnumerable<IOption2> options, OptionStore optionStore)
            : base(optionStore)
        {
            _options = options;

            // options
            var optionsPanel = new StackPanel();
            this.AddOptions(optionsPanel);

            // scroll
            var viewer = new ScrollViewer()
            {
                VerticalScrollBarVisibility = ScrollBarVisibility.Auto,
                HorizontalScrollBarVisibility = ScrollBarVisibility.Auto
            };

            viewer.Content = optionsPanel;

            // search 
            var searchBox = new TextBox() { MinWidth = 200, HorizontalAlignment = HorizontalAlignment.Stretch };

            var searchButton = new Button() { Content = "Search" };
            searchButton.Click += (o, a) =>
            {
                foreach (var item in optionsPanel.Children.OfType<CheckBox>())
                {
                    var title = item.Content as string;
                    if (title == null)
                    {
                        continue;
                    }

                    // pattern not match
                    if (title.IndexOf(searchBox.Text, StringComparison.OrdinalIgnoreCase) < 0)
                    {
                        // hide it
                        item.Visibility = Visibility.Collapsed;
                    }
                    else
                    {
                        item.Visibility = Visibility.Visible;
                    }
                }
            };

            var clearButton = new Button() { Content = "Clear" };
            clearButton.Click += (o, a) => optionsPanel.Children.OfType<CheckBox>().Do(c => c.Visibility = Visibility.Visible);

            var searchPanel = new StackPanel()
            {
                Orientation = Orientation.Horizontal,
                Margin = new Thickness(5),
                HorizontalAlignment = HorizontalAlignment.Stretch
            };

            searchPanel.Children.Add(searchBox);
            searchPanel.Children.Add(searchButton);
            searchPanel.Children.Add(clearButton);

            // button
            var checkAllButton = new Button() { Content = "Check All" };
            checkAllButton.Click += (o, a) => optionsPanel.Children.OfType<CheckBox>().Where(c => c.Visibility == Visibility.Visible).Do(c => c.IsChecked = true);

            var uncheckAllButton = new Button() { Content = "Uncheck All" };
            uncheckAllButton.Click += (o, a) => optionsPanel.Children.OfType<CheckBox>().Where(c => c.Visibility == Visibility.Visible).Do(c => c.IsChecked = false);

            var selectionPanel = new StackPanel()
            {
                Orientation = Orientation.Horizontal,
                Margin = new Thickness(5),
                HorizontalAlignment = HorizontalAlignment.Stretch
            };

            selectionPanel.Children.Add(checkAllButton);
            selectionPanel.Children.Add(uncheckAllButton);

            // main panel
            var mainPanel = new DockPanel() { HorizontalAlignment = HorizontalAlignment.Stretch };
            mainPanel.Children.Add(searchPanel);
            mainPanel.Children.Add(selectionPanel);
            mainPanel.Children.Add(viewer);

            DockPanel.SetDock(searchPanel, Dock.Top);
            DockPanel.SetDock(selectionPanel, Dock.Bottom);

            this.Content = mainPanel;
        }

        protected virtual void AddOptions(Panel panel)
        {
            foreach (var option in _options)
            {
                if (!option.IsPerLanguage)
                {
                    AddOption(panel, option);
                }
                else
                {
                    AddPerLanguageOption(panel, option, LanguageNames.CSharp);
                    AddPerLanguageOption(panel, option, LanguageNames.VisualBasic);
                }
            }
        }

        protected void AddOption(Panel panel, IOption2 option, string additional = null)
        {
            var uiElement = CreateControl(option, additional: additional);
            if (uiElement != null)
            {
                panel.Children.Add(uiElement);
            }
        }

        protected void AddPerLanguageOption(Panel panel, IOption2 option, string languageName, string additional = null)
        {
            var uiElement = CreateControl(option, languageName, additional);
            if (uiElement != null)
            {
                panel.Children.Add(uiElement);
            }
        }

        private UIElement CreateControl(IOption2 option, string languageName = null, string additional = null)
        {
            // Underscores in WPF mean that the next character is the access key for keyboard navigation
            // but thats not why our option names have underscores. Also removing them looks nicer.
            var optionDisplay = option.Definition.ConfigName.Replace('_', ' ') + GetLanguage(languageName) + GetAdditionalText(additional);

            if (option.Type == typeof(bool))
            {
                var checkBox = new CheckBox() { Content = optionDisplay };
                BindToCheckBox(checkBox, option, languageName);
                return checkBox;
            }

            if (option.Type == typeof(int))
            {
                var label = new Label() { Content = optionDisplay };
                var textBox = new TextBox();
                BindToTextBox(textBox, option, languageName);

                var panel = new StackPanel();
                panel.Children.Add(label);
                panel.Children.Add(textBox);

                AddSearchHandler(label);

                return panel;
            }

            return null;
        }

        private static string GetAdditionalText(string additional)
        {
            if (additional == null)
            {
                return string.Empty;
            }

            return " [" + additional + "]";
        }

        private static string GetLanguage(string languageName)
        {
            if (languageName == null)
            {
                return string.Empty;
            }

            return " [" + languageName + "]";
        }

        private void BindToCheckBox(CheckBox checkBox, IOption2 option, string languageName = null)
        {
            if (languageName == null)
            {
                BindToOption(checkBox, (Option2<bool>)option);
                return;
            }

            BindToOption(checkBox, (PerLanguageOption2<bool>)option, languageName);
        }

        private void BindToTextBox(TextBox textBox, IOption2 option, string languageName = null)
        {
            if (languageName == null)
            {
                BindToOption(textBox, (Option2<int>)option);
                return;
            }

            BindToOption(textBox, (PerLanguageOption2<int>)option, languageName);
        }
    }
}
