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

using System;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Design;
using System.Globalization;
using System.Reflection;
using System.Windows.Forms;

namespace EhLib
{
  [DesignerCategory("Code")]
  //[Designer("EhLib.Design.TextBoxEhDesigner" + EhLibUtils.EhLibDesignDesignerVersionInfo)]
  [ToolboxItem(true)]
  [ToolboxBitmap(typeof(TextBoxEh), "ToolboxBitmaps.EhLib_TextBox.bmp")]
  [Description("Enables the user to enter text while uses a mask to distinguish between proper and improper user input")]
  //public class TextBoxEh : ContainerControl
  public class MaskedTextBoxEh : BaseTextBoxEh
  {

    #region constructor
    public MaskedTextBoxEh()
    {
    }
    #endregion

    #region design-time properties
    [DefaultValue(false)]
    public new bool AcceptsTab
    {
      get { return base.AcceptsTab; }
      set { base.AcceptsTab = value; }
    }

    [DefaultValue(true)]
    public new bool HideSelection
    {
      get { return base.HideSelection; }
      set { base.HideSelection = value; }
    }

    /// <summary>
    ///     Specifies whether the prompt character should be treated as a valid input character or not.
    ///     The setter resets the underlying MaskedTextProvider object and attempts
    ///     to add the existing input text (if any) using the new mask, failure is ignored.
    ///     This property has no particular effect if no mask has been set.
    /// </summary>
    [
    DefaultValue(true)
    ]
    public bool AllowPromptAsInput
    {
      get { return TextBoxControl.AllowPromptAsInput; }
      set { TextBoxControl.AllowPromptAsInput = value; }
    }

    /// <summary>
    ///     Specifies whether only ASCII characters are accepted as valid input.
    ///     This property has no particular effect if no mask has been set.
    /// </summary>
    [
    RefreshProperties(RefreshProperties.Repaint),
    DefaultValue(false)
    ]
    public bool AsciiOnly
    {
      get { return TextBoxControl.AsciiOnly; }
      set { TextBoxControl.AsciiOnly = value; }
    }

    /// <summary>
    ///     Specifies whether to play a beep when the input is not valid according to the mask.
    /// </summary>
    [
    DefaultValue(false)
    ]
    public bool BeepOnError
    {
      get { return TextBoxControl.BeepOnError; }
      set { TextBoxControl.BeepOnError = value; }
    }

    /// <summary>
    ///     The culture that determines the value of the localizable mask language separators and placeholders.
    /// </summary>
    [
    RefreshProperties(RefreshProperties.Repaint),
    ]
    public CultureInfo Culture
    {
      get { return TextBoxControl.Culture; }
      set { TextBoxControl.Culture = value; }
    }

    /// <summary>
    ///    Specifies the formatting options for text cut/copited to the clipboard (Whether the mask returned from the Text 
    ///    property includes Literals and/or prompt characters).  
    ///    When prompt characters are excluded, theyare returned as spaces in the string returned.
    /// </summary>
    [
    RefreshProperties(RefreshProperties.Repaint),
    DefaultValue(MaskFormat.IncludeLiterals)
    ]
    public MaskFormat CutCopyMaskFormat
    {
      get { return TextBoxControl.CutCopyMaskFormat; }
      set { TextBoxControl.CutCopyMaskFormat = value; }
    }

    /// <summary>
    ///     Specifies whether the PromptCharacter is displayed when the control loses focus.
    /// </summary>
    [
    RefreshProperties(RefreshProperties.Repaint),
    DefaultValue(false)
    ]
    public bool HidePromptOnLeave
    {
      get { return TextBoxControl.HidePromptOnLeave; }
      set { TextBoxControl.HidePromptOnLeave = value; }
    }

    /// <summary>
    ///     Specifies the text insertion mode of the text box.  This can be used to simulated the Access masked text
    ///     control behavior where insertion is set to TextInsertionMode.AlwaysOverwrite
    ///     This property has no particular effect if no mask has been set.
    /// </summary>
    [
    DefaultValue(InsertKeyMode.Default)
    ]
    public InsertKeyMode InsertKeyMode
    {
      get { return TextBoxControl.InsertKeyMode; }
      set { TextBoxControl.InsertKeyMode = value; }
    }

    /// <summary>
    ///     The mask applied to this control.  The setter resets the underlying MaskedTextProvider object and attempts
    ///     to add the existing input text (if any) using the new mask, failure is ignored.
    /// </summary>
    [
    RefreshProperties(RefreshProperties.Repaint),
    DefaultValue(""),
    MergableProperty(false),
    Localizable(true),
    Editor("EhLib.Design.MaskPropertyEditor", typeof(UITypeEditor))
    ]
    public string Mask
    {
      get { return TextBoxControl.Mask; }
      set { TextBoxControl.Mask = value; }
    }

    /// <summary>
    ///     Specifies the character to be used in the formatted string in place of editable characters, if
    ///     set to any printable character, the text box becomes a password text box, to reset it use the null
    ///     character.
    /// </summary>
    [
    RefreshProperties(RefreshProperties.Repaint),
    DefaultValue('\0') // This property is shadowed by MaskedTextBoxDesigner.
    ]
    public char PasswordChar
    {
      get { return TextBoxControl.PasswordChar; }
      set { TextBoxControl.PasswordChar = value; }
    }

    /// <summary>
    ///     Specifies the prompt character to be used in the formatted string for unsupplied characters.
    /// </summary>
    [
    RefreshProperties(RefreshProperties.Repaint),
    Localizable(true),
    DefaultValue('_')
    ]
    public char PromptChar
    {
      get { return TextBoxControl.PromptChar; }
      set { TextBoxControl.PromptChar = value; }
    }

    /// <summary>
    ///     Specifies whether to include the mask prompt character when formatting the text in places
    ///     where an edit char has not being assigned.
    /// </summary>
    [
    DefaultValue(false)
    ]
    public bool RejectInputOnFirstFailure
    {
      get { return TextBoxControl.RejectInputOnFirstFailure; }
      set { TextBoxControl.RejectInputOnFirstFailure = value; }
    }

    /// <summary>
    ///     Specifies whether to reset and skip the current position if editable, when the input character
    ///     has the same value as the prompt.  This property takes precedence over AllowPromptAsInput.
    /// </summary>
    [
    DefaultValue(true)
    ]
    public bool ResetOnPrompt
    {
      get { return TextBoxControl.ResetOnPrompt; }
      set { TextBoxControl.ResetOnPrompt = value; }
    }

    /// <summary>
    ///     Specifies whether to reset and skip the current position if editable, when the input 
    ///     is the space character.
    /// </summary>
    [
    DefaultValue(true)
    ]
    public bool ResetOnSpace
    {
      get { return TextBoxControl.ResetOnSpace; }
      set { TextBoxControl.ResetOnSpace = value; }
    }

    /// <summary>
    ///     Specifies whether to skip the current position if non-editable and the input character has 
    ///     the same value as the literal at that position.
    /// </summary>
    [
    DefaultValue(true)
    ]
    public bool SkipLiterals
    {
      get { return TextBoxControl.SkipLiterals; }
      set { TextBoxControl.SkipLiterals = value; }
    }

    /// <summary>
    ///     The Text setter validates the input char by char, raising the MaskInputRejected event for invalid chars.
    ///     The Text getter returns the formatted text according to the IncludeLiterals and IncludePrompt properties.
    /// </summary>
    [
    Editor("System.Windows.Forms.Design.MaskedTextBoxTextEditor", typeof(UITypeEditor)),
    RefreshProperties(RefreshProperties.Repaint),
    Bindable(true),
    DefaultValue(""), // This property is shadowed by MaskedTextBoxDesigner.
    Localizable(true)
    ]
    public override string Text
    {
      get { return TextBoxControl.Text; }
      set { TextBoxControl.Text = value; }
    }

    /// <summary>
    ///     Gets or sets how text is aligned in the control.
    ///     Note: This code is duplicated in TextBox for simplicity.
    /// </summary>
    [DefaultValue(HorizontalAlignment.Left)]
    [Localizable(true)]
    public new HorizontalAlignment TextAlign
    {
      get { return TextBoxControl.TextAlign; }
      set { TextBoxControl.TextAlign = value; }
    }

    /// <summary>
    ///    Specifies the formatting options for text output (Whether the mask returned from the Text 
    ///    property includes Literals and/or prompt characters).  
    ///    When prompt characters are excluded, theyare returned as spaces in the string returned.
    /// </summary>
    [
    RefreshProperties(RefreshProperties.Repaint),
    DefaultValue(MaskFormat.IncludeLiterals)
    ]
    public MaskFormat TextMaskFormat
    {
      get { return TextBoxControl.TextMaskFormat; }
      set { TextBoxControl.TextMaskFormat = value; }
    }

    /// <summary>
    ///    Indicates if the text in the edit control should appear as the default password character. 
    ///    This property has precedence over the PasswordChar property.
    /// </summary>
    [DefaultValue(false)]
    [RefreshProperties(RefreshProperties.Repaint)]
    public new bool UseSystemPasswordChar
    {
      get { return TextBoxControl.UseSystemPasswordChar; }
      set { TextBoxControl.UseSystemPasswordChar = value; }
    }

    /// <summary>
    ///     Type of the object to be used to parse the text when the user leaves the control. 
    ///     A ValidatingType object must implement a method with one fo the following signature:
    ///         public static Object Parse(string)
    ///         public static Object Parse(string, IFormatProvider)
    ///     See DateTime.Parse(...) for an example.
    /// </summary>
    [
    Browsable(false),
    DefaultValue(null)
    ]
    public Type ValidatingType
    {
      get { return TextBoxControl.ValidatingType; }
      set { TextBoxControl.ValidatingType = value; }
    }
    #endregion design-time properties

    #region run-time properties
    [Browsable(false)]
    [DefaultValue(true)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    [Localizable(true)]
    [RefreshProperties(RefreshProperties.Repaint)]
    public override bool AutoSize
    {
      get { return base.AutoSize; }
      set { base.AutoSize = value; }
    }

    [Browsable(false)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    public new bool CanUndo
    {
      get { return base.CanUndo; }
    }

    /// <summary>
    ///     Specifies the IFormatProvider to be used when parsing the string to the ValidatingType.
    /// </summary>
    [
    Browsable(false),
    DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
    ]
    public IFormatProvider FormatProvider
    {
      get { return TextBoxControl.FormatProvider; }
      set { TextBoxControl.FormatProvider = value; }
    }

    /// <summary>
    ///     Specifies whether text insertion mode in 'on' or not.
    /// </summary>
    [
    Browsable(false)
    ]
    public bool IsOverwriteMode
    {
      get { return TextBoxControl.IsOverwriteMode; }
    }

    /// <summary>
    ///     Specifies whether the test string required input positions, as specified by the mask, have 
    ///     all been assigned.
    /// </summary>
    [
    Browsable(false)
    ]
    public bool MaskCompleted
    {
      get { return TextBoxControl.MaskCompleted; }
    }

    /// <summary>
    ///     Specifies whether all inputs (required and optional) have been provided into the mask successfully.
    /// </summary>
    [
    Browsable(false)
    ]
    public bool MaskFull
    {
      get { return TextBoxControl.MaskFull; }
    }

    /// <summary>
    ///     Returns a copy of the control's internal MaskedTextProvider.  This is useful for user's to provide
    ///     cloning semantics for the control (we don't want to do it) w/o incurring in any perf penalty since 
    ///     some of the properties require recreating the underlying provider when they are changed.
    /// </summary>
    [
    Browsable(false),
    DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
    ]
    public MaskedTextProvider MaskedTextProvider
    {
      get { return TextBoxControl.MaskedTextProvider; }
    }

    /// <summary>
    ///     Unsupported method/property.
    ///     WndProc ignores EM_LIMITTEXT & this is a virtual method.
    /// </summary>
    [
    Browsable(false),
    EditorBrowsable(EditorBrowsableState.Never),
    DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
    ]
    public override int MaxLength
    {
      get { return base.MaxLength; }
      set { }
    }

    #endregion

    #region internal properties
    //protected internal new MaskedTextEditControl TextBoxControl
    public new MaskedTextEditControl TextBoxControl
    {
      get
      {
        return (MaskedTextEditControl)EditControl;
      }
    }
    #endregion

    #region public methods
    /// <summary>
    ///     Clears information about the most recent operation from the undo buffer of the control.
    ///     Unsupported property/method.
    /// </summary>
    [
    EditorBrowsable(EditorBrowsableState.Never)
    ]
    public new void ClearUndo()
    {
    }

    /// <summary>
    ///     Returns the character nearest to the given point.
    /// </summary>
    public override char GetCharFromPosition(Point pt)
    {
      return TextBoxControl.GetCharFromPosition(pt);
    }

    /// <summary>
    ///     Returns the index of the character nearest to the given point.
    /// </summary>
    public override int GetCharIndexFromPosition(Point pt)
    {
      return TextBoxControl.GetCharIndexFromPosition(pt);
    }

    /// <summary>
    ///     Unsupported method/property.
    /// </summary>
    [
    EditorBrowsable(EditorBrowsableState.Never)
    ]
    public new int GetFirstCharIndexOfCurrentLine()
    {
      return TextBoxControl.GetFirstCharIndexOfCurrentLine();
    }

    /// <summary>
    ///     Unsupported method/property.
    /// </summary>
    [
    EditorBrowsable(EditorBrowsableState.Never)
    ]
    public new int GetFirstCharIndexFromLine(int lineNumber)
    {
      return TextBoxControl.GetFirstCharIndexFromLine(lineNumber);
    }

    /// <summary>
    ///     Unsupported method/property.
    ///     virtual method.
    /// </summary>
    [
    EditorBrowsable(EditorBrowsableState.Never)
    ]
    public override int GetLineFromCharIndex(int index)
    {
      return TextBoxControl.GetLineFromCharIndex(index);
    }

    /// <summary>
    ///     Returns the location of the character at the given index.
    /// </summary>
    public override Point GetPositionFromCharIndex(int index)
    {
      return TextBoxControl.GetPositionFromCharIndex(index);
    }

    /// <summary>
    ///       Forces type validation.  Returns the validated text value.
    /// </summary>
    public object ValidateText()
    {
      return TextBoxControl.ValidateText();
    }
    #endregion

    #region internal methos
    protected override TextBoxBase CreateTextEditControl()
    {
      return new MaskedTextEditControl(this);
    }
    #endregion

  }

  [DesignTimeVisible(false)]
  //[ToolboxItem(false)]
  public class MaskedTextEditControl : MaskedTextBox
  {
    internal readonly MaskedTextBoxEh TextBox;

    public MaskedTextEditControl(MaskedTextBoxEh textBox)
    {
      this.TextBox = textBox;
    }

    protected override void OnGotFocus(EventArgs e)
    {
      base.OnGotFocus(e);
      //textBox.ActiveControl = this;
      TextBox.IntenalGotFocus(e);
    }

    protected override void OnLostFocus(EventArgs e)
    {
      base.OnLostFocus(e);
      TextBox.IntenalLostFocus(e);
    }

    protected override void WndProc(ref Message m)
    {
      bool handled = false;
      TextBox.TextBoxControl_WndProc(ref m, ref handled);
      if (!handled)
      {
        base.WndProc(ref m);
      }
    }

    protected override void DefWndProc(ref Message m)
    {
      base.DefWndProc(ref m);

      switch (m.Msg)
      {
        case NativeMethods.WM_CHAR:
          KeyPressEventArgs kpe = new KeyPressEventArgs(unchecked((char)(long)m.WParam));
          OnKeyPressProcessed(kpe);
          break;
      }
    }

    protected virtual void OnKeyPressProcessed(KeyPressEventArgs e)
    {
      TextBox.OnKeyPressProcessed(e);
    }

    protected override bool IsInputKey(Keys keyData)
    {
      bool result = TextBox.TextBoxControl_IsInputKey(keyData);
      if (!result)
        result = base.IsInputKey(keyData);
      return result;
    }

    protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
    {
      bool result = base.ProcessCmdKey(ref msg, keyData);
      if (!result)
        result = TextBox.TextBoxControl_ProcessCmdKey(ref msg, keyData);
      return result;
    }

  }

}
