namespace Caliburn.Micro {
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
#if WinRT81
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Controls;
    using Windows.UI.Xaml.Controls.Primitives;
    using Windows.UI.Xaml.Data;
    using Windows.UI.Xaml.Markup;
    using EventTrigger = Microsoft.Xaml.Interactions.Core.EventTriggerBehavior;
    using Windows.UI.Xaml.Shapes;
#else
    using System.ComponentModel;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Controls.Primitives;
    using System.Windows.Data;
    using System.Windows.Markup;
    using System.Windows.Shapes;    
    using EventTrigger = System.Windows.Interactivity.EventTrigger;
#endif
#if !SILVERLIGHT && !WinRT
    using System.Windows.Documents;
    using Caliburn.Micro.Core;
#endif
    /// 
    /// Used to configure the conventions used by the framework to apply bindings and create actions.
    /// 
    public static class ConventionManager {
        static readonly ILog Log = LogManager.GetLog(typeof(ConventionManager));
        /// 
        /// Converters  to/from .
        /// 
        public static IValueConverter BooleanToVisibilityConverter = new BooleanToVisibilityConverter();
        /// 
        /// Indicates whether or not static properties should be included during convention name matching.
        /// 
        /// False by default.
        public static bool IncludeStaticProperties = false;
        /// 
        /// Indicates whether or not the Content of ContentControls should be overwritten by conventional bindings.
        /// 
        /// False by default.
        public static bool OverwriteContent = false;
        /// 
        /// The default DataTemplate used for ItemsControls when required.
        /// 
        public static DataTemplate DefaultItemTemplate = (DataTemplate)
#if SILVERLIGHT || WinRT
        XamlReader.Load(
#else
        XamlReader.Parse(
#endif
#if WinRT
            "" +
                "" +
            ""
#else
             " " +
                "" +
            ""
#endif
);
        /// 
        /// The default DataTemplate used for Headered controls when required.
        /// 
        public static DataTemplate DefaultHeaderTemplate = (DataTemplate)
#if SILVERLIGHT || WinRT
        XamlReader.Load(
#else
        XamlReader.Parse(
#endif
            ""
        );
        static readonly Dictionary ElementConventions = new Dictionary();
        /// 
        /// Changes the provided word from a plural form to a singular form.
        /// 
        public static Func Singularize = original => {
            return original.EndsWith("ies")
                ? original.TrimEnd('s').TrimEnd('e').TrimEnd('i') + "y"
                : original.TrimEnd('s');
        };
        /// 
        /// Derives the SelectedItem property name.
        /// 
        public static Func> DerivePotentialSelectionNames = name => {
            var singular = Singularize(name);
            return new[] {
                "Active" + singular,
                "Selected" + singular,
                "Current" + singular
            };
        };
        /// 
        /// Creates a binding and sets it on the element, applying the appropriate conventions.
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        public static Action SetBinding =
            (viewModelType, path, property, element, convention, bindableProperty) => {
#if WinRT
                var binding = new Binding { Path = new PropertyPath(path) };
#else
                var binding = new Binding(path);
#endif
                ApplyBindingMode(binding, property);
                ApplyValueConverter(binding, bindableProperty, property);
                ApplyStringFormat(binding, convention, property);
                ApplyValidation(binding, viewModelType, property);
                ApplyUpdateSourceTrigger(bindableProperty, element, binding, property);
                BindingOperations.SetBinding(element, bindableProperty, binding);
            };
        /// 
        /// Applies the appropriate binding mode to the binding.
        /// 
        public static Action ApplyBindingMode = (binding, property) => {
#if WinRT
            var setMethod = property.SetMethod;
            binding.Mode = (property.CanWrite && setMethod != null && setMethod.IsPublic) ? BindingMode.TwoWay : BindingMode.OneWay;
#else
            var setMethod = property.GetSetMethod();
            binding.Mode = (property.CanWrite && setMethod != null && setMethod.IsPublic) ? BindingMode.TwoWay : BindingMode.OneWay;
#endif
        };
        /// 
        /// Determines whether or not and what type of validation to enable on the binding.
        /// 
        public static Action ApplyValidation = (binding, viewModelType, property) => {
#if SILVERLIGHT || NET45
            if (typeof(INotifyDataErrorInfo).IsAssignableFrom(viewModelType)) {
                binding.ValidatesOnNotifyDataErrors = true;
                binding.ValidatesOnExceptions = true;
            }
#endif
#if !WinRT
            if (typeof(IDataErrorInfo).IsAssignableFrom(viewModelType)) {
                binding.ValidatesOnDataErrors = true;
                binding.ValidatesOnExceptions = true;
            }
#endif
        };
        /// 
        /// Determines whether a value converter is is needed and applies one to the binding.
        /// 
        public static Action ApplyValueConverter = (binding, bindableProperty, property) => {
            if (bindableProperty == UIElement.VisibilityProperty && typeof(bool).IsAssignableFrom(property.PropertyType))
                binding.Converter = BooleanToVisibilityConverter;
        };
        /// 
        /// Determines whether a custom string format is needed and applies it to the binding.
        /// 
        public static Action ApplyStringFormat = (binding, convention, property) => {
#if !WinRT
            if(typeof(DateTime).IsAssignableFrom(property.PropertyType))
                binding.StringFormat = "{0:d}";
#endif
        };
        /// 
        /// Determines whether a custom update source trigger should be applied to the binding.
        /// 
        public static Action ApplyUpdateSourceTrigger = (bindableProperty, element, binding, info) => {
#if SILVERLIGHT && !SL5
            ApplySilverlightTriggers(
                element, 
                bindableProperty, 
                x => x.GetBindingExpression(bindableProperty),
                info,
                binding
                );
#elif WinRT81 || NET
            binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
#endif
        };
        static ConventionManager() {
#if WINDOWS_UWP
            AddElementConvention(SplitView.ContentProperty, "IsPaneOpen", "PaneClosing").GetBindableProperty =
                delegate (DependencyObject foundControl)
                {
                    var element = (SplitView)foundControl;
                    if (!OverwriteContent)
                       return null;
                    Log.Info("ViewModel bound on {0}.", element.Name);
                    return View.ModelProperty;
               };
#endif
#if !WINDOWS_PHONE && !WinRT
            AddElementConvention(DatePicker.SelectedDateProperty, "SelectedDate", "SelectedDateChanged");
#endif
#if WinRT81
            AddElementConvention(DatePicker.DateProperty, "Date", "DateChanged");
            AddElementConvention(TimePicker.TimeProperty, "Time", "TimeChanged");
            AddElementConvention(Hub.HeaderProperty, "Header", "Loaded");
            AddElementConvention(HubSection.HeaderProperty, "Header", "SectionsInViewChanged");
            AddElementConvention(MenuFlyoutItem.TextProperty, "Text", "Click");
            AddElementConvention(ToggleMenuFlyoutItem.IsCheckedProperty, "IsChecked", "Click");
#endif
#if WinRT81 && !WP81
            AddElementConvention(SearchBox.QueryTextProperty, "QueryText", "QuerySubmitted");
#endif
#if WinRT
            AddElementConvention(ToggleSwitch.IsOnProperty, "IsOn", "Toggled");
            AddElementConvention(ProgressRing.IsActiveProperty, "IsActive", "Loaded");
            AddElementConvention(Slider.ValueProperty, "Value", "ValueChanged");
            AddElementConvention(RichEditBox.DataContextProperty, "DataContext", "TextChanged");
#endif
#if WP81 || WINDOWS_UWP
            AddElementConvention(Pivot.ItemsSourceProperty, "SelectedItem", "SelectionChanged")
                .ApplyBinding = (viewModelType, path, property, element, convention) =>
                {
                    if (!SetBindingWithoutBindingOrValueOverwrite(viewModelType, path, property, element, convention, ItemsControl.ItemsSourceProperty))
                    {
                        return false;
                    }
                    ConfigureSelectedItem(element, Pivot.SelectedItemProperty, viewModelType, path);
                    ApplyItemTemplate((ItemsControl)element, property);
                    return true;
                };
#endif
#if SILVERLIGHT || WinRT
            AddElementConvention(HyperlinkButton.ContentProperty, "DataContext", "Click");
            AddElementConvention(PasswordBox.PasswordProperty, "Password", "PasswordChanged");
#else
            AddElementConvention(DocumentViewer.DocumentProperty, "DataContext", "Loaded");
            AddElementConvention(null, "Password", "PasswordChanged");
            AddElementConvention(Hyperlink.DataContextProperty, "DataContext", "Click");
            AddElementConvention(RichTextBox.DataContextProperty, "DataContext", "TextChanged");
            AddElementConvention