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

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace EhLib
{

  public class DataAxisGridDataLink
  {
    #region GridDataLink privates
    private readonly DataAxisGrid grid;
    private readonly ListChangedEventHandler listChangedHandler;
    private readonly EventHandler positionChangedHandler;

    private object dataSource;
    private string dataMember;
    private CurrencyManager currencyManager;
    private NewRowEditState newCurrentRowState = NewRowEditState.Unchanged;
    private RowEditState currentListItemState;
    private bool currentRowModified;
    private bool editorHasValueToPush;
    #endregion GridDataLink Properties

    public DataAxisGridDataLink(DataAxisGrid grid)
    {
      this.grid = grid;
      listChangedHandler = CurrencyManagerListChanged;
      positionChangedHandler = CurrencyManagerPositionChanged;
      EditorHasValueToPush = false;
    }

    #region properties
    [Browsable(false)]
    public DataAxisGrid Grid
    {
      get { return grid; }
    }

    public bool Active
    {
      get { return (CurrencyManager != null); }
    }

    public bool AllowAdd
    {
      get
      {
        var bList = List as IBindingList;

        if ((bList != null) &&
            bList.AllowNew &&
            bList.SupportsChangeNotification &&
            (List != null) &&
            !List.IsReadOnly &&
            !List.IsFixedSize
        )
          return true;
        else
          return false;
      }
    }

    public bool AllowEdit
    {
      get
      {
        var bList = List as IBindingList;

        if ((bList != null) &&
            bList.AllowEdit &&
            (List != null) &&
            !List.IsReadOnly
        )
          return true;
        else
          return false;
      }
    }

    public bool AllowRemove
    {
      get
      {
        var bList = List as IBindingList;

        if ((bList != null) &&
            bList.AllowRemove &&
            bList.SupportsChangeNotification &&
            (List != null) &&
            !List.IsReadOnly &&
            !List.IsFixedSize
        )
          return true;
        else
          return false;
      }
    }

    public RowEditState CurrentListItemState
    {
      get
      {
        return currentListItemState;
      }

      private set
      {
        if (value != currentListItemState)
        {
          currentListItemState = value;
          OnCurrentListItemStateChanged();
        }
      }
    }

    public bool CurrentRowModified
    {
      get
      {
        return currentRowModified;
      }

      private set
      {
        if (value != currentRowModified)
        {
          currentRowModified = value;
          OnCurrentListItemModifiedStateChanged();
        }
      }
    }

    public bool EditorHasValueToPush
    {
      get
      {
        return editorHasValueToPush;
      }

      private set
      {
        if (value != editorHasValueToPush)
        {
          editorHasValueToPush = value;
          OnEditorHasValueToPushChanged();
        }
      }
    }

    public int Position
    {
      get
      {
        return currencyManager.Position;
      }
      set
      {
        if (currencyManager.Position != value)
        {
          if (CurrentListItemState != RowEditState.Browse)
          {
            Grid.PushEditorData();
            if (CurrentRowModified)
            {
              bool succeeded = EndCurrentEdit();
              if (!succeeded) return;
            }
            else
              CancelCurrentEdit();
          }
          currencyManager.Position = value;
        }
      }
    }

    public object DataSource
    {
      get
      {
        return this.dataSource;
      }
      set
      {
        if (this.dataSource != value)
        {
          this.dataSource = value;
          TryDataBinding();
        }
      }
    }

    public string DataMember
    {
      get
      {
        return this.dataMember;
      }
      set
      {
        if (this.dataMember != value)
        {
          this.dataMember = value;
          TryDataBinding();
        }
      }
    }

    public CurrencyManager CurrencyManager
    {
      get
      {
        return this.currencyManager;
      }
    }

    public IList List
    {
      get
      {
        if (this.currencyManager != null)
        {
          return this.currencyManager.List;
        }
        else
        {
          return null;
        }
      }
    }

    public bool SupportsSorting
    {
      get
      {
        IBindingList bList = null;

        if (CurrencyManager != null)
          bList = CurrencyManager.List as IBindingList;

        if (bList != null && bList.SupportsSorting)
          return true;
        else
          return false;
      }
    }

    public bool SupportsAdvancedSorting
    {
      get
      {
        IBindingList bList = null;
        IBindingListView bListView = null;

        if (CurrencyManager != null)
          bList = CurrencyManager.List as IBindingList;

        if (bList != null)
          bListView = bList as IBindingListView;

        if (bListView != null && bListView.SupportsAdvancedSorting)
          return true;
        else
          return false;
      }
    }

    public bool SupportsEditMode
    {
      get
      {
        bool result = this.currencyManager.Current is IEditableObject;
        return result;
      }
    }
    #endregion properties

    #region public methods
    public void SetCurrentRowValue(PropertyDescriptor propDescr, object value)
    {
      object srcRow = CurrencyManager.Current;
      propDescr.SetValue(srcRow, value);
      CurrentRowModified = true;
      EditorHasValueToPush = false;
    }

    public void AddNew()
    {
      if (CurrentListItemState != RowEditState.Edit)
      {
          if (EditorHasValueToPush)
          Grid.HideEditor(true);

        if (CurrentRowModified)
          EndCurrentEdit();
        else
          CancelCurrentEdit();
      }

      newCurrentRowState = NewRowEditState.Add;
      this.currencyManager.AddNew();
      newCurrentRowState = NewRowEditState.Unchanged;
    }

    public void BeginCurrentEdit()
    {
      if (CurrentListItemState == RowEditState.Add ||
          CurrentListItemState == RowEditState.Edit)
        return;

      var iEditObj = this.currencyManager.Current as IEditableObject;
      //var rowView = this.currencyManager.Current as DataRowView;
      //if (rowView != null)
      //{
      //  newCurrentRowState = NewRowEditState.Edit;
      //  rowView.Row.BeginEdit();
      //  CurrentRowState = RowEditState.Edit;
      //  newCurrentRowState = NewRowEditState.Unchanged;
      //  Grid.InvalidateGrid();
      //}
      //else 
      if (iEditObj != null)
      {
        newCurrentRowState = NewRowEditState.Edit;
        iEditObj.BeginEdit();
        CurrentListItemState = RowEditState.Edit;
        newCurrentRowState = NewRowEditState.Unchanged;
        Grid.InvalidateGrid();
      }
    }

    public void CancelCurrentEdit()
    {
      newCurrentRowState = NewRowEditState.Browse;
      this.currencyManager.CancelCurrentEdit();
      newCurrentRowState = NewRowEditState.Unchanged;
      CurrentRowModified = false;
      CurrentListItemState = RowEditState.Browse;
      EditorHasValueToPush = false;
      Grid.InvalidateGrid();
    }

    public bool EndCurrentEdit()
    {
      try
      {
        this.currencyManager.EndCurrentEdit();
      }
      catch (Exception e)
      {
        if (EhLibUtils.IsCriticalException(e))
        {
          throw;
        }

        DataGridSetDataValueErrorEventArgs evArgs = new DataGridSetDataValueErrorEventArgs(e);
        Grid.OnSetDataValueError(evArgs);
        return false;
      }

      CurrentListItemState = RowEditState.Browse;
      CurrentRowModified = false;
      EditorHasValueToPush = false;
      Grid.InvalidateGrid();
      return true;
    }

    public int MoveBy(int distance)
    {
      int maxDisc;

      if (distance > 0)
      {
        maxDisc = (currencyManager.Count - Position - 1);
        if (distance > maxDisc)
          distance = maxDisc;
        Position = Position + distance;
        return distance;
      }
      else
      {
        maxDisc = Position;
        if (-distance > maxDisc)
          distance = -maxDisc;
        Position = Position + distance;
        return distance;
      }
    }

    public void DeleteCurrent()
    {
      Grid.DeleteCurrentListItem();
    }
    #endregion 

    #region internal methods

    internal void TryDataBinding()
    {
      CurrencyManager cm;

      if (this.DataSource == null || Grid.BindingContext == null)
      {
        cm = null;
      }
      else
      {
        //try
        //{
        cm = (CurrencyManager)Grid.BindingContext[this.DataSource, this.DataMember];
        //}
        //catch (System.ArgumentException)
        //{
        //  return;
        //}
      }

      if (this.currencyManager != cm)
      {
        if (this.currencyManager != null)
        {
          this.currencyManager.ListChanged -= listChangedHandler;
          this.currencyManager.PositionChanged -= positionChangedHandler;
        }

        this.currencyManager = cm;

        if (this.currencyManager != null)
        {
          this.currencyManager.ListChanged += listChangedHandler;
          this.currencyManager.PositionChanged += positionChangedHandler;
        }

        CurrencyManagerListChanged(currencyManager, new ListChangedEventArgs(ListChangedType.Reset, -1));
      }
    }

    private void CurrencyManagerListChanged(object sender, ListChangedEventArgs e)
    {
      if (newCurrentRowState == NewRowEditState.Add)
        CurrentListItemState = RowEditState.Add;
      else if (newCurrentRowState == NewRowEditState.Browse)
        CurrentListItemState = RowEditState.Browse;
      else if (newCurrentRowState == NewRowEditState.Edit)
        CurrentListItemState = RowEditState.Edit;

      Grid.CurrencyManagerListChanged(sender, e);
    }

    private void CurrencyManagerPositionChanged(object sender, EventArgs e)
    {
      Grid.CurrencyManagerPositionChanged(sender, e);
    }

    public bool CheckSupportsEditMode(out IEditableObject editableObject)
    {
      editableObject = this.currencyManager.Current as IEditableObject;
      return (editableObject != null);
    }

    internal IList GetDataSortDescription()
    {
      IBindingList bList = null;
      IBindingListView bListView = null;
      IList res;
      ListSortDescription lsDesc;

      if (Grid.CurrencyManager != null)
        bList = Grid.CurrencyManager.List as IBindingList;

      if (bList != null)
        bListView = bList as IBindingListView;

      if (bList == null || !bList.SupportsSorting || !bList.IsSorted)
      {
        res = new List<ListSortDescription>();

      }
      else if (bList.SortProperty != null)
      {
        res = new List<ListSortDescription>();
        lsDesc = new ListSortDescription(bList.SortProperty, bList.SortDirection);
        res.Add(lsDesc);

      }
      else if (bListView != null)
      {
        res = bListView.SortDescriptions;
      }
      else
      {
        res = null;
      }

      return res;
    }

    internal virtual void Sort(DataGridColumn column, SortOrder sortOrder)
    {
      Debug.Assert(column.IsDataBound, "GridDataLink.Sort(..) column.IsDataBound = false");
      Debug.Assert(this.List is IBindingList, "GridDataLink.Sort(..) List is not IBindingList");

      IBindingList bndList = ((IBindingList)this.List);
      if (sortOrder == SortOrder.Ascending)
        bndList.ApplySort(column.PropDescr, ListSortDirection.Ascending);
      else if (sortOrder == SortOrder.Descending)
        bndList.ApplySort(column.PropDescr, ListSortDirection.Descending);
      else
        bndList.RemoveSort();
    }

    internal virtual void Sort(DataGridSortCollection sortMarkers)
    {
      Debug.Assert(SupportsSorting, "GridDataLink.Sorting is not Supported");
      if (sortMarkers.Count > 1)
        Debug.Assert(SupportsAdvancedSorting, "GridDataLink Sorting by multiple columns are not Supported");

      IBindingListView bListView = (IBindingListView)List;

      if (SupportsAdvancedSorting)
      {
        //ListSortDescriptionCollection sorts = new ListSortDescriptionCollection();
        List<ListSortDescription> sdList = new List<ListSortDescription>();

        foreach (DataGridSortItem sni in sortMarkers)
        {
          ListSortDescription sd = new ListSortDescription(sni.Column.PropDescr, sni.SortDirection);
          sdList.Add(sd);
        }
        bListView.ApplySort(new ListSortDescriptionCollection(sdList.ToArray()));
      }
    }

    internal void EditorValueChanged()
    {
      if (EditorHasValueToPush == false)
      {
        Grid.InvalidateGrid();
        EditorHasValueToPush = true;
      }
      if (SupportsEditMode && CurrentListItemState == RowEditState.Browse)
        BeginCurrentEdit();
    }

    internal void EditorClosed()
    {
      if (EditorHasValueToPush == true)
      {
        EditorHasValueToPush = false;
        Grid.InvalidateGrid();
      }
    }

    private void OnCurrentListItemStateChanged()
    {
      Grid.OnCurrentListItemStateChanged();
    }

    private void OnCurrentListItemModifiedStateChanged()
    {
      Grid.OnCurrentListItemModifiedStateChanged();
    }

    private void OnEditorHasValueToPushChanged()
    {
      Grid.OnEditorHasValueToPushChanged();
    }

    internal string GetRowError(int rowIndex)
    {
      IDataErrorInfo errInfo = null;
      try
      {
        errInfo = CurrencyManager.List[rowIndex] as IDataErrorInfo;
      }
      catch (Exception exception)
      {
        if (EhLibUtils.IsCriticalException(exception) && !(exception is IndexOutOfRangeException))
        {
          throw;
        }
        //DataGridViewDataErrorEventArgs dgvdee = new DataGridViewDataErrorEventArgs(exception, -1 /*columnIndex*/, rowIndex,
        //                                                                           DataGridViewDataErrorContexts.Display);
        //owner.OnDataErrorInternal(dgvdee);
        //if (dgvdee.ThrowException)
        //{
        //  throw dgvdee.Exception;
        //}
      }

      if (errInfo != null)
      {
        return errInfo.Error;
      }
      else
      {
        return String.Empty;
      }
    }
    #endregion
  }
}
