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

using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using System.Windows.Forms.VisualStyles;

namespace EhLib
{
  public interface ICheckBoxDataCellHolder
  {
    CheckState OnGetCheckState(PropertyAxisBar propAxisBar, object listItemBar, out bool handled);
  }

  [DataAxisGridDataCellDesignTimeVisibleAttribute(true)]
  public class DataAxisGridCheckBoxDataCell : DataAxisGridButtonBaseCell
  {

    #region private consts
    private static readonly object EventKeyQueryCheckboxState = new object();
    #endregion private consts

    #region privates
    private static readonly Type CheckStateType = typeof(CheckState);
    private static readonly Type BooleanType = typeof(bool);

    bool threeState;
    object trueValue;
    object falseValue;
    object indeterminateValue;
    private FlatStyle flatStyle;
    #endregion privates

    #region properties
    [DefaultValue(false)]
    public bool ThreeState
    {
      get
      {
        return this.threeState;
      }
      set
      {
        if (this.threeState != value)
        {
          this.threeState = value;
          if (Grid != null)
            Grid.InvalidateGrid();
        }
      }
    }

    [DefaultValue(null)]
    [TypeConverter(typeof(StringConverter))]
    public object TrueValue
    {
      get
      {
        return this.trueValue;
      }
      set
      {
        if (this.trueValue != value)
        {
          this.trueValue = value;
          if (Grid != null)
            Grid.InvalidateGrid();
        }
      }
    }

    [DefaultValue(null)]
    [TypeConverter(typeof(StringConverter))]
    public object FalseValue
    {
      get
      {
        return this.falseValue;
      }
      set
      {
        if (this.falseValue != value)
        {
          this.falseValue = value;
          if (Grid != null)
            Grid.InvalidateGrid();
        }
      }
    }

    [DefaultValue(null)]
    [TypeConverter(typeof(StringConverter))]
    public object IndeterminateValue
    {
      get
      {
        return this.indeterminateValue;
      }
      set
      {
        if (this.indeterminateValue != value)
        {
          this.indeterminateValue = value;
          if (Grid != null)
            Grid.InvalidateGrid();
        }
      }
    }

    [DefaultValue(FlatStyle.Standard)]
    public FlatStyle FlatStyle
    {
      get
      {
        return this.flatStyle;
      }
      set
      {
        if (this.flatStyle != value)
        {
          flatStyle = value;
          if (Grid != null)
            Grid.InvalidateGrid();
        }
      }
    }
    #endregion 

    #region events
    public event EventHandler<DataGridCheckBoxColumnQueryCheckStateEventArgs> QueryCheckState
    {
      add
      {
        this.Events.AddHandler(EventKeyQueryCheckboxState, value);
      }
      remove
      {
        this.Events.RemoveHandler(EventKeyQueryCheckboxState, value);
      }
    }
    #endregion events

    #region Methods
    public virtual CheckState DefaultGetCheckState(PropertyAxisBar propAxisBar, object listItemBar)
    {
      CheckState result;
      if (propAxisBar != null && listItemBar != null)
      {
        object value = propAxisBar.GetValue(listItemBar);
        result = GetCheckStateFromValue(value);
      }
      else
      {
        result = CheckState.Indeterminate;
      }
      return result;
    }

    protected internal virtual CheckState OnGetCheckState(PropertyAxisBar propAxisBar, object listItemBar)
    {
      DataAxisGridCheckBoxQueryDataCellCheckStateEventArgs e = null;
      var eh = this.Events[EventKeyQueryCheckboxState] as EventHandler<DataAxisGridCheckBoxQueryDataCellCheckStateEventArgs>;
      if (eh != null /*&& !this.IsDisposed*/)
      {
        e = new DataAxisGridCheckBoxQueryDataCellCheckStateEventArgs(listItemBar);
        eh(this, e);
      }

      if ((e != null) && e.Handled)
        return e.CheckState;
      else
      {
        bool handled = false;
        CheckState result = CheckState.Indeterminate;

        var holder = PropAxisBar as ICheckBoxDataCellHolder;
        if (holder != null)
          result = holder.OnGetCheckState(propAxisBar, listItemBar, out handled);
        
        if (!handled)
          result = DefaultGetCheckState(propAxisBar, listItemBar);

        return result;
      }
    }

    public CheckBoxState GetCheckBoxState(PropertyAxisBar propAxisBar, object listItemBar)
    {
      CheckBoxState result;

      CheckState chState = OnGetCheckState(propAxisBar, listItemBar);

      if (chState == CheckState.Checked)
        result = CheckBoxState.CheckedNormal;
      else if (chState == CheckState.Unchecked)
        result = CheckBoxState.UncheckedNormal;
      else
        result = CheckBoxState.MixedNormal;

      return result;
    }

    public CheckState GetCheckState(PropertyAxisBar propAxisBar, object listItemBar)
    {
      return OnGetCheckState(propAxisBar, listItemBar);
    }

    public CheckState GetCheckStateFromValue(object value)
    {
      CheckState result;

      if (value == null)
        result = CheckState.Indeterminate;
      else if (value.Equals(true))
        result = CheckState.Checked;
      else if (value.Equals(false))
        result = CheckState.Unchecked;
      else
        result = CheckState.Indeterminate;

      return result;
    }

    public void SetCheckState(PropertyAxisBar propAxisBar, object listItemBar, CheckState checkState)
    {
      object value = GetDataValueFromCheckState(propAxisBar, listItemBar, checkState);
      Grid.SetDataValue(propAxisBar, value);
    }

    public object GetDataValueFromCheckState(PropertyAxisBar propAxisBar, object listItemBar, CheckState checkState)
    {
      object result = null;

      switch (checkState)
      {
        case CheckState.Unchecked:
          {
            if (FalseValue != null)
            {
              result = FalseValue;
            }
            else
            {
              if (propAxisBar.DataType != null && propAxisBar.DataType.IsAssignableFrom(BooleanType))
                result = false;
              else if (propAxisBar.DataType != null && propAxisBar.DataType.IsAssignableFrom(CheckStateType))
                result = CheckState.Unchecked;
            }
            break;
          }
        case CheckState.Checked:
          {
            if (TrueValue != null)
            {
              result = TrueValue;
            }
            else
            {
              if (propAxisBar.DataType != null && propAxisBar.DataType.IsAssignableFrom(BooleanType))
                //EhLibUtils.DoNothing(); 
                result = true;
              else if (propAxisBar.DataType != null && propAxisBar.DataType.IsAssignableFrom(CheckStateType))
                result = CheckState.Checked;
            }
            break;
          }
        case CheckState.Indeterminate:
          {
            if (IndeterminateValue != null)
            {
              result = IndeterminateValue;
            }
            else
            {
              if (propAxisBar.DataType != null && propAxisBar.DataType.IsAssignableFrom(BooleanType))
                //EhLibUtils.DoNothing();
                result = null;
              else if (propAxisBar.DataType != null && propAxisBar.DataType.IsAssignableFrom(CheckStateType))
                result = CheckState.Indeterminate;
            }
            break;
          }
      }
      return result;
    }

    public CheckState NextCheckState(CheckState cs)
    {
      if (ThreeState)
      {
        switch (cs)
        {
          case CheckState.Unchecked:
            {
              return CheckState.Checked;
            }
          case CheckState.Checked:
            {
              return CheckState.Indeterminate;
            }
          case CheckState.Indeterminate:
            {
              return CheckState.Unchecked;
            }
          default:
            {
              return CheckState.Unchecked;
            }
        }
      }
      else
      {
        switch (cs)
        {
          case CheckState.Unchecked:
            {
              return CheckState.Checked;
            }
          case CheckState.Checked:
            {
              return CheckState.Unchecked;
            }
          default:
            {
              return CheckState.Unchecked;
            }
        }
      }
    }

    public override void Toggle(int dataColIndex, int dataRowIndex)
    {
      PropertyAxisBar propAxisBar;
      object listItemBar;
      AxisObjectsByDataColRowIndex(dataColIndex, dataRowIndex, out propAxisBar, out listItemBar);

      CheckState cbState = GetCheckState(propAxisBar, listItemBar);
      cbState = NextCheckState(cbState);
      SetCheckState(propAxisBar, listItemBar, cbState);
    }

    protected override void PaintForeground(DataAxisGridDataCellPaintEventArgs e)
    {
      Rectangle chRect = new Rectangle();
      bool isHot = false;
      bool isDown = false;
      int gridColIndex;
      int gridRowIndex;
      GridCoord mouseHolderCellCoord = Grid.MouseHolderCellCoord;

      CheckBoxState cbState = GetCheckBoxState(e.PropAxisBar, e.ListItemBar);
      Size cbSize = CheckBoxRenderer.GetGlyphSize(e.Graphics, cbState);
      chRect.Size = cbSize;
      chRect = EhLibUtils.RectCenter(chRect, e.CellRect);

      AreaCellPosToGridCellPos(e.AreaColIndex, e.AreaRowIndex, out gridColIndex, out gridRowIndex);

      if (DownState && (Grid.CurrentDataRowIndex == e.AreaRowIndex))
      {
        isDown = true;
      }
      else if (!DownState &&
                mouseHolderCellCoord.X == gridColIndex &&
                mouseHolderCellCoord.Y == gridRowIndex
                )
      {
        isHot = true;
      }

      cbState = EhLibUtilsInternal.FixCheckBoxStateByState(cbState, isHot, isDown, true);

      CheckBoxRenderer.DrawCheckBox(e.Graphics, chRect.Location, cbState);
    }

    #endregion Methods
  }
}
