using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using InABox.Core;

namespace InABox.DynamicGrid
{
    public delegate void EditorControlValueChangedHandler(IDynamicEditorControl sender, Dictionary<string, object> values);

    public interface IDynamicEditorControl
    {
        string ColumnName { get; set; }

        bool Loaded { get; set; }

        BaseEditor EditorDefinition { get; set; }

        bool IsEnabled { get; set; }
        void SetFocus();
        int DesiredHeight();

        event EditorControlValueChangedHandler OnEditorValueChanged;

        void Configure();

        void SetEnabled(bool enabled);

        void SetVisible(bool enabled);

        void SetValue(object value);

        object? GetValue();
    }

    public interface ILookupEditorControl : IDynamicEditorControl
    {
        Dictionary<string, string> OtherColumns { get; }
        event OnDefineLookup OnDefineLookups;
        event OnLookupsDefined OnLookupsDefined;
        void LoadLookups(CoreTable values);
    }

    public interface IPopupEditorControl
    {
        Dictionary<string, string> OtherColumns { get; }
        event OnDefineFilter OnDefineFilter;
    }

    public abstract class BaseDynamicEditorControl : ContentControl, IDynamicEditorControl
    {
        public static readonly DependencyProperty ColumnNameProperty =
            DependencyProperty.Register(nameof(ColumnName), typeof(string), typeof(BaseDynamicEditorControl));

        public static readonly DependencyProperty ColorProperty =
            DependencyProperty.Register("Color", typeof(Color), typeof(BaseDynamicEditorControl));

        public new static readonly DependencyProperty IsEnabledProperty =
            DependencyProperty.Register(nameof(IsEnabled), typeof(bool), typeof(BaseDynamicEditorControl));

        private BaseEditor _editordefinition;

        public BaseDynamicEditorControl()
        {
            Color = Colors.LightYellow;
        }

        public Color Color
        {
            get => (Color)GetValue(ColorProperty);
            set
            {
                SetValue(ColorProperty, value);
                if (EditorDefinition != null)
                    SetColor(Color);
            }
        }

        public string ColumnName
        {
            get => (string)GetValue(ColumnNameProperty);
            set => SetValue(ColumnNameProperty, value);
        }

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public BaseEditor EditorDefinition
        {
            get => _editordefinition;
            set
            {
                _editordefinition = value;
                Content = CreateEditor();
                SetColor(Color);
            }
        }

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public bool Loaded { get; set; }

        public virtual event EditorControlValueChangedHandler OnEditorValueChanged;
        public abstract int DesiredHeight();

        public abstract void SetFocus();

        public virtual void Configure()
        {
        }

        public abstract void SetEnabled(bool enabled);

        public abstract void SetVisible(bool visible);

        public abstract void SetValue(object? value);

        public abstract object? GetValue();

        public new bool IsEnabled
        {
            get => (bool)GetValue(IsEnabledProperty);
            set
            {
                SetValue(IsEnabledProperty, value);
                SetEnabled(value);
            }
        }

        protected abstract FrameworkElement CreateEditor();

        public abstract int DesiredWidth();

        public abstract void SetColor(Color color);

        protected List<Button> CreateButtons(out bool bDisableEditor)
        {
            var buttons = new List<Button>();
            bDisableEditor = false;
            if (EditorDefinition != null && EditorDefinition is IButtonEditor)
            {
                var ce = (IButtonEditor)EditorDefinition;
                if (ce.Buttons != null)
                    foreach (var ceb in ce.Buttons.Reverse())
                    {
                        if (ceb.DisableEditor)
                            bDisableEditor = true;

                        var button = new Button
                        {
                            HorizontalAlignment = HorizontalAlignment.Left,
                            VerticalAlignment = VerticalAlignment.Stretch,
                            VerticalContentAlignment = VerticalAlignment.Center,
                            HorizontalContentAlignment = HorizontalAlignment.Center,
                            Content = ceb.Caption,
                            Width = ceb.Width,
                            Margin = new Thickness(5, 0, 0, 0),
                            Padding = new Thickness(5, 0, 5, 0),
                            Tag = ceb,
                            IsEnabled = ceb.IsEnabled
                        };
                        button.Click += (o, e) =>
                        {
                            var b = (Button)o;
                            var eb = b.Tag as EditorButton;
                            eb.Click(this);
                        };
                        ceb.OnEnabled += (enabled) =>
                        {
                            button.IsEnabled = enabled;
                        };
                        buttons.Add(button);
                    }
            }
            return buttons;
        }
    }

    public abstract class DynamicEditorControl<T> : BaseDynamicEditorControl
    {
        //{
        //    get { return (EditorControlValueChangedHandler)GetValue(OnEditorValueChangedProperty); }
        //    set { SetValue(OnEditorValueChangedProperty, value) }
        //}

        protected bool _changed;

        private bool Updating;

        public DynamicEditorControl()
        {
            Loaded = false;
            OtherValues = new Dictionary<string, object>();
            MinHeight = 25;
            Focusable = false;
        }

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public bool Changed
        {
            get => _changed;
            set => _changed = value;
        }

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public T Value
        {
            get => RetrieveValue();
            set => UpdateValue(value);
        }

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        protected virtual Dictionary<string, object?> OtherValues { get; }

        //public static readonly DependencyProperty OnEditorValueChangedProperty =
        //    DependencyProperty.Register(nameof(OnEditorValueChanged), typeof(EditorControlValueChangedHandler), typeof(DynamicEditorControl<T>));

        public override event EditorControlValueChangedHandler OnEditorValueChanged;

        protected bool CheckChanged()
        {
            //Logger.Send(LogType.Information, "", string.Format("{0}({1}).CheckChanged()", GetType().EntityName().Split('.').Last(), ColumnName));
            if (Loaded && !Updating)
            {
                Updating = true;
                try
                {
                    var values = new Dictionary<string, object>();
                    var sColumn = string.IsNullOrEmpty(ColumnName) ? "" : ColumnName;
                    values[sColumn] = RetrieveValue();
                    foreach (var key in OtherValues.Keys)
                        values[key] = OtherValues[key];
                    OnEditorValueChanged?.Invoke(this, values);
                }
                finally
                {
                    Changed = true;
                    Updating = false;
                }
            }

            return Changed;
        }

        public override void SetValue(object? value)
        {
            UpdateValue(value != null ? (T)value : default);
        }

        public override object? GetValue()
        {
            return RetrieveValue();
        }

        protected abstract T RetrieveValue();
        protected abstract void UpdateValue(T value);

        public override bool ShouldSerializeContent()
        {
            return false;
        }

        public override void SetEnabled(bool enabled)
        {
            var element = Content as FrameworkElement;
            element.IsEnabled = enabled;
            SetColor(enabled ? Color : Colors.WhiteSmoke);
        }

        public override void SetVisible(bool visible)
        {
            Visibility = visible ? Visibility.Visible : Visibility.Collapsed;
        }
    }
}