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

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

namespace EhLib
{

  [DataAxisGridDataCellDesignTimeVisibleAttribute(true)]
  public class DataAxisGridDateTimeDataCell : DataAxisGridDataCellMan
  {

    #region privates
    private Rectangle inCellLinkBounds;
    private GridCoord linkCellIndex = new GridCoord(-1, -1);

    private string dateFormatString = "d";
    private string timeFormatString = "t";
    private bool hideTimeWhenZero = true;
    private string dateTimePartSeparator = " ";
    #endregion

    public DataAxisGridDateTimeDataCell()
    {
    }

    #region properties
    [DefaultValue("d")]
    public string DateFormatString
    {
      get
      {
        return dateFormatString;
      }
      set
      {
        if (value != dateFormatString)
        {
          dateFormatString = value;
          if (Grid != null)
            Grid.Invalidate();
        }
      }
    }

    [DefaultValue("t")]
    public string TimeFormatString
    {
      get
      {
        return timeFormatString;
      }
      set
      {
        if (value != timeFormatString)
        {
          timeFormatString = value;
          if (Grid != null)
            Grid.Invalidate();
        }
      }
    }

    [DefaultValue(true)]
    public bool HideTimeWhenZero
    {
      get
      {
        return hideTimeWhenZero;
      }
      set
      {
        if (value != hideTimeWhenZero)
        {
          hideTimeWhenZero = value;
          if (Grid != null)
            Grid.Invalidate();
        }
      }
    }

    [DefaultValue(" ")]
    public string DateTimePartSeparator
    {
      get
      {
        return dateTimePartSeparator;
      }
      set
      {
        if (value != dateTimePartSeparator)
        {
          dateTimePartSeparator = value;
          if (Grid != null)
            Grid.Invalidate();
        }
      }
    }

    public override Type EditType
    {
      get
      {
        return typeof(DataAxisGridDateTimeBoxEditControl);
      }
    }
    #endregion

    #region methods
    protected override void PaintBackground(DataAxisGridDataCellPaintEventArgs e)
    {
      base.PaintBackground(e);
    }

    protected override void PaintContent(DataAxisGridDataCellContentPaintEventArgs e)
    {
      Color foreColor;
      Rectangle textRect;
      Font drawFont =  e.DataCellPaintEventArgs.Font;

      if (((BasePaintCellStates.Current & e.State) != 0) ||
          ((BasePaintCellStates.RowSelected & e.State) != 0)
      )
        foreColor = SystemColors.HighlightText;
      else
        foreColor = ForeColor;

      string text = GetDisplayText(e.DataColIndex, e.DataRowIndex);
      if (text != null)
      {
        textRect = EhLibUtils.TrimPadding(e.CellContentRect, Padding);

        if (linkCellIndex.X == e.DataCellPaintEventArgs.ColIndex &&
            linkCellIndex.Y == e.DataCellPaintEventArgs.RowIndex)
        {
          drawFont = new Font(Font, FontStyle.Underline);
        }

        Grid.PaintingDrawText(e.Graphics, text, drawFont, textRect, foreColor, HorzAlign, VertAlign, false);
      }

      CharacterRange[] charRanges;
      string searchingText = Grid.GetHighlightSearchingData(e.PropAxisBar, e.ListItemBar, text, out charRanges);
      if (!String.IsNullOrEmpty(searchingText))
      { 
        PaintHighlightedText(e, searchingText, charRanges);
      }
    }

    protected internal virtual void PaintHighlightedText(DataAxisGridDataCellContentPaintEventArgs e, string text, CharacterRange[] charRanges)
    {
      string cellText;
      string hiS;
      Rectangle textRect;
      Region oldClip;
      int outOfBounds = 0;
      Rectangle paintRect = e.CellContentRect;

      cellText = GetDisplayText(e.PropAxisBar, e.ListItemBar);
      hiS = text;
      if (string.IsNullOrEmpty(hiS) == true) return;

      textRect = EhLibUtils.TrimPadding(e.CellContentRect, Padding);

      Rectangle[] stringRegions = EhLibUtils.MeasureCharacterRanges(e.Graphics, cellText, Font,
        textRect, HorzAlign, VertAlign, false, charRanges);
      oldClip = e.Graphics.Clip;
      e.Graphics.SetClip(paintRect, CombineMode.Intersect);

      for (int i = 0; i < stringRegions.Length; i++)
      {
        Rectangle regRect = stringRegions[i];

        if (textRect.IntersectsWith(regRect))
        {
          e.Graphics.FillRectangle(new SolidBrush(Color.Yellow), regRect);

          string drawText = cellText.Substring(charRanges[i].First, charRanges[i].Length);

          EhLibUtils.DrawText(e.Graphics, drawText, Font, regRect, ForeColor, HorizontalAlignment.Left, VerticalAlignment.Top, TextFormatFlagsEh.None);
        }
        else
        {
          outOfBounds = outOfBounds + 1;
        }
      }

      if (outOfBounds > 0)
      {
        string outOfBoundsStr = outOfBounds.ToString();
        Font ofbFont = new Font(Font.FontFamily, 6);
        Size ofbSize = EhLibUtils.MeasureText(e.Graphics, outOfBoundsStr, ofbFont, new Size(1, 1), DataGridTextWrapMode.NoWrap);
        Rectangle ofbRect = new Rectangle(textRect.Right - ofbSize.Width - 2, textRect.Top, ofbSize.Width + 2, ofbSize.Height + 2);

        e.Graphics.FillRectangle(new SolidBrush(Color.Yellow), ofbRect);
        EhLibUtils.DrawText(e.Graphics, outOfBoundsStr, ofbFont, ofbRect, ForeColor, HorizontalAlignment.Center, VerticalAlignment.Center, TextFormatFlagsEh.None);
      }

      e.Graphics.Clip = oldClip;
    }

    protected internal override void PrintCell(PrintServiceEventArgs e,
      Rectangle paintRect, int areaColIndex,
      int areaRowIndex,
      PrintServiceEventArgs printContext
        )
    {
      Font drawFont;
      Rectangle textRect;
      Color fForeColor = Color.Black;

      string text = GetDisplayText(areaColIndex, areaRowIndex);
      if (text != null)
      {
        drawFont = Font;
        TextFormatFlagsEh flags = TextFormatFlagsEh.None;
        textRect = EhLibUtils.TrimPadding(paintRect, Padding);

        e.PrintText(text, drawFont, textRect, fForeColor, HorzAlign, VertAlign, flags);
      }
    }

    public override bool IsCharToEnterEditMode(KeyPressEventArgs e)
    {
      UnicodeCategory uc = char.GetUnicodeCategory(e.KeyChar);
      if (uc != UnicodeCategory.Control)
        return true;

      return base.IsCharToEnterEditMode(e);
    }

    public override void OccupyEditControl(Control cellEditControl, int areaColIndex, int areaRowIndex, bool selectAll)
    {
      DataAxisGridDateTimeBoxEditControl editor = (DataAxisGridDateTimeBoxEditControl)cellEditControl;

      PropertyAxisBar propAxisBar;
      object listItemBar;
      AxisObjectsByDataColRowIndex(areaColIndex, areaRowIndex, out propAxisBar, out listItemBar);

      object val = propAxisBar.GetValue(listItemBar);

      if (val is DateTime)
        editor.EditorEditValue = (DateTime)val;
      else
        editor.EditorEditValue = null;
      editor.Font = Font;
      editor.ReadOnly = !CanModify(areaColIndex, areaRowIndex);
      editor.PrepareEditorForEdit(selectAll);
      editor.Padding = Padding;
      editor.HorzAlign = HorzAlign;
      editor.DateFormatString = DateFormatString;
      editor.TimeFormatString = TimeFormatString;
      editor.DateTimePartSeparator = DateTimePartSeparator;
    }

    public virtual string GetCellNonfitToolTipText(BaseGridCellEventArgs e)
    {
      SizeF sf;
      Graphics g = EhLibUtils.DisplayGraphicsCash;
      string cellText = GetDisplayText(e.AreaColIndex, e.AreaRowIndex);
      Rectangle textRect = e.CellRect;

      textRect = EhLibUtils.TrimPadding(textRect, Padding);
                                                                        
      sf = EhLibUtils.MeasureText(g, cellText, Grid.Font, textRect.Size, DataGridTextWrapMode.NoWrap);

      if (sf.Width > e.CellRect.Width)
        return cellText;
      else
        return "";
    }

    public override string GetValueDisplayText(PropertyAxisBar propAxisBar, object value)
    {
      Type dataType;

      if (propAxisBar != null)
        dataType = propAxisBar.DataType;
      else
        dataType = null;

      if (!EhLibUtils.DBValueEqual(value, null) && dataType.IsAssignableFrom(typeof(DateTime)))
      {
        DateTime dtValue = Convert.ToDateTime(value);
        string result = "";
        if (!String.IsNullOrEmpty(DateFormatString))
          result = dtValue.ToString(DateFormatString);
        if (!String.IsNullOrEmpty(DateFormatString) && 
            (!HideTimeWhenZero || (HideTimeWhenZero && dtValue.TimeOfDay.Ticks != 0))
           )
        { 
          result = result + DateTimePartSeparator;
          result = result + dtValue.ToString(TimeFormatString);
        }
        return result;
      }
      else
      {
        return base.GetValueDisplayText(propAxisBar, value);
      }
    }

    protected internal override void OnMouseEnter(BaseGridCellEnterEventArgs e)
    {

      base.OnMouseEnter(e);

      string cellText = GetCellNonfitToolTipText(e);

      if (!string.IsNullOrEmpty(cellText))
      {
        Grid.ShowCellNonFitToolTip(cellText, e);
      }

      PropertyAxisBar propAxisBar;
      object listItemBar;
      AxisObjectsByDataColRowIndex(e.AreaColIndex, e.AreaRowIndex, out propAxisBar, out listItemBar);
    }

    protected internal override void OnMouseLeave(BaseGridCellLeaveEventArgs e)
    {
      base.OnMouseLeave(e);
      Grid.HideCellNonFitToolTip();
      inCellLinkBounds = Rectangle.Empty;
      if (linkCellIndex.X >= 0 || linkCellIndex.Y >= 0)
      {
        linkCellIndex = new GridCoord(-1, -1);
        Grid.InvalidateRow(e.RowIndex);
      }
    }

    protected internal override void OnMouseMove(BaseGridCellMouseEventArgs e)
    {
      base.OnMouseMove(e);
      Point inCellPos = new Point(e.InCellX, e.InCellY);

      bool oldMouseInCellLinkBounds = (linkCellIndex.X >= 0 || linkCellIndex.Y >= 0);
      bool newMouseInCellLinkBounds = inCellLinkBounds.Contains(inCellPos);

      if (oldMouseInCellLinkBounds != newMouseInCellLinkBounds)
      {
        if (newMouseInCellLinkBounds)
          linkCellIndex = new GridCoord(e.ColIndex, e.RowIndex);
        else
          linkCellIndex = new GridCoord(-1, -1);
        Grid.InvalidateRow(e.RowIndex);
      }
    }

    protected internal override void OnMouseDown(DataAxisGridDataCellMouseEventArgs e)
    {
      if (linkCellIndex == new GridCoord(e.ColIndex, e.RowIndex) && !Grid.EditorBanned)
      {
        Grid.EditorBanned = true;
        base.OnMouseDown(e);
        Grid.EditorBanned = false;
      }
      else
        base.OnMouseDown(e);
    }

    protected internal override void OnMouseClick(BaseGridCellMouseEventArgs e)
    {
      base.OnMouseClick(e);
      if (linkCellIndex == new GridCoord(e.ColIndex, e.RowIndex))
      {
        PropertyAxisBar propAxisBar;
        object listItemBar;
        AxisObjectsByDataColRowIndex(e.AreaColIndex, e.AreaRowIndex, out propAxisBar, out listItemBar);

        DataAxisGridDataCellEventArgs de = propAxisBar.CreateDataCellEventArgs(e.ColIndex, e.RowIndex,
          e.AreaColIndex, e.AreaRowIndex, e.CellRect, propAxisBar, listItemBar, this);
        OnContentClick(de);
      }
    }

    protected internal override void OnQueryCursor(BaseGridCellQueryCursorEventArgs e)
    {
      base.OnQueryCursor(e);
      if (linkCellIndex.X == e.ColIndex && linkCellIndex.Y == e.RowIndex)
        e.Cursor = Cursors.Hand;
    }

    public virtual int GetOneLineHeight()
    {
      return CalcDefaultRowHeight();
    }

    protected internal override int CalcCellHeight(PropertyAxisBar propAxisBar, object listItemBar, int cellWidth)
    {
      int th, result;
      Size outSize;

      string text = GetDisplayText(propAxisBar, listItemBar);

      result = base.CalcCellHeight(propAxisBar, listItemBar, cellWidth);

      if (HeightOptions.AutoExpand && HeightOptions.Unit == GridRowHeightUnit.TextLine)
      {
        outSize = EhLibUtils.MeasureText(EhLibUtils.DisplayGraphicsCash, text, Font);

        th = outSize.Height + Padding.Top + Padding.Bottom;
        if (th > result)
          result = th;
      }

      return result;
    }

    protected override bool EditButtonDefaultVisible()
    {
      return true;
    }
    #endregion
  }

  [ToolboxItem(false)]
  [System.ComponentModel.DesignerCategory("Code")]
  public class DataAxisGridDateTimeBoxEditControl : DateTimeBoxEh, IDataAxisGridCellInPlaceEditor
  {
    private DataAxisGrid ownerGrid;
    private int rowIndex;
    private bool valueChanged;

    public DataAxisGridDateTimeBoxEditControl()
    {
      this.TabStop = false;
      this.Border.Style = ControlBorderStyle.None;
      InitData();
    }

    private void InitData()
    {
      this.AutoSize = false;
    }

    public object EditorEditValue
    {
      get { return Value; }
      set
      {
        this.Value = (value as Nullable<DateTime>);
        this.valueChanged = false;
      }
    }

    public DataAxisGrid EditorOwnerGrid
    {
      get { return this.ownerGrid; }
      set { this.ownerGrid = value; }
    }

    public int EditorTableRowIndex
    {
      get { return this.rowIndex; }
      set { this.rowIndex = value; }
    }

    public bool EditorValueChanged
    {
      get
      {
        Validate();
        return this.valueChanged;
      }
      set
      {
        this.valueChanged = value;
      }
    }

    public bool EditorWantsInputKey(Keys keyData, bool dataGridWantsInputKey)
    {
      switch (keyData & Keys.KeyCode)
      {
        case Keys.Right:
            return true;

        case Keys.Left:
            return true;

        case Keys.Down:
            return true;

        case Keys.Up:
            return true;

        case Keys.Home:
        case Keys.End:
            return true;

        case Keys.Prior:
        case Keys.Next:
            return true;

        case Keys.Delete:
            return true;
      }
      return !dataGridWantsInputKey;
    }

    public void PrepareEditorForEdit(bool selectAll)
    {
      if (selectAll)
        SelectAll();
      else
        SelectedPatternItemIndex = 0;
    }

    protected override void OnValueChanged(EventArgs e)
    {
      base.OnTextChanged(e);
      this.valueChanged = true;
      ownerGrid.EditorTextChanged(e);
    }

    protected override void OnEditItemDisplayTextChagned(EventArgs e)
    {
      base.OnEditItemDisplayTextChagned(e);
      if (Focused)
      {
        this.valueChanged = true;
        ownerGrid.EditorTextChanged(e);
      }
    }

  }

}
