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

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Windows.Forms.VisualStyles;

namespace EhLib
{

  public enum DataValueSource
  {
    DataProperty, ListItem
  }

  public enum RowEditState
  {
    Browse, Edit, Add
  }

  public enum NewRowEditState
  {
    Unchanged, Browse, Edit, Add
  }

  public enum GridKeyboardAction
  {
    None, OpenEditor, OpenCloseEditor, NextRowCell, NextColCell,
  }

  public enum ContextMenuBuildingMode
  {
    ContextMenuStripPropertyDefined,
    LocalAndGlobalMenuCompound,
    LocalMenuCompound
    //ContextMenuStripPropertyOrCompoundGlobal
  }

  public interface IDataAxisGridInternal
  {
    bool IsInEndInit();
    void ResetViewOrderedPropBars();
  }

  //public interface IDataCellHolder
  //{
  //  Type GetValueDataType();
  //  Color GetBackColor();
  //  PropertyDescriptor GetPropertyDescriptor();

  //  void OnDataCellContentClick(DataGridDataCellEventArgs e);
  //}

  /// <summary>
  /// Base class for DataGridEh and DataVertGridEh
  /// </summary>
  [DesignerCategory("Code")]
  [ToolboxItem(false)]
  [ComplexBindingProperties("DataSource", "DataMember")]
  public abstract class DataAxisGrid : BaseGridControl, ISupportInitialize, IDataAxisGridInternal
  {
    #region private consts
    private const string ErrorCaptionMessage = "Error";
    private static readonly object EventKeyPaint = new object();
    #endregion

    #region privates
    private readonly DataAxisGridStaticPropBarCollection staticPropBars;

    private readonly DataAxisGridMainPropBarCollection propBars;
    private readonly List<PropertyAxisBar> propBarsList;

    private readonly DataAxisGridReadOnlyPropBarCollection dynamicPropBars;
    private readonly List<PropertyAxisBar> dynamicPropBarsList;

    private readonly DataAxisGridReadOnlyPropBarCollection viewOrderedPropBars;
    private readonly List<PropertyAxisBar> viewOrderedPropBarsList;
    protected List<PropertyAxisBar> OldViewOrderedPropBarsList;

    private readonly DataAxisGridReadOnlyPropBarCollection visiblePropBars;
    private readonly List<PropertyAxisBar> visiblePropBarsList;

    private bool inInitialization;
    private bool readOnly;
    private int gridLayoutSuspendCount;
    private bool gridLayoutChanged;

    internal protected bool InEndInit;
    internal protected bool EditorDataPushing;

    private DataAxisGridTitleBar titleBar;
    private DataAxisGridDataLink dataLink;

    private bool autoGeneratePropBars;
    private bool autoGeneratePropBarsStored = false;
    MouseHoverToolTip cellNonfitToolTip;
    MouseHoverToolTip cellHintToolTip;

    private Control cellEditControl;
    private Type cellEditControlType;
    //private bool editorTextWasChanged;
    private bool passNextCharMessageInEditControl;

    private GridKeyboardOptions keyboardOptions;
    #endregion privates

    protected DataAxisGrid()
    {
      //staticPropBars = new DataAxisGridStaticPropBarCollection(this);
      staticPropBars = CreateStaticPropBarCollection();

      propBarsList = new List<PropertyAxisBar>();
      propBars = CreatePropBarMainCollection(propBarsList);

      dynamicPropBarsList = new List<PropertyAxisBar>();
      dynamicPropBars = CreatePropBarReadOnlyCollection(dynamicPropBarsList);

      viewOrderedPropBarsList = new List<PropertyAxisBar>();
      viewOrderedPropBars = CreatePropBarReadOnlyCollection(viewOrderedPropBarsList);

      visiblePropBarsList = new List<PropertyAxisBar>();
      visiblePropBars = CreatePropBarReadOnlyCollection(visiblePropBarsList);
    }

    protected override void Dispose(bool disposing)
    {
      if (disposing)
      {
        for (int i = 0; i < StaticPropBars.Count; i++)
        {
          StaticPropBars[i].Dispose();
        }

        StaticPropBars.Clear();
      }

      base.Dispose(disposing);
    }

    #region properties
    [Browsable(false)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    public DataAxisGridStaticPropBarCollection StaticPropBars
    {
      get { return staticPropBars; }
    }

    [Browsable(false)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    protected internal DataAxisGridReadOnlyPropBarCollection DynamicPropBars
    {
      get { return dynamicPropBars; }
    }

    [Browsable(false)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    protected internal DataAxisGridMainPropBarCollection PropBars
    {
      get { return propBars; }
    }

    [Browsable(false)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    protected internal DataAxisGridReadOnlyPropBarCollection ViewOrderedPropBars
    {
      get { return viewOrderedPropBars; }
    }

    [Browsable(false)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    protected internal DataAxisGridReadOnlyPropBarCollection VisiblePropBars
    {
      get { return visiblePropBars; }
    }

    //DataSource & DataMember
    [TypeConverter("System.Windows.Forms.Design.DataSourceConverter, System.Design")]
    [Category("Data")]
    [DefaultValue(null)]
    public object DataSource
    {
      get { return DataLink.DataSource; }
      set { DataLink.DataSource = value; }
    }

    [Category("Data")]
    [Editor("System.Windows.Forms.Design.DataMemberListEditor, System.Design", "System.Drawing.Design.UITypeEditor, System.Drawing")]
    [DefaultValue(null)]
    public string DataMember
    {
      get { return DataLink.DataMember; }
      set { DataLink.DataMember = value; }
    }

    [Browsable(false)]
    public virtual int CurrentDataColIndex
    {
      get { return Col; }
    }

    [Browsable(false)]
    public virtual int CurrentDataRowIndex
    {
      get { return Row; }
    }
    //Other
    protected internal bool EditorBanned { get; set; }

    [Browsable(false)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    public new bool DesignMode
    {
      get { return base.DesignMode; }
    }

    [DefaultValue(false)]
    protected bool AutoGeneratePropBars
    {
      get
      {
        if (autoGeneratePropBarsStored)
          return autoGeneratePropBars;
        else
          return DefaultAutoGeneratePropBars();
      }

      set
      {
        if (autoGeneratePropBars != value || !autoGeneratePropBarsStored)
        {
          autoGeneratePropBars = value;
          autoGeneratePropBarsStored = true;
          if (!InInitialization)
            UpdatePropBarsList();
        }
      }
    }

    [Browsable(false)]
    public bool InInitialization
    {
      get
      {
        return inInitialization;
      }
    }

    [DefaultValue(false)]
    public bool ReadOnly
    {
      get
      {
        return readOnly;
      }

      set
      {
        readOnly = value;
      }
    }

    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
    protected internal DataAxisGridTitleBar TitleBar
    {
      get
      {
        if (titleBar == null)
          titleBar = CreateTitleBar();
        return titleBar;
      }
    }

    [Browsable(false)]
    [DefaultValue(false)]
    public CurrencyManager CurrencyManager
    {
      get { return DataLink.CurrencyManager; }
    }

    protected internal DataAxisGridDataLink DataLink
    {
      get
      {
        if (dataLink == null)
          dataLink = CreateDataLink();
        return dataLink;
      }
    }

    protected virtual PropertyAxisBar CurrentPropBar
    {
      get { return null; }
    }

    protected virtual object CurrentListItemBar
    {
      get { return null; }
    }

    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
    protected GridKeyboardOptions KeyboardOptions
    {
      get
      {
        if (keyboardOptions == null)
          keyboardOptions = CreateKeyboardOptions();

        return keyboardOptions;
      }
    }

    protected Control CellEditControl
    {
      get
      {
        return cellEditControl;
      }
    }
    #endregion

    #region events
    public new event EventHandler<ControlPaintEventArgs> Paint
    {
      add
      {
        this.Events.AddHandler(EventKeyPaint, value);
      }
      remove
      {
        this.Events.RemoveHandler(EventKeyPaint, value);
      }
    }
    #endregion

    #region methods
    //ISupportInitialize
    public void BeginInit()
    {
      if (inInitialization)
      {
        throw new InvalidOperationException("DataGridEh already in Initialization");
      }

      this.inInitialization = true;
      SuspendGridLayout();
    }

    public void EndInit()
    {
      Debug.Assert(inInitialization == true);

      InEndInit = true;
      this.inInitialization = false;
      try
      {
        OnEndInitialization();
        ResumeGridLayout();
      }
      finally
      {
        InEndInit = false;
      }
    }

    protected virtual void OnEndInitialization()
    {
    }

    //GridLayout
    public void SuspendGridLayout()
    {
      gridLayoutSuspendCount = gridLayoutSuspendCount + 1;
    }

    public void ResumeGridLayout()
    {
      Debug.Assert(gridLayoutSuspendCount > 0, "Unbalanance suspend/resume grid layout.");
      gridLayoutSuspendCount = gridLayoutSuspendCount - 1;

      if ((gridLayoutSuspendCount == 0) &&
          (gridLayoutChanged == true)
      )
        PerformGridLayout();
    }

    public void GridLayoutChanged()
    {
      if (gridLayoutSuspendCount == 0)
        PerformGridLayout();
      else
        gridLayoutChanged = true;
    }

    protected virtual void PerformGridLayout()
    {
      foreach (PropertyAxisBar bar in VisiblePropBars)
      {
        bar.PerformLayout();
      }
      gridLayoutChanged = false;
    }

    //AutoGeneratePropBars
    protected virtual bool DefaultAutoGeneratePropBars()
    {
      return StaticPropBars.Count == 0;
    }

    protected virtual bool ShouldSerializeAutoGeneratePropBars()
    {
      return autoGeneratePropBarsStored;
    }

    protected virtual void ResetAutoGeneratePropBars()
    {
      autoGeneratePropBarsStored = false;
    }

    //Editor
    protected internal virtual void PrepareForShowEditor()
    {

    }

    protected internal override void ShowEditor(bool selectAll)
    {
      Type newCellEditType;
      int localColIndex;
      int localRowIndex;

      BaseGridCellManager cellMan = CellByColRow(Col, Row, out localColIndex, out localRowIndex);

      if (!CanShowEditor()) return;
      if (cellMan == null) return;
      if (EditorMode == true) return;

      ClampInView(new GridCoord(Col, Row), true, true);

      newCellEditType = cellMan.EditType;
      if (newCellEditType == null) return;

      OnShowEditor();
      if (newCellEditType != cellEditControlType)
      {
        if (cellEditControl != null)
        {
          cellEditControl.Dispose();
          cellEditControl = null;
          cellEditControlType = newCellEditType;
        }
        cellEditControl = (Control)Activator.CreateInstance(newCellEditType);
        cellEditControl.Visible = false;
        Controls.Add(cellEditControl);
        ((IDataAxisGridCellInPlaceEditor)cellEditControl).EditorOwnerGrid = this;
      }

      cellMan.OccupyEditControl(cellEditControl, localColIndex, localRowIndex, selectAll);
      UpdateCellEditorBounds();
      cellEditControl.Visible = true;

      EditorMode = true;
      //editorTextWasChanged = false;
      UpdateFocus();
    }

    protected virtual void OnShowEditor()
    {

    }

    protected internal override bool HideEditor(bool acceptValue)
    {
      //base.HideEditor();
      if (EditorMode == false) return true;
      if (cellEditControl == null) return true;
      if (acceptValue)
      {
        if (!PushEditorData())
        {
          UpdateFocus();
          return false;
        }
      }
      EditorMode = false;
      UpdateFocus();
      cellEditControl.Visible = false;
      CurrentCellMan.ReleaseEditControl(cellEditControl);
      DataLink.EditorClosed();
      //editorTextWasChanged = false;
      return true;
    }

    protected internal virtual bool CanShowEditor()
    {
      return false;
    }

    protected internal virtual void EditorTextChanged(EventArgs e)
    {
      if (EditorMode)
      {
        //editorTextWasChanged = true;
        DataLink.EditorValueChanged();
      }
    }

    internal virtual void InitCellEditControl()
    {

    }

    internal virtual void UpdateCellEditorBounds()
    {
      if (cellEditControl == null) return;

      //var custAreaMeasures = new CellCustomAreaMeasures();
      int areaColIndex;
      int areaRowIndex;
      Rectangle cellRect = CellRect(Col, Row);
      Rectangle editorRect = cellRect;
      BaseGridCellManager cell = CellByColRow(Col, Row, out areaColIndex, out areaRowIndex);
      DataAxisGridDataCellMan dataCell = cell as DataAxisGridDataCellMan;

      //cell.SetCellCustomAreaMeasures(Col, Row, areaColIndex, areaRowIndex, custAreaMeasures);
      //Rectangle editorRect = EhLibUtils.ReduceRectCellCustomAreaMeasures(cellRect, custAreaMeasures);

      if (dataCell != null)
      {
        PropertyAxisBar propAxisBar;
        object listItemBar;

        dataCell.AxisObjectsByDataColRowIndex(areaColIndex, areaRowIndex, out propAxisBar, out listItemBar);
        editorRect = dataCell.GetCellClientRect(propAxisBar, listItemBar, cellRect);
      }

      cellEditControl.Location = editorRect.Location;
      cellEditControl.Size = editorRect.Size;
    }

    internal virtual bool PushEditorData()
    {
      bool result = true;

      if ((EditorMode) && (((IDataAxisGridCellInPlaceEditor)cellEditControl).EditorValueChanged == true))
      {
        EditorDataPushing = true;
        try
        {
          if (cellEditControl.Focused)
          {
            Form form = FindForm();
            if (form != null) form.Validate(true);
            //if (form != null) form.ValidateChildren();
          }
          object editorValue = ((IDataAxisGridCellInPlaceEditor)cellEditControl).EditorEditValue;
          PropertyAxisBar curPropBar = CurrentPropBar;

          result = SafePushValue(curPropBar, editorValue);
        }
        finally
        {
          EditorDataPushing = true;
        }
      }
      return result;
    }

    internal virtual bool SafePushValue(PropertyAxisBar propBar, object value)
    {
      bool result = true;

      Exception exception;

      result = PushValue(propBar, value, out exception);
      if (!result)
      {
        DataGridSetDataValueErrorEventArgs evArgs = new DataGridSetDataValueErrorEventArgs(exception);
        OnSetDataValueError(evArgs);
      }

      return result;
    }

    internal virtual bool PushValue(PropertyAxisBar propBar, object value, out Exception exception)
    {
      exception = null;
      try
      {
        SetDataValue(propBar, value);
      }
      catch (Exception e)
      {
        if (EhLibUtils.IsCriticalException(e))
        {
          throw;
        }
        exception = e;
        return false;
      }
      return true;
    }

    internal void SetDataValue(PropertyAxisBar propBar, object dataValue)
    {
      //if (propBar.PropDescr != null)
      //{
        object parsedValue = propBar.ParseValue(CurrentListItemBar, dataValue);
        DataLink.BeginCurrentEdit();
        propBar.SetValue(CurrentListItemBar, parsedValue);
        //DataLink.SetCurrentRowValue(propBar.PropDescr, parsedValue);
      //}
    }

    //Focus
    protected internal override void FocusGrid()
    {
      if (EditorMode == true)
        cellEditControl.Focus();
      else
        base.FocusGrid();
    }

    internal virtual void UpdateFocus()
    {
      if (EditorMode == true)
      {
        cellEditControl.Focus();
      }
      else
      {
        if ((cellEditControl != null) &&
           (cellEditControl.Focused == true))
        {
          this.Focus();
        }
      }
    }

    //Keyboard
    protected override bool ProcessKeyEventArgs(ref Message m)
    {
      if (m.Msg == NativeMethods.WM_SYSKEYDOWN || m.Msg == NativeMethods.WM_KEYDOWN)
      {
        passNextCharMessageInEditControl = false;
        //KeyEventArgs ke = new KeyEventArgs((Keys)(unchecked((int)(long)m.WParam)) | ModifierKeys);
        bool wmChatInQueue;
        Message charMsg;
        wmChatInQueue = EhLibUtils.PeekMessage(this, out charMsg, NativeMethods.WM_CHAR, NativeMethods.WM_CHAR, false);
        if (wmChatInQueue)
        {
          KeyPressEventArgs ke = new KeyPressEventArgs(unchecked((char)(long)charMsg.WParam));
          if (ke.KeyChar == (char)Keys.Escape) return false;

          PrepareForShowEditor();
          BaseGridCellManager currentCell = CurrentCellMan;

          if ((currentCell != null) && currentCell.IsCharToEnterEditMode(ke))
          {
            ClampInView(new GridCoord(Col, Row), true, true);
            ShowEditor(true);
            if (EditorMode)
            {
              if (this.cellEditControl != null)
              {
                passNextCharMessageInEditControl = true;
                //EhLibUtils.SendMessage(this.cellEditControl, m.Msg, m.WParam, m.LParam);
                return true;
              }
            }
          }
        }
      }
      else if ((m.Msg == NativeMethods.WM_CHAR) &&
                (passNextCharMessageInEditControl == true) &&
                cellEditControl.Focused
                )
      {
        passNextCharMessageInEditControl = false;
        KeyPressEventArgs ke = new KeyPressEventArgs(unchecked((char)(long)m.WParam));
        SendKeys.Send(ke.KeyChar.ToString());
        //EhLibUtils.SendMessage(this.cellEditControl, m.Msg, m.WParam, m.LParam);
        return true;
      }
      return base.ProcessKeyEventArgs(ref m);
    }

    protected virtual bool ProcessF2Key(Keys keyData)
    {
      if (EditorMode == false)
      {
        ShowEditor(false);
        return true;
      }
      return false;
    }

    protected virtual bool ProcessEnterKey(Keys keyData)
    {
      if (KeyboardOptions.EnterKeyAction == GridKeyboardAction.OpenCloseEditor)
      {
        if (EditorMode == true)
        {
          bool saveData;
          IDataAxisGridCellInPlaceEditor ipe = (cellEditControl as IDataAxisGridCellInPlaceEditor);
          if (ipe != null)
            saveData = ipe.EditorValueChanged;
          else
            saveData = true;
          HideEditor(saveData);
        }
        else
        {
          ShowEditor(true);
        }
        return true;
      }
      else if (KeyboardOptions.EnterKeyAction == GridKeyboardAction.OpenEditor)
      {
        if (EditorMode == false)
        {
          ShowEditor(true);
          return true;
        }
      }
      else if (KeyboardOptions.EnterKeyAction == GridKeyboardAction.NextColCell)
      {
        GridMoveCellDirection direction;
        if ((keyData & Keys.Shift) == Keys.Shift)
          direction = GridMoveCellDirection.Backward;
        else
          direction = GridMoveCellDirection.Forward;

        bool result = NextCol(direction, true, true, false, true);
        return result;
      }

      return false;
    }

    protected virtual bool ProcessEscapeKey(Keys keyData)
    {
      if (EditorMode == true)
      {
        HideEditor(false);
        return true;
      }

      if ((DataLink.CurrentListItemState == RowEditState.Edit) ||
          (DataLink.CurrentListItemState == RowEditState.Add))
      {
        DataLink.CancelCurrentEdit();
        return true;
      }

      return false;
    }

    //Errors handling
    protected internal virtual void OnSetDataValueError(DataGridSetDataValueErrorEventArgs e)
    {
      string errorText = e.Exception.Message;
      MessageBox.Show(errorText, ErrorCaptionMessage, MessageBoxButtons.OK, MessageBoxIcon.Error);
    }

    //IDataAxisGridInternal

    bool IDataAxisGridInternal.IsInEndInit()
    {
      return InEndInit;
    }

    //Other
    protected virtual GridKeyboardOptions CreateKeyboardOptions()
    {
      return new GridKeyboardOptions(this);
    }

    protected virtual bool PrepareChangeCellPos()
    {
      bool result;
      result = PushEditorData();
      if (result && EditorMode)
        HideEditor(false);
      return result;
    }

    protected override void OnBindingContextChanged(EventArgs e)
    {
      DataLink.TryDataBinding();
      base.OnBindingContextChanged(e);
    }

    protected internal virtual void PropertyAxisBarVisibleChanged(PropertyAxisBar propAxisBar, bool value)
    {
      if (PropBars.Updating) return;
      UpdatePropBarsView();
    }

    protected internal void CreateAutoGeneratedColumns()
    {
      dynamicPropBarsList.Clear();

      if (CurrencyManager != null)
      {
        int colIndex = 0;

        Type listItemType = ListBindingHelper.GetListItemType(CurrencyManager.List);

        if (!ListItemIsSimple(listItemType))
        {
          foreach (PropertyDescriptor prop in CurrencyManager.GetItemProperties())
          {
            PropertyAxisBar[] bars = CreateDynamicColumnsForDataProperty(prop);
            if (bars == null) continue;

            foreach(var bar in bars)
            { 
              if (bar == null) continue;
              bar.DataPropertyName = prop.Name;
              bar.PropDescr = prop;
              bar.AutoGenerated = true;
              bar.Grid = this;
              InitDynaPropBar(bar, prop);

              dynamicPropBarsList.Add(bar);
              colIndex = colIndex + 1;
            }
          }
        }
        else
        {
          PropertyAxisBar propBar = CreatePropBarForListItemType(listItemType);
          if (propBar != null)
          {
            propBar.DataValueSource = DataValueSource.ListItem;
            propBar.DataPropertyName = "";
            propBar.PropDescr = null;
            propBar.AutoGenerated = true;
            propBar.Grid = this;
            InitDynaPropBar(propBar, null);

            dynamicPropBarsList.Add(propBar);
          }
        }
      }
    }

    //protected virtual bool OnCheckIfCreateDynaColumnForDataProperty(PropertyDescriptor prop)
    //{
    //  PropertyAxisBar statPropBar = StaticPropBars.FindBarByDataProperty(prop.Name);
    //  if (statPropBar != null)
    //    return false;
    //  else
    //    return true;
    //}

    protected virtual PropertyAxisBar[] CreateDynamicColumnsForDataProperty(PropertyDescriptor prop)
    {
      PropertyAxisBar statPropBar = StaticPropBars.FindBarByDataProperty(prop.Name);

      DataAxisCreatingDynamicColumnsForDataPropertyEventArgs e =
        GetDataAxisCreatingDynamicColumnsForDataPropertyEventArgs(prop, statPropBar != null);
      //var e = new DataAxisCreatingDynamicColumnsForDataPropertyEventArgs(this, prop, statPropBar != null);
      ProcessCreateDynamicPropBarsForDataPropertyEvent(e);
      if (!e.Handled)
        OnCreateDynamicPropBarsForDataProperty(e);
      return e.PropertyBars;
    }

    protected virtual DataAxisCreatingDynamicColumnsForDataPropertyEventArgs GetDataAxisCreatingDynamicColumnsForDataPropertyEventArgs(
      PropertyDescriptor propDescriptor, bool inStaticColumns)
    {
      return new DataAxisCreatingDynamicColumnsForDataPropertyEventArgs(this, propDescriptor, inStaticColumns);
    }

    protected virtual void ProcessCreateDynamicPropBarsForDataPropertyEvent(DataAxisCreatingDynamicColumnsForDataPropertyEventArgs e)
    {
    }

    protected virtual void OnCreateDynamicPropBarsForDataProperty(DataAxisCreatingDynamicColumnsForDataPropertyEventArgs e)
    {
      if (e.InStaticPropBars)
      {
        e.PropertyBars = null;
      }
      else
      {
        e.PropertyBars = new PropertyAxisBar[1];
        e.PropertyBars[0] = CreatePropBarForDataProperty(e.PropertyDescriptor);
      }
    }

    protected virtual PropertyAxisBar CreatePropBarForDataProperty(PropertyDescriptor prop)
    {
      return CreatePropBarFromType(prop.PropertyType);
    }

    protected virtual PropertyAxisBar CreatePropBarForListItemType(Type type)
    {
      return CreatePropBarFromType(type);
    }

    public abstract PropertyAxisBar CreatePropBarFromType(Type type);

    public abstract Type GetAxisBarTypeForDataType(Type type);

    protected virtual void InitDynaPropBar(PropertyAxisBar bar, PropertyDescriptor prop)
    {
      DataTable dataTable = GetDataTable();

      if ((dataTable != null) && (dataTable.Columns[prop.Name] != null))
        bar.MaxLength = dataTable.Columns[prop.Name].MaxLength;
    }

    internal protected virtual void UpdatePropBarsList()
    {
      if (InInitialization) return;

      if (PropBars.Updating)
      {
        PropBars.listGonnaChange = true;
        return;
      }


      PropertyAxisBar[] oldList = new PropertyAxisBar[propBarsList.Count];
      propBarsList.CopyTo(oldList);

      propBarsList.Clear();

      PropBars.BeginUpdate();
      //try
      {
        //StaticColumns.RemoveAutoGeneratedColumns();
        if (AutoGeneratePropBars && !DesignMode)
          CreateAutoGeneratedColumns();

        foreach (PropertyAxisBar bar in StaticPropBars)
          propBarsList.Add(bar);
        foreach (PropertyAxisBar bar in DynamicPropBars)
          propBarsList.Add(bar);

        foreach (PropertyAxisBar bar in PropBars)
        {
          if (!string.IsNullOrEmpty(bar.DataPropertyName))
          {
            PropertyDescriptor prop = null;
            if (CurrencyManager != null)
            {
              PropertyDescriptorCollection dataItemProps = CurrencyManager.GetItemProperties();
              prop = dataItemProps.Find(bar.DataPropertyName, true);
            }
            if (prop != null)
            {
              bar.PropDescr = prop;
              //bar.DataType = prop.PropertyType;
            }
            else
            {
              bar.PropDescr = null;
              //bar.DataType = null;
            }
          }
          bar.UpdatedInList();
        }

        bool listChanged = false;
        if (propBarsList.Count != oldList.Length)
        {
          listChanged = true;
        }
        else
        {
          for (int i = 0; i < oldList.Length; i++)
          {
            if (oldList[i] != propBarsList[i])
            {
              listChanged = true;
              break;
            }
          }
        }

        if (listChanged)
          PropBarsListUpdated();

      }
      //finally
      {
        PropBars.EndUpdate();
      }

    }

    protected internal void CheckPropBarsPropDescr()
    {
      PropertyDescriptorCollection dataItemProps;

      if (CurrencyManager == null)
        dataItemProps = null;
      else
        dataItemProps = CurrencyManager.GetItemProperties();

      foreach (PropertyAxisBar bar in PropBars)
      {
        if (bar.PropDescr != null)
        {
          if (dataItemProps == null)
            //Debug.Fail("column.propDescr reference is inconsistent.");
            bar.PropDescr = null;
          else if (dataItemProps.IndexOf(bar.PropDescr) < 0)
            Debug.Fail("column.propDescr reference is inconsistent.");
        }
      }
    }

    protected virtual void PropBarsListUpdated()
    {
    }

    protected void SetPropBarsDisplayOrder(Collection<PropertyAxisBar> newDispOrderedPropBars)
    {
      bool orderChagned = false;

      if (newDispOrderedPropBars.Count != ViewOrderedPropBars.Count)
        throw new InvalidOperationException("List of bars must be complete and not redundant.");

      IEnumerator<PropertyAxisBar> enmrt = newDispOrderedPropBars.GetEnumerator();
      for (int i = 0; enmrt.MoveNext(); i++)
      {
        PropertyAxisBar ordCol = enmrt.Current;
        if (!ViewOrderedPropBars.Contains(ordCol))
          throw new InvalidOperationException(String.Format("PropBar {0} not in the list", ordCol));
        if (ordCol != ViewOrderedPropBars[i])
          orderChagned = true;
      }

      if (orderChagned == false) return;

      for (int i = 0; i < newDispOrderedPropBars.Count; i++)
        newDispOrderedPropBars[i].DisplayIndexInternal = i;

      PropBarsDisplayIndexChanged();
    }

    protected internal void PropBarsDisplayIndexChanged()
    {
      if (InInitialization || PropBars.Updating) return;
      UpdatePropBarsView();
    }

    protected void UpdateColumnsDisplayIndex()
    {
      List<PropertyAxisBar> oldColList;
      var voColList = viewOrderedPropBarsList;

      if (OldViewOrderedPropBarsList != null)
        oldColList = OldViewOrderedPropBarsList;
      else
        oldColList = new List<PropertyAxisBar>(viewOrderedPropBarsList);

      voColList.Clear();

      FillPropBarsViewOrderedList(voColList);

      for (int i = 0; i < voColList.Count; i++)
      {
        PropertyAxisBar column = voColList[i];
        column.DisplayIndexInternal = i;
        column.NewColumn = false;
        //column.DisplayIndexQuery = false;
        //column.DisplayIndexQueryIndex = -1;
      }

      bool listChanged = false;

      if (voColList.Count != oldColList.Count)
        listChanged = true;
      else
      {
        for (int i = 0; i < voColList.Count; i++)
        {
          if (voColList[i] != oldColList[i])
          {
            listChanged = true;
            break;
          }
        }

      }

      if (listChanged)
        OnOrderedColumnsListChanged(new EventArgs());

      OldViewOrderedPropBarsList = null;
      UpdateVisibleColumns();
    }

    protected virtual void FillPropBarsViewOrderedList(List<PropertyAxisBar> voColList)
    {
      foreach (PropertyAxisBar col in PropBars)
      {
        voColList.Add(col);
        if (col.DisplayIndex < 0)
          col.DisplayIndexInternal = PropBars.Count + 1;
      }
      voColList.Sort(CompareDisplayOrderColumns);

      // New columns have higher priority
      var newCols = new List<PropertyAxisBar>();
      for (int i = 0; i < voColList.Count; i++)
      {
        PropertyAxisBar col = voColList[i];
        if (col.NewColumn && col.DisplayIndex >= 0)
        {
          newCols.Add(col);
          voColList[i] = null;
        }
      }

      if (newCols.Count > 0)
      {
        voColList.RemoveAll(col => col == null);
        newCols.Sort(CompareDisplayOrderColumns);

        foreach (PropertyAxisBar col in newCols)
        {
          if (col.DisplayIndex < voColList.Count)
            voColList.Insert(col.DisplayIndex, col);
          else
            voColList.Add(col);
        }
      }
    }

    protected virtual int CompareDisplayOrderColumns(PropertyAxisBar x, PropertyAxisBar y)
    {
      if (x.DisplayIndex < y.DisplayIndex)
      {
        return -1;
      }
      else if (x.DisplayIndex > y.DisplayIndex)
      {
        return 1;
      }
      else
      {
        if (x.Index < y.Index)
          return -1;
        else if (x.Index < y.Index)
          return 1;
        else
          return 0;
      }
    }

    protected virtual void OnOrderedColumnsListChanged(EventArgs e)
    {
    }

    protected virtual void VisibleColumnsListChanged()
    {

    }

    protected internal virtual void UpdatePropBarsView()
    {
      UpdateColumnsDisplayIndex();
    }

    protected internal void UpdateVisibleColumns()
    {
      int i;
      bool listChanged = false;
      List<PropertyAxisBar> oldVisiblePropBarsList = new List<PropertyAxisBar>(visiblePropBarsList);

      visiblePropBarsList.Clear();

      i = 0;
      foreach (PropertyAxisBar bar in ViewOrderedPropBars)
      {
        if (bar.IsVisible)
        {
          visiblePropBarsList.Add(bar);
          bar.VisibleIndex = i;
          i++;
        }
        else
        {
          bar.VisibleIndex = -1;
        }
      }

      if (oldVisiblePropBarsList.Count != visiblePropBarsList.Count)
      {
        listChanged = true;
      }
      else
      {
        for (i = 0; i < visiblePropBarsList.Count; i++)
        {
          if (oldVisiblePropBarsList[i] != visiblePropBarsList[i])
          {
            listChanged = true;
            break;
          }
        }
      }

      if (listChanged)
        VisibleColumnsListChanged();
    }

    protected DataTable GetDataTable()
    {
      BindingSource bindingSource = (DataSource as BindingSource);
      if ((bindingSource != null) && string.IsNullOrEmpty(DataMember))
      {
        DataSet dataSet = (bindingSource.DataSource as DataSet);
        if ((dataSet != null) && (bindingSource.DataMember != null))
        {
          DataTable dataTable = dataSet.Tables[bindingSource.DataMember];
          return dataTable;
        }
      }
      return null;
    }

    protected internal virtual void CurrencyManagerListChanged(object sender, ListChangedEventArgs e)
    {
    }

    protected internal virtual void CurrencyManagerPositionChanged(object sender, EventArgs e)
    {
    }

    protected internal virtual void OnCurrentListItemStateChanged()
    {
    }

    protected internal virtual void OnCurrentListItemModifiedStateChanged()
    {
    }

    protected internal virtual void OnEditorHasValueToPushChanged()
    {
    }

    protected internal virtual void SetPropBarDisplayIndex(PropertyAxisBar propertyAxisBar, int newDisplayIndex)
    {
      if (InInitialization) return;
      if (PropBars.Updating)
      {
        propertyAxisBar.DisplayIndexInternal = newDisplayIndex;
      }
      else
      {
        if (viewOrderedPropBarsList[propertyAxisBar.DisplayIndex] != propertyAxisBar)
          throw new InvalidOperationException("propertyAxisBar.DisplayIndex is incorrect");

        OldViewOrderedPropBarsList = new List<PropertyAxisBar>(viewOrderedPropBarsList);
        viewOrderedPropBarsList.RemoveAt(propertyAxisBar.DisplayIndex);
        viewOrderedPropBarsList.Insert(newDisplayIndex, propertyAxisBar);
        for (int i = 0; i < viewOrderedPropBarsList.Count; i++)
        {
          viewOrderedPropBarsList[i].DisplayIndexInternal = i;
        }
      }
    }

    void IDataAxisGridInternal.ResetViewOrderedPropBars()
    {
      ResetViewOrderedPropBars();
    }

    internal void ResetViewOrderedPropBars()
    {
      foreach(var axisBar in ViewOrderedPropBars)
        axisBar.DisplayIndexInternal = -1;
      PropBarsDisplayIndexChanged();
    }

    protected internal virtual void OnSuperTitleNameChanged(DataGridSuperTitle superTitle)
    {
    }

    protected internal virtual void OnPropBarNameChanged(PropertyAxisBar propertyAxisBar)
    {
      //OnColumnNameChanged
    }

    internal protected virtual void UpdateBaseFixedBands()
    {
      //UpdateBaseFixedBands
    }

    protected virtual DataAxisGridReadOnlyPropBarCollection CreatePropBarReadOnlyCollection(IList<PropertyAxisBar> list)
    {
      return new DataAxisGridReadOnlyPropBarCollection(this, list);
    }

    protected virtual DataAxisGridMainPropBarCollection CreatePropBarMainCollection(IList<PropertyAxisBar> list)
    {
      return new DataAxisGridMainPropBarCollection(this, list);
    }

    protected virtual DataAxisGridStaticPropBarCollection CreateStaticPropBarCollection()
    {
      return new DataAxisGridStaticPropBarCollection(this);
    }

    internal protected virtual DataAxisGridDataLink CreateDataLink()
    {
      return new DataAxisGridDataLink(this);
    }

    internal protected abstract DataAxisGridTitleBar CreateTitleBar();
    //{
    //  //return new DataAxisGridTitleBar(this);
    //}

    protected internal virtual void OnPropBarAdded(PropertyAxisBar propBar)
    {
      //OnColumnAdded
    }

    protected internal virtual void OnPropBarRemoved(PropertyAxisBar propBar)
    {
      //OnColumnRemoved
    }

    protected internal virtual void PropBarsUpdateCommitted()
    {
      UpdatePropBarsView();
      //ColumnsUpdateCommited()
    }

    protected internal virtual bool PropBarCanBeVisible(PropertyAxisBar bar)
    {
      return true;
    }

    protected virtual bool ListItemIsSimple(Type listItemType)
    {
      if (listItemType.IsPrimitive ||
          listItemType == typeof(string) ||
          listItemType == typeof(DateTime))
        return true;
      else
        return false;
    }

    protected internal virtual void UpdatePaddingsForSidePadding()
    {
    }

    protected internal virtual void SidePaddingChanged()
    {
      UpdatePaddingsForSidePadding();
    }

    protected internal abstract Padding GetDefaultPaddingForAlign(HorizontalAlignment horzAlign, VerticalAlignment vertAlign);
    protected internal abstract Padding GetDataCellSidePadding();

    protected internal virtual HorizontalAlignment DefaultHorzAlignForType(Type dataType)
    {
      return EhLibManager.DefaultEhLibManager.GetHorzAlignForType(dataType);
    }

    protected internal virtual VerticalAlignment DefaultVertAlign()
    {
      return VerticalAlignment.Top;
    }

    public virtual void ShowCellNonFitToolTip(string toolTipText, BaseGridCellEventArgs e)
    {
      Point hintShowPos;

      if (cellNonfitToolTip == null)
      {
        cellNonfitToolTip = new MouseHoverToolTip();
        cellNonfitToolTip.ShowAlways = true;
        cellNonfitToolTip.InitialDelay = 0;
        cellNonfitToolTip.UseFading = true;
        cellNonfitToolTip.UseAnimation = false;
        cellNonfitToolTip.AutoPopDelay = 0;
      }

      hintShowPos = new Point(e.CellRect.X, e.CellRect.Y + e.CellRect.Height);
      cellNonfitToolTip.Active = true;
      cellNonfitToolTip.Show(toolTipText, this, hintShowPos, -1, 3000);
    }

    public virtual void HideCellNonFitToolTip()
    {
      if (cellNonfitToolTip == null) return;
      cellNonfitToolTip.Hide();
    }

    public virtual void ShowCellHintTooltip(string toolTipText, int delay)
    {
      if (cellHintToolTip == null)
      {
        cellHintToolTip = new MouseHoverToolTip();
      }
      cellHintToolTip.Show(toolTipText, this, delay, 3000);
    }

    public virtual void HideCellHintToolTip()
    {
      if (cellHintToolTip == null) return;
      cellHintToolTip.Hide();
    }

    protected override void OnValidating(CancelEventArgs e)
    {
      HideEditor(true);
      if (DataLink.CurrentListItemState != RowEditState.Browse)
      {
        if (DataLink.CurrentRowModified)
          DataLink.EndCurrentEdit();
        else
          DataLink.CancelCurrentEdit();
      }
      base.OnValidating(e);
    }

    protected internal virtual void OnDataCellMouseDownEvent(DataAxisGridDataCellMouseEventArgs e)
    {
    }

    protected internal virtual void OnDataCellContentClick(DataAxisGridDataCellEventArgs e)
    {
    }

    protected internal virtual void RowHeightAffectPropertyChanged()
    {
    }

    protected internal virtual string GetHighlightSearchingData(PropertyAxisBar propAxisBar, object listItemBar, 
      string cellText, out CharacterRange[] charRanges)
    {
      charRanges = null;
      return null;
    }

    protected internal virtual void OnDataCellClick(DataAxisGridDataCellEventArgs e)
    {
    }

    protected internal virtual void OnFillDataCellFormatParams(DataAxisGridDataCellFormatParamsEventArgs formatParams)
    {
    }

    protected override void HandlePaintEvent(ControlPaintEventArgs e)
    {
      var eh = this.Events[EventKeyPaint] as EventHandler<ControlPaintEventArgs>;
      if (eh != null)
      {
        eh(this, e);
      }
    }

    protected internal virtual void OnDataCellContextMenuStripNeeded(DataAxisGridDataCellContextMenuStripNeededEventArgs age)
    {
      throw new NotImplementedException();
    }

    protected internal virtual void HandleDataCellMouseContextMenuStripNeededEvent(DataAxisGridDataCellContextMenuStripNeededEventArgs e)
    {
    }

    protected internal virtual void HandleDataCellClientAreaNeededEvent(DataAxisGridDataCellClientAreaNeededEventArgs e)
    {
    }

    internal protected virtual void DeleteCurrentListItem()
    {
      CurrencyManager.RemoveAt(CurrencyManager.Position);
    }
    #endregion

  }

  [TypeConverter(typeof(ExpandableObjectConverter))]
  public class GridKeyboardOptions
  {
    #region privates
    //private readonly DataAxisGrid grid;

    private GridKeyboardAction tabKeyAction = GridKeyboardAction.None;
    private GridKeyboardAction enterKeyAction = GridKeyboardAction.OpenCloseEditor;
    private GridKeyboardAction f2KeyAction = GridKeyboardAction.OpenEditor;
    #endregion privates

    public GridKeyboardOptions(DataAxisGrid grid)
    {
      //this.grid = grid;
    }

    #region design-time properties
    //[DefaultValue(GridKeyboardAction.NextColCell)]
    public virtual GridKeyboardAction TabKeyAction
    {
      get { return tabKeyAction; }
      set { tabKeyAction = value; }
    }

    [DefaultValue(GridKeyboardAction.OpenCloseEditor)]
    public virtual GridKeyboardAction EnterKeyAction
    {
      get { return enterKeyAction; }
      set { enterKeyAction = value; }
    }

    [DefaultValue(GridKeyboardAction.OpenEditor)]
    public virtual GridKeyboardAction F2KeyAction
    {
      get { return f2KeyAction; }
      set { f2KeyAction = value; }
    }
    #endregion  design-time properties

  }

}
