//------------------------------------------------------------------------------
// <copyright file=”*.cs” company=”EhLib Team”>
//     Copyright (c) 2017 Dmitry V. Bolshakov   
// </copyright>
//------------------------------------------------------------------------------

using System;
using System.Collections;
using System.Windows.Forms.Design;
using System.ComponentModel.Design;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.Drawing.Design;

namespace EhLib.Design
{

  public class ComboBoxDesigner : BaseEditBoxDesigner
  {
    private DesignerActionListCollection _actionLists;

    public override void InitializeNewComponent(IDictionary defaultValues)
    {
      base.InitializeNewComponent(defaultValues);

      IDesignerHost host = (IDesignerHost)base.Component.Site.GetService(typeof(IDesignerHost));

      EditButton b = (EditButton)host.CreateComponent(typeof(EditButton));

      EditBox.InEditControl = b;
    }

    public override DesignerActionListCollection ActionLists
    {
      get
      {
        if (this._actionLists == null)
        {
          this._actionLists = new DesignerActionListCollection();
          this._actionLists.Add(new ListControlBoundActionList(this));
        }
        return this._actionLists;
      }
    }

  }

  [ComplexBindingProperties("DataSource", "DataMember")]
  public class ListControlBoundActionList : DesignerActionList
  {
    private bool _boundMode;
    private object _boundSelectedValue;
    private readonly ControlDesigner _owner;
    private readonly DesignerActionUIService uiService;

    public ListControlBoundActionList(ControlDesigner owner) : base(owner.Component)
    {
      this._owner = owner;
      ComboBoxEh component = (ComboBoxEh)Component;
      if (component.DataSource != null)
      {
        this._boundMode = true;
      }
      this.uiService = GetService(typeof(DesignerActionUIService)) as DesignerActionUIService;
    }

    private System.Windows.Forms.Binding GetSelectedValueBinding()
    {
      ComboBoxEh component = (ComboBoxEh)Component;
      System.Windows.Forms.Binding binding = null;
      foreach (System.Windows.Forms.Binding binding2 in component.DataBindings)
      {
        if (binding2.PropertyName == "SelectedValue")
        {
          binding = binding2;
        }
      }
      return binding;
    }

    public override DesignerActionItemCollection GetSortedActionItems()
    {
      DesignerActionItemCollection items = new DesignerActionItemCollection();
      //items.Add(new DesignerActionPropertyItem("BoundMode", System.Design.SR.GetString("BoundModeDisplayName"), System.Design.SR.GetString("DataCategoryName"), System.Design.SR.GetString("BoundModeDescription")));
      items.Add(new DesignerActionPropertyItem("BoundMode", "Use Data Bound Items", "DataCategoryName", "BoundModeDescription"));
      ComboBoxEh component = Component as ComboBoxEh;
      if (this._boundMode || ((component != null) && (component.DataSource != null)))
      {
        this._boundMode = true;
        //items.Add(new DesignerActionHeaderItem(System.Design.SR.GetString("BoundModeHeader"), System.Design.SR.GetString("DataCategoryName")));
        //items.Add(new DesignerActionPropertyItem("DataSource", System.Design.SR.GetString("DataSourceDisplayName"), System.Design.SR.GetString("DataCategoryName"), System.Design.SR.GetString("DataSourceDescription")));
        //items.Add(new DesignerActionPropertyItem("DisplayMember", System.Design.SR.GetString("DisplayMemberDisplayName"), System.Design.SR.GetString("DataCategoryName"), System.Design.SR.GetString("DisplayMemberDescription")));
        //items.Add(new DesignerActionPropertyItem("ValueMember", System.Design.SR.GetString("ValueMemberDisplayName"), System.Design.SR.GetString("DataCategoryName"), System.Design.SR.GetString("ValueMemberDescription")));
        //items.Add(new DesignerActionPropertyItem("BoundSelectedValue", System.Design.SR.GetString("BoundSelectedValueDisplayName"), System.Design.SR.GetString("DataCategoryName"), System.Design.SR.GetString("BoundSelectedValueDescription")));

        items.Add(new DesignerActionHeaderItem("Data Binding Mode", "DataCategoryName"));
        items.Add(new DesignerActionPropertyItem("DataSource", "Data Source ", "DataCategoryName", "DataSourceDescription"));
        items.Add(new DesignerActionPropertyItem("DisplayMember", "Display Member", "DataCategoryName", "DisplayMemberDescription"));
        items.Add(new DesignerActionPropertyItem("ValueMember", "Value Member", "DataCategoryName", "ValueMemberDescription"));
        items.Add(new DesignerActionPropertyItem("BoundSelectedValue", "Selected Value", "DataCategoryName", "BoundSelectedValueDescription"));
      }
      else
      {
        //items.Add(new DesignerActionHeaderItem(System.Design.SR.GetString("UnBoundModeHeader"), System.Design.SR.GetString("DataCategoryName")));
        //items.Add(new DesignerActionMethodItem(this, "InvokeItemsDialog", System.Design.SR.GetString("EditItemDisplayName"), System.Design.SR.GetString("DataCategoryName"), System.Design.SR.GetString("EditItemDescription"), true));

        items.Add(new DesignerActionHeaderItem("UnBoundModeHeader", "Unbound Mode"));
        items.Add(new DesignerActionMethodItem(this, "InvokeItemsDialog", "Edit items...", "DataCategoryName", "EditItemDescription", true));
      }
      return items;
    }

    public void InvokeItemsDialog()
    {
      EditorServiceContext.EditValue(this._owner, base.Component, "Items");
    }

    private void RefreshPanelContent()
    {
      if (this.uiService != null)
      {
        this.uiService.Refresh(this._owner.Component);
      }
    }

    private void SetSelectedValueBinding(object dataSource, string dataMember)
    {
      ComboBoxEh component = (ComboBoxEh)base.Component;
      IDesignerHost host = base.GetService(typeof(IDesignerHost)) as IDesignerHost;
      IComponentChangeService service = base.GetService(typeof(IComponentChangeService)) as IComponentChangeService;
      PropertyDescriptor member = TypeDescriptor.GetProperties(component)["DataBindings"];
      if ((host != null) && (service != null))
      {
        using (DesignerTransaction transaction = host.CreateTransaction("TextBox DataSource RESX"))
        {
          service.OnComponentChanging(this._owner.Component, member);
          System.Windows.Forms.Binding selectedValueBinding = this.GetSelectedValueBinding();
          if (selectedValueBinding != null)
          {
            component.DataBindings.Remove(selectedValueBinding);
          }
          if (dataSource != null && !string.IsNullOrEmpty(dataMember))
          {
            component.DataBindings.Add("SelectedValue", dataSource, dataMember);
          }
          service.OnComponentChanged(this._owner.Component, member, null, null);
          transaction.Commit();
        }
      }
    }

    public bool BoundMode
    {
      get
      {
        return this._boundMode;
      }
      set
      {
        if (!value)
        {
          this.DataSource = null;
        }
        if (this.DataSource == null)
        {
          this._boundMode = value;
        }
        this.RefreshPanelContent();
      }
    }

    [TypeConverter("System.Windows.Forms.Design.DesignBindingConverter")]
    [Editor("System.Windows.Forms.Design.DesignBindingEditor", typeof(UITypeEditor))]
    public object BoundSelectedValue
    {
      get
      {
        string bindingMember;
        object dataSource;
        System.Windows.Forms.Binding selectedValueBinding = this.GetSelectedValueBinding();
        if (selectedValueBinding == null)
        {
          bindingMember = null;
          dataSource = null;
        }
        else
        {
          bindingMember = selectedValueBinding.BindingMemberInfo.BindingMember;
          dataSource = selectedValueBinding.DataSource;
        }
        string typeName = string.Format(CultureInfo.InvariantCulture, "System.Windows.Forms.Design.DesignBinding, {0}", typeof(ControlDesigner).Assembly.FullName);

        Type objectType = System.Type.GetType(typeName);
        Debug.Assert(objectType != null, @"objectType != null");
        this._boundSelectedValue = TypeDescriptor.CreateInstance(null, objectType, new System.Type[] { typeof(object), typeof(string) }, new object[] { dataSource, bindingMember });

        return this._boundSelectedValue;
      }
      set
      {
        if (value is string)
        {
          PropertyDescriptor descriptor = TypeDescriptor.GetProperties(this)["BoundSelectedValue"];
          Debug.Assert(descriptor.Converter != null, @"descriptor.Converter != null");
          this._boundSelectedValue = descriptor.Converter.ConvertFrom(new EditorServiceContext(this._owner), CultureInfo.InvariantCulture, value);
        }
        else
        {
          this._boundSelectedValue = value;
          if (value != null)
          {
            object dataSource = TypeDescriptor.GetProperties(this._boundSelectedValue)["DataSource"].GetValue(this._boundSelectedValue);
            string dataMember = (string)TypeDescriptor.GetProperties(this._boundSelectedValue)["DataMember"].GetValue(this._boundSelectedValue);
            this.SetSelectedValueBinding(dataSource, dataMember);
          }
        }
      }
    }

    [AttributeProvider(typeof(IListSource))]
    public object DataSource
    {
      get
      {
        return ((ComboBoxEh)base.Component).DataSource;
      }
      set
      {
        ComboBoxEh component = (ComboBoxEh)base.Component;
        IDesignerHost host = base.GetService(typeof(IDesignerHost)) as IDesignerHost;
        IComponentChangeService service = base.GetService(typeof(IComponentChangeService)) as IComponentChangeService;
        PropertyDescriptor member = TypeDescriptor.GetProperties(component)["DataSource"];
        if ((host != null) && (service != null))
        {
          using (DesignerTransaction transaction = host.CreateTransaction("DGV DataSource TX Name"))
          {
            service.OnComponentChanging(base.Component, member);
            component.DataSource = value;
            if (value == null)
            {
              component.DisplayMember = "";
              component.ValueMember = "";
            }
            service.OnComponentChanged(base.Component, member, null, null);
            transaction.Commit();
            this.RefreshPanelContent();
          }
        }
      }
    }

    [Editor("System.Windows.Forms.Design.DataMemberFieldEditor", typeof(UITypeEditor))]
    public string DisplayMember
    {
      get
      {
        return ((ComboBoxEh)base.Component).DisplayMember;
      }
      set
      {
        ComboBoxEh component = (ComboBoxEh)base.Component;
        IDesignerHost host = base.GetService(typeof(IDesignerHost)) as IDesignerHost;
        IComponentChangeService service = base.GetService(typeof(IComponentChangeService)) as IComponentChangeService;
        PropertyDescriptor member = TypeDescriptor.GetProperties(component)["DisplayMember"];
        if ((host != null) && (service != null))
        {
          using (DesignerTransaction transaction = host.CreateTransaction("DGV DataSource TX Name"))
          {
            service.OnComponentChanging(base.Component, member);
            component.DisplayMember = value;
            service.OnComponentChanged(base.Component, member, null, null);
            transaction.Commit();
          }
        }
      }
    }

    [Editor("System.Windows.Forms.Design.DataMemberFieldEditor", typeof(UITypeEditor))]
    public string ValueMember
    {
      get
      {
        return ((ComboBoxEh)base.Component).ValueMember;
      }
      set
      {
        ComboBoxEh component = (ComboBoxEh)this._owner.Component;
        IDesignerHost host = base.GetService(typeof(IDesignerHost)) as IDesignerHost;
        IComponentChangeService service = base.GetService(typeof(IComponentChangeService)) as IComponentChangeService;
        PropertyDescriptor member = TypeDescriptor.GetProperties(component)["ValueMember"];
        if ((host != null) && (service != null))
        {
          using (DesignerTransaction transaction = host.CreateTransaction("DGV DataSource TX Name"))
          {
            service.OnComponentChanging(base.Component, member);
            component.ValueMember = value;
            service.OnComponentChanged(base.Component, member, null, null);
            transaction.Commit();
          }
        }
      }
    }
  }

  internal class EditorServiceContext : IWindowsFormsEditorService, ITypeDescriptorContext
  {
    private IComponentChangeService _componentChangeSvc;
    private readonly ComponentDesigner _designer;
    private readonly PropertyDescriptor _targetProperty;

    internal EditorServiceContext(ComponentDesigner designer)
    {
      this._designer = designer;
    }

    internal EditorServiceContext(ComponentDesigner designer, PropertyDescriptor prop)
    {
      this._designer = designer;
      this._targetProperty = prop;
      if (prop == null)
      {
        prop = TypeDescriptor.GetDefaultProperty(designer.Component);
        if ((prop != null) && typeof(ICollection).IsAssignableFrom(prop.PropertyType))
        {
          this._targetProperty = prop;
        }
      }
    }

    internal EditorServiceContext(ComponentDesigner designer, PropertyDescriptor prop, string newVerbText) : this(designer, prop)
    {
      this._designer.Verbs.Add(new DesignerVerb(newVerbText, this.OnEditItems));
    }

    public static object EditValue(ComponentDesigner designer, object objectToChange, string propName)
    {
      PropertyDescriptor prop = TypeDescriptor.GetProperties(objectToChange)[propName];
      EditorServiceContext context = new EditorServiceContext(designer, prop);
      UITypeEditor editor = prop.GetEditor(typeof(UITypeEditor)) as UITypeEditor;
      object obj2 = prop.GetValue(objectToChange);
      Debug.Assert(editor != null, "editor != null");
      object obj3 = editor.EditValue(context, context, obj2);
      if (obj3 != obj2)
      {
        try
        {
          prop.SetValue(objectToChange, obj3);
        }
        catch (CheckoutException)
        {
        }
      }
      return obj3;
    }

    private void OnEditItems(object sender, EventArgs e)
    {
      object component = this._targetProperty.GetValue(this._designer.Component);
      if (component != null)
      {
        CollectionEditor editor = TypeDescriptor.GetEditor(component, typeof(UITypeEditor)) as CollectionEditor;
        if (editor != null)
        {
          editor.EditValue(this, this, component);
        }
      }
    }

    void ITypeDescriptorContext.OnComponentChanged()
    {
      this.ChangeService.OnComponentChanged(this._designer.Component, this._targetProperty, null, null);
    }

    bool ITypeDescriptorContext.OnComponentChanging()
    {
      try
      {
        this.ChangeService.OnComponentChanging(this._designer.Component, this._targetProperty);
      }
      catch (CheckoutException exception)
      {
        if (exception != CheckoutException.Canceled)
        {
          throw;
        }
        return false;
      }
      return true;
    }

    object IServiceProvider.GetService(System.Type serviceType)
    {
      if ((serviceType == typeof(ITypeDescriptorContext)) || (serviceType == typeof(IWindowsFormsEditorService)))
      {
        return this;
      }
      if (this._designer.Component.Site != null)
      {
        return this._designer.Component.Site.GetService(serviceType);
      }
      return null;
    }

    void IWindowsFormsEditorService.CloseDropDown()
    {
    }

    void IWindowsFormsEditorService.DropDownControl(System.Windows.Forms.Control control)
    {
    }

    System.Windows.Forms.DialogResult IWindowsFormsEditorService.ShowDialog(System.Windows.Forms.Form dialog)
    {
      IUIService service = (IUIService)((IServiceProvider)this).GetService(typeof(IUIService));
      if (service != null)
      {
        return service.ShowDialog(dialog);
      }
      return dialog.ShowDialog(this._designer.Component as System.Windows.Forms.IWin32Window);
    }

    private IComponentChangeService ChangeService
    {
      get
      {
        if (this._componentChangeSvc == null)
        {
          this._componentChangeSvc = (IComponentChangeService)((IServiceProvider)this).GetService(typeof(IComponentChangeService));
        }
        return this._componentChangeSvc;
      }
    }

    IContainer ITypeDescriptorContext.Container
    {
      get
      {
        if (this._designer.Component.Site != null)
        {
          return this._designer.Component.Site.Container;
        }
        return null;
      }
    }

    object ITypeDescriptorContext.Instance
    {
      get
      {
        return this._designer.Component;
      }
    }

    PropertyDescriptor ITypeDescriptorContext.PropertyDescriptor
    {
      get
      {
        return this._targetProperty;
      }
    }
  }

}
