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

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

namespace EhLib
{

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

    #region privates
    private string mask;
    private char passwordChar;
    private char promptChar;
    private MaskFormat textMaskFormat;
    private InsertKeyMode insertKeyMode;

    private MaskedTextProvider maskedTextBuiler;
    private MaskFormat cutCopyMaskFormat;
    private bool allowPromptAsInput;
    private bool beepOnError;
    private Type validatingType;
    private bool useSystemPasswordChar;
    private bool skipLiterals;
    private bool resetOnPrompt;
    private bool resetOnSpace;
    private bool rejectInputOnFirstFailure;
    private CultureInfo culture;
    #endregion

    public DataAxisGridMaskedTextDataCell()
    {
      mask = "";
      passwordChar = '\0';
      useSystemPasswordChar = false;
      promptChar = '_';
      textMaskFormat = MaskFormat.IncludeLiterals;
      insertKeyMode = InsertKeyMode.Default;
      cutCopyMaskFormat = MaskFormat.IncludeLiterals;
      allowPromptAsInput = true;
      beepOnError = false;
      validatingType = null;
      skipLiterals = true;
      resetOnPrompt = true;
      resetOnSpace = true;
      rejectInputOnFirstFailure = false;
    }

    #region design-time properties
    public override Type EditType
    {
      get
      {
        return typeof(DataAxisGridMaskedTextBoxEditControl);
      }
    }

    [
    RefreshProperties(RefreshProperties.Repaint),
    DefaultValue(""),
    MergableProperty(false),
    Localizable(true),
    Editor("EhLib.Design.MaskPropertyEditor", typeof(UITypeEditor))
    ]
    public string Mask
    {
      get
      {
        return mask;
      }
      set
      {
        if (value != mask)
        {
          mask = value;
          MaskChanged();
        }
      }
    }

    [
    RefreshProperties(RefreshProperties.Repaint),
    DefaultValue('\0') // This property is shadowed by MaskedTextBoxDesigner.
    ]
    public char PasswordChar
    {
      get
      {
        return passwordChar;
      }
      set
      {
        if (value != passwordChar)
        {
          passwordChar = value;
          PasswordCharChanged();
        }
      }
    }

    [DefaultValue(false)]
    [RefreshProperties(RefreshProperties.Repaint)]
    public new bool UseSystemPasswordChar
    {
      get { return useSystemPasswordChar; }
      set { useSystemPasswordChar = value; }
    }

    [
    RefreshProperties(RefreshProperties.Repaint),
    Localizable(true),
    DefaultValue('_')
    ]
    public char PromptChar
    {
      get
      {
        return promptChar;
      }
      set
      {
        if (value != promptChar)
        {
          promptChar = value;
          PromptCharChanged();
        }
      }
    }

    [
    RefreshProperties(RefreshProperties.Repaint),
    DefaultValue(MaskFormat.IncludeLiterals)
    ]
    public MaskFormat TextMaskFormat
    {
      get
      {
        return textMaskFormat;
      }
      set
      {
        if (value != textMaskFormat)
        {
          textMaskFormat = value;
          TextMaskFormatChanged();
        }
      }
    }

    [
    DefaultValue(InsertKeyMode.Default)
    ]
    public InsertKeyMode InsertKeyMode
    {
      get
      {
        return insertKeyMode;
      }
      set
      {
        insertKeyMode = value;
      }
    }

    [
    RefreshProperties(RefreshProperties.Repaint),
    DefaultValue(MaskFormat.IncludeLiterals)
    ]
    public MaskFormat CutCopyMaskFormat
    {
      get { return cutCopyMaskFormat; }
      set { cutCopyMaskFormat = value; }
    }

    [
    DefaultValue(true)
    ]
    public bool AllowPromptAsInput
    {
      get { return allowPromptAsInput; }
      set { allowPromptAsInput = value; }
    }

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

    [
    DefaultValue(true)
    ]
    public bool SkipLiterals
    {
      get { return skipLiterals; }
      set { skipLiterals = value; }
    }

    [
    DefaultValue(true)
    ]
    public bool ResetOnPrompt
    {
      get { return resetOnPrompt; }
      set { resetOnPrompt = value; }
    }

    [
    DefaultValue(true)
    ]
    public bool ResetOnSpace
    {
      get { return resetOnSpace; }
      set { resetOnSpace = value; }
    }

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

    [
    RefreshProperties(RefreshProperties.Repaint),
    ]
    public CultureInfo Culture
    {
      get { return culture; }
      set { culture = value; }
    }
    #endregion

    #region run-time properties
    [
    Browsable(false),
    DefaultValue(null)
    ]
    public Type ValidatingType
    {
      get { return validatingType; }
      set { validatingType = value; }
    }
    #endregion

    #region methods
    public override string GetDisplayText(PropertyAxisBar propAxisBar, object listItemBar)
    {
      string baseResult = base.GetDisplayText(propAxisBar, listItemBar);

      if (String.IsNullOrEmpty(Mask))
        return baseResult;

      if (maskedTextBuiler == null || !String.Equals(maskedTextBuiler.Mask, Mask))
        maskedTextBuiler = new MaskedTextProvider(Mask);
      maskedTextBuiler.PasswordChar = PasswordChar;
      maskedTextBuiler.PromptChar = PromptChar;
      maskedTextBuiler.IncludePrompt = false;
      //TextMaskFormat == MaskFormat.IncludePromptAndLiterals || TextMaskFormat == MaskFormat.IncludePrompt;
      maskedTextBuiler.IncludeLiterals = true;
        //TextMaskFormat == MaskFormat.IncludePromptAndLiterals || TextMaskFormat == MaskFormat.IncludeLiterals;

      if (!maskedTextBuiler.Set(baseResult))
        maskedTextBuiler.Set("");

      string result = maskedTextBuiler.ToString();
      return result;
    }

    protected virtual void MaskChanged()
    {
      if (Grid != null)
        Grid.Invalidate();
    }

    protected virtual void PasswordCharChanged()
    {
      if (Grid != null)
        Grid.Invalidate();
    }

    protected virtual void PromptCharChanged()
    {
      if (Grid != null)
        Grid.Invalidate();
    }

    protected virtual void TextMaskFormatChanged()
    {
      if (Grid != null)
        Grid.Invalidate();
    }

    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 = ForeColor;

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

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

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

        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 = false;

      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;
      //DataGridColumn column = Grid.VisibleColumns[dataColIndex];

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

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

      textRect = EhLibUtils.TrimPadding(cellRect, Padding);

      editor.EditorEditValue = GetDisplayText(areaColIndex, areaRowIndex);
      editor.Font = Font;
      editor.ReadOnly = !CanModify(areaColIndex, areaRowIndex);
      editor.PrepareEditorForEdit(selectAll);
      editor.Padding = Padding;
      editor.TextAlign = HorzAlign;
      editor.Mask = Mask;
      editor.PasswordChar = PasswordChar;
      editor.PromptChar = PromptChar;
      editor.TextMaskFormat = TextMaskFormat;
      editor.InsertKeyMode = InsertKeyMode;
      editor.TextAlign = HorzAlign;
    }

    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);
      //sf = g.MeasureString(cellText, Grid.Font, new PointF(0, 0), StringFormat.GenericTypographic);

      if (sf.Width > e.CellRect.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);
      }
    }

    protected internal override void OnMouseLeave(BaseGridCellLeaveEventArgs e)
    {
      base.OnMouseLeave(e);
      Grid.HideCellNonFitToolTip();
    }

    protected internal override void OnMouseMove(BaseGridCellMouseEventArgs e)
    {
      base.OnMouseMove(e);
    }

    protected internal override void OnMouseDown(DataAxisGridDataCellMouseEventArgs e)
    {
      base.OnMouseDown(e);
    }

    protected internal override void OnMouseClick(BaseGridCellMouseEventArgs e)
    {
      base.OnMouseClick(e);
    }

    //private void OnLinkClick(DataGridDataCellEventArgs de)
    //{
    //}

    protected internal override void OnQueryCursor(BaseGridCellQueryCursorEventArgs e)
    {
      base.OnQueryCursor(e);
    }

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

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

    public DataAxisGridMaskedTextBoxEditControl()
    {
      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.ToString().IndexOf("\r\n", end, StringComparison.Ordinal) != -1)
          {
            return true;
          }
          break;

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

        case Keys.Home:
        case Keys.End:
          if (this.SelectionLength != this.ToString().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.ToString().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);
    }
  }

}
