//------------------------------------------------------------------------------
// <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 DataAxisGridTextDataCell : DataAxisGridDataCellMan
  {

    #region privates
    private bool cellDataIsLink;
    private DataGridTextWrapMode wrapMode;
    private Rectangle inCellLinkBounds;
    private GridCoord linkCellIndex = new GridCoord(-1, -1);
    #endregion

    public DataAxisGridTextDataCell()
    {
    }

    #region properties
    public override Type EditType
    {
      get
      {
        return typeof(DataAxisGridTextBoxEditControl);
      }
    }

    [DefaultValue(DataGridTextWrapMode.Auto)]
    public DataGridTextWrapMode WrapMode
    {
      get
      {
        return wrapMode;
      }
      set
      {
        if (wrapMode != value)
        {
          wrapMode = value;
          WrapModeChanged();
        }
      }
    }

    [DefaultValue(false)]
    public bool CellDataIsLink
    {
      get { return cellDataIsLink; }
      set { cellDataIsLink = value; }
    }

    #endregion

    #region methods
    private void WrapModeChanged()
    {
      if (Grid != null)
        Grid.RowHeightAffectPropertyChanged();
    }

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

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

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

      string text = GetDisplayText(e.DataColIndex, e.DataRowIndex);
      if (text != null)
      {

        //TextFormatFlagsEh flags = TextFormatFlagsEh.Default;
        textRect = EhLibUtils.TrimPadding(e.CellContentRect, Padding);

        wordWrap = CheckWordWrap(textRect);
        //if (wordWrap)
        //  flags = flags | TextFormatFlagsEh.WordBreak;

        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, wordWrap);

      }

      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;
      bool wordWrap;
      Region oldClip;
      int outOfBounds = 0;
      //CharacterRange[] charRanges;
      Rectangle paintRect = e.CellContentRect;

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

      //if (!Grid.SearchBox.CheckTextHit(e.Column, cellText, Grid.SearchBox.SearchingText)) return;

      //charRanges = Grid.SearchBox.GetHitRanges(e.Column, e.Row, cellText);

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

      wordWrap = CheckWordWrap(paintRect);

      Rectangle[] stringRegions = EhLibUtils.MeasureCharacterRanges(e.Graphics, cellText, Font,
        textRect, HorzAlign, VertAlign, wordWrap, 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;
      bool wordWrap;
      //DataGridColumn column = Grid.VisibleColumns[dataColIndex];

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

        wordWrap = CheckWordWrap(textRect);
        if (wordWrap)
          flags = flags | TextFormatFlagsEh.WordBreak;

        e.PrintText(text, drawFont, textRect, fForeColor, HorzAlign, VertAlign, flags);
        //EhLibUtils.DrawText(e.Graphics, text, drawFont, textRect, fForeColor, HorzAlign, VertAlign, flags, UsedGraphicEngine.WinFormsGDIPlus);
      }
    }

    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)
    {
      Rectangle cellRect;
      Rectangle textRect;
      int colIndex, rowIndex;
      bool wordWrap;
      DataAxisGridTextBoxEditControl editor = (DataAxisGridTextBoxEditControl)cellEditControl;

      AreaCellPosToGridCellPos(areaColIndex, areaRowIndex, out colIndex, out rowIndex);
      cellRect = Grid.CellRect(colIndex, rowIndex, true);

      textRect = EhLibUtils.TrimPadding(cellRect, Padding);
      wordWrap = CheckWordWrap(cellRect);

      editor.EditorEditValue = GetDisplayText(areaColIndex, areaRowIndex);
      editor.Font = Font;
      editor.ReadOnly = !CanModify(areaColIndex, areaRowIndex);
      editor.PrepareEditorForEdit(selectAll);
      editor.Padding = Padding;
      editor.TextAlign = HorzAlign;
      editor.WordWrap = wordWrap;
      editor.Multiline = editor.WordWrap;
    }

    public virtual string GetCellNonfitToolTipText(BaseGridCellEventArgs e)
    {
      DataGridTextWrapMode wrapMode;
      SizeF sf;
      Graphics g = EhLibUtils.DisplayGraphicsCash;
      PropertyAxisBar propAxisBar;
      object listItemBar;
      string cellText = GetDisplayText(e.AreaColIndex, e.AreaRowIndex);

      AxisObjectsByDataColRowIndex(e.AreaColIndex, e.AreaRowIndex, out propAxisBar, out listItemBar);
      Rectangle clientRect = GetCellClientRect(propAxisBar, listItemBar, e.CellRect);
      Rectangle textRect = EhLibUtils.TrimPadding(clientRect, Padding);

      bool wordWrap = CheckWordWrap(textRect);
      if (wordWrap)
        wrapMode = DataGridTextWrapMode.WordWrap;
      else
        wrapMode = DataGridTextWrapMode.NoWrap;

      sf = EhLibUtils.MeasureText(g, cellText, Grid.Font, textRect.Size, wrapMode);
      //sf = g.MeasureString(cellText, Grid.Font, new PointF(0, 0), StringFormat.GenericTypographic);

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

    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);

      CalcInCellLinkBounds(propAxisBar, listItemBar, e.CellRect, out this.inCellLinkBounds);
    }

    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);
      }
    }

    //private void OnLinkClick(DataGridDataCellEventArgs 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 bool CheckWordWrap(Rectangle paintRect)
    {
      bool wordWrap = false;
      int oneLineHeight;
      if (WrapMode == DataGridTextWrapMode.Auto)
      {
        oneLineHeight = GetOneLineHeight();
        if (paintRect.Height > oneLineHeight)
          wordWrap = true;
      }
      else if (WrapMode == DataGridTextWrapMode.WordWrap)
      {
        wordWrap = true;
      }
      return wordWrap;
    }

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

      string text = GetDisplayText(propAxisBar, listItemBar);

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

      if (HeightOptions.AutoExpand && HeightOptions.Unit == GridRowHeightUnit.TextLine)
      {
        if ((WrapMode == DataGridTextWrapMode.Auto) ||
            (WrapMode == DataGridTextWrapMode.WordWrap))
        {
          inSize = new Size(cellWidth - Padding.Left - Padding.Right, CalcDefaultRowHeight());
          outSize = EhLibUtils.MeasureText(EhLibUtils.DisplayGraphicsCash, text, Font, inSize, DataGridTextWrapMode.WordWrap);
        }
        else
        {
          outSize = EhLibUtils.MeasureText(EhLibUtils.DisplayGraphicsCash, text, Font);
        }

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

      return result;
    }

    protected virtual void CalcInCellLinkBounds(PropertyAxisBar propAxisBar, object listItemBar,
      Rectangle cellRect, out Rectangle inCellLinkBounds)
    {
      Rectangle contentRect = cellRect;
      Rectangle textRect;
      Size textSize;
      string displayText;

      inCellLinkBounds = Rectangle.Empty;
      if (!CellDataIsLink) return;

      if (EditButtonAccessible() && EditButton.Visible)
      {
        contentRect.Width = contentRect.Width - EditButton.Width;
      }

      contentRect = EhLibUtils.TrimPadding(contentRect, Padding);

      displayText = GetDisplayText(propAxisBar, listItemBar);
      if (string.IsNullOrEmpty(displayText)) return;

      textSize = EhLibUtils.MeasureText(EhLibUtils.DisplayGraphicsCash, displayText, Font);
      textRect = new Rectangle(Point.Empty, textSize);

      if (textRect.Width > contentRect.Width)
        textRect.Width = contentRect.Width;
      if (textRect.Height > contentRect.Height)
        textRect.Height = contentRect.Height;

      if (HorzAlign == HorizontalAlignment.Left)
        textRect.X = contentRect.X;
      else if (HorzAlign == HorizontalAlignment.Right)
        textRect.X = contentRect.Right - textRect.Width;
      else // HorizontalAlignment.Center
        textRect.X = contentRect.X + (contentRect.Width + textRect.Width) / 2;

      if (VertAlign == VerticalAlignment.Top)
        textRect.Y = contentRect.Y;
      else if (VertAlign == VerticalAlignment.Bottom)
        textRect.Y = contentRect.Bottom - textRect.Height;
      else // VerticalAlignment.Center
        textRect.Y = contentRect.Y + (contentRect.Height + textRect.Height) / 2;

      inCellLinkBounds = textRect;
      inCellLinkBounds.Offset(-cellRect.X, -cellRect.Y);
    }
    #endregion
  }

  public interface IDataAxisGridCellInPlaceEditor
  {

    DataAxisGrid EditorOwnerGrid
    {
      get;
      set;
    }

    object EditorEditValue
    {
      get;
      set;
    }

    int EditorTableRowIndex
    {
      get;
      set;
    }

    bool EditorValueChanged
    {
      get;
      set;
    }

    bool EditorWantsInputKey(Keys keyData, bool dataGridWantsInputKey);

    void PrepareEditorForEdit(bool selectAll);
  }

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

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

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

    public object EditorEditValue
    {
      get { return Text; }
      set
      {
        this.Text = (string)value;
        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 { return this.valueChanged; }
      set { this.valueChanged = value; }
    }

    public bool EditorWantsInputKey(Keys keyData, bool dataGridWantsInputKey)
    {
      switch (keyData & Keys.KeyCode)
      {
        case Keys.Right:
          if ((this.RightToLeft == RightToLeft.No && !(this.SelectionLength == 0 && this.SelectionStart == this.Text.Length)) ||
              (this.RightToLeft == RightToLeft.Yes && !(this.SelectionLength == 0 && this.SelectionStart == 0)))
          {
            return true;
          }
          break;

        case Keys.Left:
          if ((this.RightToLeft == RightToLeft.No && !(this.SelectionLength == 0 && this.SelectionStart == 0)) ||
              (this.RightToLeft == RightToLeft.Yes && !(this.SelectionLength == 0 && this.SelectionStart == this.Text.Length)))
          {
            return true;
          }
          break;

        case Keys.Down:
          int end = this.SelectionStart + this.SelectionLength;
          if (this.Text.IndexOf("\r\n", end, StringComparison.Ordinal) != -1)
          {
            return true;
          }
          break;

        case Keys.Up:
          if (!(this.Text.IndexOf("\r\n", StringComparison.Ordinal) < 0 ||
                this.SelectionStart + this.SelectionLength < this.Text.IndexOf("\r\n", StringComparison.Ordinal)))
          {
            return true;
          }
          break;

        case Keys.Home:
        case Keys.End:
          if (this.SelectionLength != this.Text.Length)
          {
            return true;
          }
          break;

        case Keys.Prior:
        case Keys.Next:
          if (this.valueChanged)
          {
            return true;
          }
          break;

        case Keys.Delete:
          if (this.SelectionLength > 0 ||
              this.SelectionStart < this.Text.Length)
          {
            return true;
          }
          break;

        case Keys.Enter:
          if ((keyData & (Keys.Control | Keys.Shift | Keys.Alt)) == Keys.Shift && this.Multiline && this.AcceptsReturn)
          {
            return true;
          }
          break;
      }
      return !dataGridWantsInputKey;
    }

    public void PrepareEditorForEdit(bool selectAll)
    {
      if (selectAll)
        SelectAll();
      else
        this.SelectionStart = this.Text.Length;
    }

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

}
