//------------------------------------------------------------------------------
// <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.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.Windows.Forms;

namespace EhLib
{

  public enum DataGridColumnTitleFilterExpressionLogRelation
  {
    And, Or, Non
  }

  public enum DataGridColumnTitleFilterExpressionOperator
  {
    Non,
    Equal, NotEqual,
    GreaterThan, LessThan, GreaterOrEqual, LessOrEqual,
    Like, NotLike,
    BeginWith, NotBeginWith, EndWith, NotEndWith, Contain, NotContain,
    In, NotIn,
    IsEmpty, IsNotEmpty,
    And, Or,
    Value,
    EqualToNull, NotEqualToNull
  }

  [TypeConverter(typeof(ExpandableObjectConverter))]
  public class DataGridTitleFilter : DataGridObject, IDisposable
  {

    #region privates
    private bool active;
    DataGridFilterDropDownForm dropDownForm;
    Collection<DataGridFilterFormListRecord> filterList;
    private bool disposed;
    #endregion privates

    #region constructor
    public DataGridTitleFilter(DataGridEh grid) : base(grid)
    {
    }

    ~DataGridTitleFilter()
    {
      Dispose(false);
    }

    public void Dispose()
    {
      Dispose(true);
      GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
      if (disposed)
        return;
      if (disposing)
      {
        if (dropDownForm != null)
          dropDownForm.Dispose();
      }
      disposed = true;
    }
    #endregion constructor

    #region design-time properties
    [DefaultValue(false)]
    public bool Active
    {
      get
      {
        return active;
      }
      set
      {
        if (active != value)
        {
          active = value;
          Grid.Invalidate();
        }
      }
    }
    #endregion  design-time properties

    #region run-time properties
    [Browsable(false)]
    public DataGridColumn FilterDropDownFormColumn { get; internal set; }
    #endregion  run-time properties

    #region internal methods
    public virtual void ApplyFilter()
    {
      Grid.RebuildVisibleRows(false, false, true);
      Grid.CurrentRowIndex = 0;
    }

    internal void ShowFilterDropDownForm(DataGridColumn column, Rectangle cellRect, Rectangle screenFilterBtnRect)
    {
      if (dropDownForm == null)
        dropDownForm = new DataGridFilterDropDownForm();

      if (FilterDropDownFormColumn != null)
      {
        DataGridColumn oldFilterColumn = FilterDropDownFormColumn;
        dropDownForm.Close(DialogResult.Cancel);
        if (oldFilterColumn == column)
          return;
      }

      FilterDropDownFormColumn = column;
      Collection<KeyDisplayValue> filterStrings = column.GetUniqueStringListValues();

      filterList = DataGridFilterFormListRecord.GetListFromStringList(filterStrings);
      SetSelecteItems(column, filterList);

      dropDownForm.InitData(filterList, column.Title.Filter, screenFilterBtnRect);

      if (Grid.UseRightToLeft)
        cellRect = EhLibUtils.RightToLeftFlipRectangle(Grid.ClientRectangle, cellRect);

      dropDownForm.ExecuteNomodal(Grid.RectangleToScreen(cellRect), DropDownAlign.Left, FilterClosed, Grid);
    }

    private void FilterClosed(object sender, DropDownFormCloseEventArgs e)
    {
      List<object> inList = new List<object>();
      DataGridColumnTitleFilter colFilter = FilterDropDownFormColumn.Title.Filter;
      colFilter.DropDownFormSize = dropDownForm.Size;

      if (e.CloseResult == DialogResult.OK)
      {
        foreach (DataGridFilterFormListRecord record in filterList)
        {
          if (record.RowSelected == true)
            inList.Add(record.KeyValue);
        }

        colFilter.Expression.Clear();
        if (inList.Count > 0)
        {
          colFilter.Expression.Operator1 = DataGridColumnTitleFilterExpressionOperator.In;
          colFilter.Expression.Operand1 = inList;
        }
        Grid.Title.Filter.ApplyFilter();
      }

      FilterDropDownFormColumn = null;
    }

    private void SetSelecteItems(DataGridColumn column, Collection<DataGridFilterFormListRecord> filterList)
    {
      DataGridColumnTitleFilterExpression colExpr = column.Title.Filter.Expression;
      if (colExpr.Relation == DataGridColumnTitleFilterExpressionLogRelation.Non)
      {
        if (colExpr.Operator1 == DataGridColumnTitleFilterExpressionOperator.Equal)
        {
          foreach (DataGridFilterFormListRecord flRec in filterList)
          {
            if (EhLibUtils.DBValueEqual(flRec.KeyValue, colExpr.Operand1))
            {
              flRec.RowSelected = true;
              break;
            }
          }
        }
        else if (colExpr.Operator1 == DataGridColumnTitleFilterExpressionOperator.In)
        {
          List<object> inList = (List<object>)colExpr.Operand1;
          foreach (object lval in inList)
          {
            foreach (DataGridFilterFormListRecord flRec in filterList)
            {
              if (EhLibUtils.DBValueEqual(flRec.KeyValue, lval))
              {
                flRec.RowSelected = true;
                break;
              }
            }
          }
        }
      }
    }

    internal bool CheckRowVisible(DataGridRow row)
    {
      if (!Active) return true;

      Collection<DataGridColumn> fList = GetFilterList(null);
      if (fList== null) return true;

      bool result = RowMatchColumnsFilter(row, fList);
      return result;
    }

    internal bool RowMatchColumnsFilter(DataGridRow row, Collection<DataGridColumn> fList)
    {
      //bool result = true;
      object cellVal;

      if (fList == null) return true;

      foreach (DataGridColumn col in fList)
      {
        cellVal = col.GetRowValue(row);
        bool colResult = col.Title.Filter.Expression.ValueMatchExpression(cellVal);
        if (colResult == false)
          return false;
      }
      return true;
    }

    public Collection<DataGridColumn> GetFilterList(DataGridColumn excludeColumn)
    {
      Collection<DataGridColumn> result = null;

      foreach (DataGridColumn col in Grid.Columns)
      {
        if ((col != excludeColumn) && 
            (!col.Title.Filter.Expression.IsEmpty()))
        {
          if (result == null)
            result = new Collection<DataGridColumn>();
          result.Add(col);
        }
      }

      return result;
    }

    public void Clear()
    {
      foreach (DataGridColumn col in Grid.Columns)
        col.Title.Filter.Clear();
    }

    #endregion internal methods

    #region public methods
    #endregion public methods
  }

  public class DataGridFilterService
  {
    public DataGridFilterService()
    {
    }

    public virtual void ApplyFilter(DataGridEh grid)
    {

    }
  }

  [TypeConverter(typeof(ExpandableObjectConverter))]
  public class DataGridColumnTitleFilter
  {
    #region Privates
    private readonly DataGridColumnTitle columnTitle;
    private readonly DataGridColumnTitleFilterExpression expression;
    private bool visible = true;
    private Size dropDownFormSize = Size.Empty;
    #endregion Privates

    public DataGridColumnTitleFilter(DataGridColumnTitle columnTitle)
    {
      this.columnTitle = columnTitle;
      this.expression = new DataGridColumnTitleFilterExpression();
    }

    #region design-time properties
    [DefaultValue(true)]
    public bool Visible
    {
      get
      {
        return visible;
      }
      set
      {
        if (value != visible)
        {
          visible = value;
          columnTitle.TitleFilterChanged();
        }
      }
    }
    #endregion  design-time properties

    #region public properties
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [Browsable(false)]
    public DataGridColumnTitle ColumnTitle
    {
      get { return columnTitle; }
    }

    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [Browsable(false)]
    public DataGridColumnTitleFilterExpression Expression
    {
      get { return expression; }
    }

    [Browsable(false)]
    public bool IsShown
    {
      get
      {
        if (ColumnTitle.Column.Grid.Title.Filter.Active &&
            Visible
        )
          return true;
        else
          return false;
      }
    }

    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [Browsable(false)]
    public Size DropDownFormSize
    {
      get { return dropDownFormSize; }
      set { dropDownFormSize = value; }
    }
    #endregion  public properties

    #region public methods
    internal void Clear()
    {
      Expression.Clear();
    }

    internal bool ShowCustomFilterDialog()
    {
      return DataGridSimpleFilterDialog.StartDBGridEhColumnFilterDialog(ColumnTitle.Column);
    }

    public bool IsFilterOperatorSupported(DataGridColumnTitleFilterExpressionOperator __operator)
    {

      switch (ColumnTitle.Column.TypeCategory)
      {
        case TypeCategory.String:
          switch (__operator)
          {
            case DataGridColumnTitleFilterExpressionOperator.Equal:
            case DataGridColumnTitleFilterExpressionOperator.NotEqual:
            case DataGridColumnTitleFilterExpressionOperator.BeginWith:
            case DataGridColumnTitleFilterExpressionOperator.NotBeginWith:
            case DataGridColumnTitleFilterExpressionOperator.EndWith:
            case DataGridColumnTitleFilterExpressionOperator.NotEndWith:
            case DataGridColumnTitleFilterExpressionOperator.Contain:
            case DataGridColumnTitleFilterExpressionOperator.NotContain:
            case DataGridColumnTitleFilterExpressionOperator.In:
            case DataGridColumnTitleFilterExpressionOperator.NotIn:
            case DataGridColumnTitleFilterExpressionOperator.IsEmpty:
            case DataGridColumnTitleFilterExpressionOperator.IsNotEmpty:
              return true;
            default:
              return false;
          }
        case TypeCategory.Number:
        case TypeCategory.DateTime:
          switch (__operator)
          {
            case DataGridColumnTitleFilterExpressionOperator.Equal:
            case DataGridColumnTitleFilterExpressionOperator.NotEqual:
            case DataGridColumnTitleFilterExpressionOperator.GreaterThan:
            case DataGridColumnTitleFilterExpressionOperator.LessThan:
            case DataGridColumnTitleFilterExpressionOperator.GreaterOrEqual:
            case DataGridColumnTitleFilterExpressionOperator.LessOrEqual:
            case DataGridColumnTitleFilterExpressionOperator.In:
            case DataGridColumnTitleFilterExpressionOperator.NotIn:
            case DataGridColumnTitleFilterExpressionOperator.IsEmpty:
            case DataGridColumnTitleFilterExpressionOperator.IsNotEmpty:
              return true;
            default:
              return false;
          }
        case TypeCategory.Boolean:
          switch (__operator)
          {
            case DataGridColumnTitleFilterExpressionOperator.Equal:
            case DataGridColumnTitleFilterExpressionOperator.NotEqual:
            case DataGridColumnTitleFilterExpressionOperator.IsEmpty:
            case DataGridColumnTitleFilterExpressionOperator.IsNotEmpty:
              return true;
            default:
              return false;
          }
        case TypeCategory.Binary:
        case TypeCategory.BinaryImage:
          switch (__operator)
          {
            case DataGridColumnTitleFilterExpressionOperator.IsEmpty:
            case DataGridColumnTitleFilterExpressionOperator.IsNotEmpty:
              return true;
            default:
              return false;
          }
        case TypeCategory.Unknown:
          switch (__operator)
          {
            case DataGridColumnTitleFilterExpressionOperator.Equal:
            case DataGridColumnTitleFilterExpressionOperator.NotEqual:
            case DataGridColumnTitleFilterExpressionOperator.IsEmpty:
            case DataGridColumnTitleFilterExpressionOperator.IsNotEmpty:
              return true;
            default:
              return false;
          }
      }
      return false;
    }
    #endregion public methods
  }

  public class DataGridColumnTitleFilterExpression
  {
    public object Operand1 { get; set; }
    public object Operand2 { get; set; }

    public DataGridColumnTitleFilterExpressionOperator Operator1 { get; set; }
    public DataGridColumnTitleFilterExpressionOperator Operator2 { get; set; }
    public DataGridColumnTitleFilterExpressionLogRelation Relation { get; set; } // AND, OR, Non

    public bool IsEmpty()
    {
      if ((Operator1 == DataGridColumnTitleFilterExpressionOperator.Non) &&
          (Operator1 == DataGridColumnTitleFilterExpressionOperator.Non))
        return true;
      else
        return false;
    }

    public void Clear()
    {
      Operand1 = null;
      Operand2 = null;
      Operator1 = DataGridColumnTitleFilterExpressionOperator.Non;
      Operator2 = DataGridColumnTitleFilterExpressionOperator.Non;
      Relation = DataGridColumnTitleFilterExpressionLogRelation.Non;
    }

    public bool ValueMatchExpression(object value)
    {
      bool res2;
      bool res1;

      Debug.Assert(!IsEmpty(), "Can't check Empty Expression");
      res1 = ValueMatchOperatorOperand(Operator1, Operand1, value);

      if (Relation == DataGridColumnTitleFilterExpressionLogRelation.Non)
      {
        return res1;
      }
      else
      {
        res2 = ValueMatchOperatorOperand(Operator2, Operand2, value);
        if (Relation == DataGridColumnTitleFilterExpressionLogRelation.And)
          return res1 && res2;
        else //Or
          return res1 || res2;
      }
    }

    protected bool ValueMatchOperatorOperand(DataGridColumnTitleFilterExpressionOperator oprtr, 
      object oprnd, object value)
    {
      bool result = true;
      switch (oprtr)
      {
        case DataGridColumnTitleFilterExpressionOperator.Non:
          Debug.Fail("operator = Non");
          break;

        case DataGridColumnTitleFilterExpressionOperator.Equal:
          result = EhLibUtils.DBValueEqual(value, oprnd);
          break;
        case DataGridColumnTitleFilterExpressionOperator.NotEqual:
          result = !EhLibUtils.DBValueEqual(value, oprnd);
          break;

        case DataGridColumnTitleFilterExpressionOperator.GreaterThan:
          result = EhLibUtils.DBCompareValues(value, oprnd) > 0;
          break;
        case DataGridColumnTitleFilterExpressionOperator.LessThan:
          result = EhLibUtils.DBCompareValues(value, oprnd) < 0;
          break;
        case DataGridColumnTitleFilterExpressionOperator.GreaterOrEqual:
          result = EhLibUtils.DBCompareValues(value, oprnd) >= 0;
          break;
        case DataGridColumnTitleFilterExpressionOperator.LessOrEqual:
          result = EhLibUtils.DBCompareValues(value, oprnd) <= 0;
          break;

        case DataGridColumnTitleFilterExpressionOperator.Like:
          Debug.Fail("operator 'Like' is not supported");
          break;
        case DataGridColumnTitleFilterExpressionOperator.NotLike:
          Debug.Fail("operator 'not Like' is not supported");
          break;

        case DataGridColumnTitleFilterExpressionOperator.BeginWith:
        case DataGridColumnTitleFilterExpressionOperator.NotBeginWith:
        case DataGridColumnTitleFilterExpressionOperator.EndWith:
        case DataGridColumnTitleFilterExpressionOperator.NotEndWith:
        case DataGridColumnTitleFilterExpressionOperator.Contain:
        case DataGridColumnTitleFilterExpressionOperator.NotContain:

          if (!EhLibUtils.DBValueEqual(oprnd, null) &&
              !EhLibUtils.DBValueEqual(value, null))
          {
            string valueAsStr = value.ToString();
            string oprndAsStr = oprnd.ToString();

            switch (oprtr)
            {

              case DataGridColumnTitleFilterExpressionOperator.BeginWith:
              case DataGridColumnTitleFilterExpressionOperator.NotBeginWith:
                  result = valueAsStr.StartsWith(oprndAsStr, true, CultureInfo.CurrentCulture);
                  if (oprtr == DataGridColumnTitleFilterExpressionOperator.NotBeginWith)
                    result = !result;
                break;
              case DataGridColumnTitleFilterExpressionOperator.EndWith:
              case DataGridColumnTitleFilterExpressionOperator.NotEndWith:
                  result = valueAsStr.EndsWith(oprndAsStr, true, CultureInfo.CurrentCulture);
                  if (oprtr == DataGridColumnTitleFilterExpressionOperator.NotEndWith)
                    result = !result;
                break;
              case DataGridColumnTitleFilterExpressionOperator.Contain:
                result = valueAsStr.IndexOf(oprndAsStr, StringComparison.OrdinalIgnoreCase) >= 0;
                break;
              case DataGridColumnTitleFilterExpressionOperator.NotContain:
                result = valueAsStr.IndexOf(oprndAsStr, StringComparison.OrdinalIgnoreCase) < 0;
                break;
            }

          }
          else
          {
            result = false;
          }
          break;
        case DataGridColumnTitleFilterExpressionOperator.In:
          result = false;
          List<object> inList = (List<object>)oprnd;
          foreach(object lval in inList)
          {
            if (EhLibUtils.DBValueEqual(lval, value))
            {
              result = true;
              break;
            }
          }
          break;
        case DataGridColumnTitleFilterExpressionOperator.NotIn:
          result = !ValueMatchOperatorOperand(DataGridColumnTitleFilterExpressionOperator.In, oprnd, value);
          break;

        case DataGridColumnTitleFilterExpressionOperator.IsEmpty:
          result = EhLibUtils.DBValueEqual(value, null);
          break;
        case DataGridColumnTitleFilterExpressionOperator.IsNotEmpty:
          result = !EhLibUtils.DBValueEqual(value, null);
          break;

        case DataGridColumnTitleFilterExpressionOperator.And:
          Debug.Fail("operator = AND");
          break;
        case DataGridColumnTitleFilterExpressionOperator.Or:
          Debug.Fail("operator = OR");
          break;
        case DataGridColumnTitleFilterExpressionOperator.Value:
          Debug.Fail("operator = Value");
          break;
        case DataGridColumnTitleFilterExpressionOperator.EqualToNull:
          Debug.Fail("operator '= Null' is not supported");
          break;
        case DataGridColumnTitleFilterExpressionOperator.NotEqualToNull:
          Debug.Fail("operator '!= Null' is not supported");
          break;
      }
      return result;
    }
  }
}
