using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.Windows.Forms.Design;
using System.Diagnostics;
using System.Reflection;

namespace CaLib.User.DynamicProperty
{
  
  public partial class EnumEditorUI : UserControl
  {
    private class EnumItemData
    {
      public EnumItemData()
      {

      }
      public EnumItemData(string name, string dispayName, string description)
      {
        Name = name;
        DispName = dispayName;
        Description = description;
      }
      public EnumItemData(string name, string dispayName, string description, int position)
      {
        Name = name;
        DispName = dispayName;
        Description = description;
        Index = position;
      }
      private string m_Name = String.Empty;

      public string Name
      {
        get
        {
          return m_Name;
        }
        set
        {
          m_Name = value;
        }
      }
      private string m_DispName = String.Empty;

      public string DispName
      {
        get
        {
          return m_DispName;
        }
        set
        {
          m_DispName = value;
        }
      }
      private string m_Description = String.Empty;

      public string Description
      {
        get
        {
          return m_Description;
        }
        set
        {
          m_Description = value;
        }
      }
      private int m_Index = -1;

      public int Index
      {
        get { return m_Index; }
        set { m_Index = value; }
      }

      public override string ToString()
      {
        return m_DispName;
      }
    }  
    // end of EnumItemData class

    private Type m_Type = Type.Missing.GetType();
    IWindowsFormsEditorService m_editorService = null;

    private bool m_bNoFire = false;
    private bool m_bFlag = false;
    private int m_NoneIndex = -1;  // in an enum with FlagAttribute, this memeber represent value 0
    public EnumEditorUI()
    {
      InitializeComponent();

    }

    public void SetData(ITypeDescriptorContext context, IWindowsFormsEditorService editorService, int value)
    {
      m_editorService = editorService;
      m_Type = context.PropertyDescriptor.PropertyType;
      Debug.Assert(m_Type.IsEnum, "Type must be a typeof of Enum.");

      if (context.PropertyDescriptor.PropertyType.GetCustomAttributes(typeof(FlagsAttribute), false).Length > 0)
      {
        m_bFlag = true;
      }

      lstEnumValue.Visible = !m_bFlag;
      chklstEnumValue.Visible = m_bFlag;

      chklstEnumValue.Items.Clear();
      lstEnumValue.Items.Clear();
      ListBox listBox = (m_bFlag ? (ListBox)chklstEnumValue : lstEnumValue); // get generic interface to work with



      FieldInfo[] fields = m_Type.GetFields(BindingFlags.Public | BindingFlags.Static);
      TypeConverter converter = TypeDescriptor.GetConverter(m_Type);  // this converter may be a default one or EnumTypeConverter

      m_bNoFire = true;
      // load up the appropriate list box
      foreach (FieldInfo fi in fields)
      {
        string sName = fi.Name;
        string sDispName = sName;  
        string sDesc = String.Empty;
        bool bBrowsable = true;

        EnumItemAttribute[] attrItems = fi.GetCustomAttributes(typeof(EnumItemAttribute), false) as EnumItemAttribute[];
        if (attrItems != null && attrItems.Length > 0)
        {
          bBrowsable = attrItems[0].Browsable;
          sDesc = (attrItems[0].Description != null ? attrItems[0].Description : sDesc);
        }
        if (bBrowsable)
        {
          // Now we use the converter to get the display name.
          // why not use EnumItemAttribute's DisplayName property instead? Well, if the enum does not have EnumTypeConverter attribute,
          // we could have a situation where the PropertyGrid's cell contains the name of the enum item and the dropdown contains
          // the DisplayName from the EnumItemAttribute - resulting in consistancy.

          sDispName = converter.ConvertToString( fi.GetValue(null) );
          if ((int)fi.GetValue(null) == 0)
          {
            m_NoneIndex = listBox.Items.Count;
          }
          listBox.Items.Add(new EnumItemData(sName, sDispName, sDesc, listBox.Items.Count));
        }

      }

      // now check the item(s) that correspondes to the value
      string sDelimitedValues = Enum.Format(m_Type, value, "G");
      string[] arrValue = sDelimitedValues.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
      foreach (string sValue in arrValue)
      {
        string sTrimmedValue = sValue.Trim();

        for(int i = 0; i < listBox.Items.Count; i++)
        {
          EnumItemData data = (EnumItemData)listBox.Items[i];  //objItem;
          if (String.Compare(data.Name, sTrimmedValue, true) == 0)
          {
            if (listBox is CheckedListBox)
            {
              CheckedListBox chk = (CheckedListBox)listBox;
              chk.SetItemChecked(data.Index, true);
              break;
            }
            else
            {
              listBox.SetSelected(data.Index, true);
              break;
            }
          }
        }
      }
      if (m_bFlag)
      {
        listBox.SetSelected(0, true);
      }
      m_bNoFire = false;


    }

    public object GetValue()
    {
      if (m_bFlag)
      {
        StringBuilder sb = new StringBuilder(100);
        foreach (object objItem in chklstEnumValue.CheckedItems)
        {
          EnumItemData data = (EnumItemData)objItem;
          if (sb.Length > 0)
          {
            sb.Append(",");
          }
          sb.Append(data.Name);
        }
        return Enum.Parse(m_Type, sb.ToString());
      }
      else
      {
        EnumItemData data = (EnumItemData)lstEnumValue.SelectedItem;
        return Enum.Parse(m_Type, data.Name);
        
      }

    }

    private void chklstEnumValue_MouseDoubleClick(object sender, MouseEventArgs e)
    {
      m_editorService.CloseDropDown();
    }

    private void chklstEnumValue_ItemCheck(object sender, ItemCheckEventArgs e)
    {
      if (m_bNoFire == true)
      {
        return;
      }

      if (e.NewValue == CheckState.Unchecked && chklstEnumValue.CheckedItems.Count == 1)  // user trying to uncheck the last checked item.
      {                                                                                // won't allow that
        e.NewValue = CheckState.Checked;
        return;
      }

      m_bNoFire = true;

      if (e.Index == m_NoneIndex) // the None one. so uncheck all other      
      {
        for (int i = 0; i < chklstEnumValue.Items.Count; i++)
        {
          chklstEnumValue.SetItemChecked(i, i == e.Index);
        }
      }
      else if (m_NoneIndex != -1)
      {
          chklstEnumValue.SetItemChecked(m_NoneIndex, false);  // uncheck the None one.
      }
      m_bNoFire = false;

    }

    private void lstEnumValue_SelectedIndexChanged(object sender, EventArgs e)
    {
      ListBox listBox = (ListBox)sender;

      EnumItemData data = (EnumItemData)listBox.SelectedItem;
      lblDispName.Text = data.DispName;
      lblDesc.Text = data.Description;
    }



  }
}
