//------------------------------------------------------------------------------
// <copyright file=”*.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.Reflection;
using System.Windows.Forms;

namespace EhLib
{
  [DesignerCategory("Code")]
  public class BaseTextBoxEh : BaseEditBoxEh
  {

    #region consts
    //private const int ThemedBorderWidth = 1; // width of custom border we draw when themed
    #endregion consts

    #region internal fields
    private bool codeTextChanged;
    private bool userTextChanged;
    #endregion internal fields

    #region constructor
    public BaseTextBoxEh()
    {

      InitData();
      //SetStyle(ControlStyles.Selectable, false);
      DataBindings.CollectionChanged += DataBindings_CollectionChanged;
    }

    private void InitData()
    {
      SetStyle(ControlStyles.FixedHeight, AutoHeight());
    }

    protected override void Dispose(bool disposing)
    {
      UnboundDataBindings();
      base.Dispose(disposing);
    }
    #endregion

    #region design-time properties

    //[DefaultValue(false)]
    //protected bool AcceptsReturn
    //{
    //  get
    //  {
    //    return TextBoxControl.AcceptsReturn;
    //  }
    //  set
    //  {
    //    TextBoxControl.AcceptsReturn = value;
    //  }
    //}

    //[Browsable(true)]
    //[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
    //[Editor("System.Windows.Forms.Design.ListControlStringCollectionEditor, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor))]
    //[EditorBrowsable(EditorBrowsableState.Always)]
    //[Localizable(true)]
    //protected AutoCompleteStringCollection AutoCompleteCustomSource
    //{
    //  get
    //  {
    //    return TextBoxControl.AutoCompleteCustomSource;
    //  }
    //  set
    //  {
    //    TextBoxControl.AutoCompleteCustomSource = value;
    //  }
    //}

    //[Browsable(true)]
    //[DefaultValue(AutoCompleteMode.None)]
    //[EditorBrowsable(EditorBrowsableState.Always)]
    //protected AutoCompleteMode AutoCompleteMode
    //{
    //  get
    //  {
    //    return TextBoxControl.AutoCompleteMode;
    //  }
    //  set
    //  {
    //    TextBoxControl.AutoCompleteMode = value;
    //  }
    //}

    //[Browsable(true)]
    //[DefaultValue(AutoCompleteSource.None)]
    //[EditorBrowsable(EditorBrowsableState.Always)]
    //// TODO ? [TypeConverter(typeof(TextBoxAutoCompleteSourceConverter))]
    //protected AutoCompleteSource AutoCompleteSource
    //{
    //  get
    //  {
    //    return TextBoxControl.AutoCompleteSource;
    //  }
    //  set
    //  {
    //    TextBoxControl.AutoCompleteSource = value;
    //  }
    //}

    //[DefaultValue(CharacterCasing.Normal)]
    //protected CharacterCasing CharacterCasing
    //{
    //  get
    //  {
    //    return TextBoxControl.CharacterCasing;
    //  }
    //  set
    //  {
    //    TextBoxControl.CharacterCasing = value;
    //  }
    //}

    //[Description("TextBoxMultilineDescr")]
    [Category("Behavior")]
    [DefaultValue(false)]
    [Localizable(true)]
    [RefreshProperties(RefreshProperties.All)]
    protected bool Multiline
    {
      get
      {
        return TextBoxControl.Multiline;
      }
      set
      {
        if (value != Multiline)
        {
          TextBoxControl.Multiline = value;
          UpdateFixedHeight();
        }
      }
    }

    //[DefaultValue('\0')]
    //[Localizable(true)]
    //[RefreshProperties(RefreshProperties.Repaint)]
    //protected char PasswordChar
    //{
    //  get
    //  {
    //    return TextBoxControl.PasswordChar;
    //  }
    //  set
    //  {
    //    TextBoxControl.PasswordChar = value;
    //  }
    //}

    //[DefaultValue(ScrollBars.None)]
    //[Localizable(true)]
    //public ScrollBars ScrollBars
    //{
    //  get
    //  {
    //    return TextBoxControl.ScrollBars;
    //  }
    //  set
    //  {
    //    TextBoxControl.ScrollBars = value;
    //  }
    //}

    //[Editor("System.ComponentModel.Design.MultilineStringEditor, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor))]
    //[Editor(typeof(EhLib.Design.InEditControlEditor), typeof(UITypeEditor))]
    [Localizable(true)]
    public override string Text
    {
      get
      {
        return TextBoxControl.Text;
      }
      set
      {
        codeTextChanged = true;
        try
        {
          TextBoxControl.Text = value;
        }
        finally
        {
          codeTextChanged = false;
        }
      }
    }

    //[DefaultValue(HorizontalAlignment.Left)]
    //[Localizable(true)]
    //protected HorizontalAlignment TextAlign
    //{
    //  get
    //  {
    //    return TextBoxControl.TextAlign;
    //  }
    //  set
    //  {
    //    TextBoxControl.TextAlign = value;
    //  }
    //}

    //[DefaultValue(false)]
    //[RefreshProperties(RefreshProperties.Repaint)]
    //protected bool UseSystemPasswordChar
    //{
    //  get
    //  {
    //    return TextBoxControl.UseSystemPasswordChar;
    //  }
    //  set
    //  {
    //    TextBoxControl.UseSystemPasswordChar = value;
    //  }
    //}

    [DefaultValue(false)]
    protected bool AcceptsTab
    {
      get
      {
        return TextBoxControl.AcceptsTab;
      }
      set
      {
        TextBoxControl.AcceptsTab = value;
      }
    }

    public override Color BackColor
    {
      get
      {
        return TextBoxControl.BackColor;
      }
      set
      {
        TextBoxControl.BackColor = value;
      }
    }

    public override Color ForeColor
    {
      get
      {
        return TextBoxControl.ForeColor;
      }
      set
      {
        base.ForeColor = value;
        TextBoxControl.ForeColor = value;
      }
    }

    //[Browsable(false)]
    //[EditorBrowsable(EditorBrowsableState.Never)]
    //public override Image BackgroundImage { get; set; }

    //[Browsable(false)]
    //[EditorBrowsable(EditorBrowsableState.Never)]
    //public override ImageLayout BackgroundImageLayout { get; set; }

    [DefaultValue(true)]
    protected bool HideSelection
    {
      get
      {
        return TextBoxControl.HideSelection;
      }
      set
      {
        TextBoxControl.HideSelection = value;
      }
    }

    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [Editor("System.Windows.Forms.Design.StringArrayEditor, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor))]
    [Localizable(true)]
    [MergableProperty(false)]
    protected string[] Lines
    {
      get
      {
        return TextBoxControl.Lines;
      }
      set
      {
        TextBoxControl.Lines = value;
      }
    }

    [DefaultValue(32767)]
    [Localizable(true)]
    public virtual int MaxLength
    {
      get
      {
        return TextBoxControl.MaxLength;
      }
      set
      {
        TextBoxControl.MaxLength = value;
      }
    }

    //[Browsable(false)]
    //[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    //[EditorBrowsable(EditorBrowsableState.Never)]
    //public Padding Padding { get; set; }

    public override int PreferredHeight
    {
      get
      {
        int result = TextBoxControl.PreferredHeight;
        if (Border.Style != ControlBorderStyle.None)
        {
          result = result + SystemInformation.BorderSize.Height * 4 + 3;
        }
        return result;
      }
    }

    [DefaultValue(false)]
    [RefreshProperties(RefreshProperties.Repaint)]
    public override bool ReadOnly
    {
      get
      {
        return TextBoxControl.ReadOnly;
      }
      set
      {
        TextBoxControl.ReadOnly = value;
      }
    }

    [DefaultValue(true)]
    public virtual bool ShortcutsEnabled
    {
      get
      {
        return TextBoxControl.ShortcutsEnabled;
      }
      set
      {
        TextBoxControl.ShortcutsEnabled = value;
      }
    }

    [DefaultValue(true)]
    [Localizable(true)]
    protected bool WordWrap
    {
      get
      {
        return TextBoxControl.WordWrap;
      }
      set
      {
        TextBoxControl.WordWrap = 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 TextBoxControl.AutoSize;
      }
      set
      {
        if (TextBoxControl.AutoSize != value)
        {
          TextBoxControl.AutoSize = value;
          if (!Multiline)
          {
            SetStyle(ControlStyles.FixedHeight, value);
            AdjustHeight(false);
          }
        }
      }
    }

    [Browsable(false)]
    public virtual int TextLength
    {
      get
      {
        return TextBoxControl.TextLength;
      }
    }

    [Browsable(false)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    protected bool CanUndo
    {
      get
      {
        return TextBoxControl.CanUndo;
      }
    }

    [Browsable(false)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    public bool Modified
    {
      get
      {
        return TextBoxControl.Modified;
      }
      set
      {
        TextBoxControl.Modified = value;
      }
    }

    [Browsable(false)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    public virtual string SelectedText
    {
      get
      {
        return TextBoxControl.SelectedText;
      }
      set
      {
        TextBoxControl.SelectedText = value;
      }
    }

    [Browsable(false)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    public virtual int SelectionLength
    {
      get
      {
        return TextBoxControl.SelectionLength;
      }
      set
      {
        TextBoxControl.SelectionLength = value;
      }
    }

    [Browsable(false)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    public int SelectionStart
    {
      get
      {
        return TextBoxControl.SelectionStart;
      }
      set
      {
        TextBoxControl.SelectionStart = value;
      }
    }

    protected internal TextBoxBase TextBoxControl
    {
      get
      {
        return (TextBoxBase)EditControl;
      }
    }

    protected override Padding DefaultPadding
    {
      get
      {
        if (Border.Style == ControlBorderStyle.None)
          return Padding.Empty;
        else
          return new Padding(1, 1, 1, 2);
      }
    }

    [Browsable(false)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    public override bool Focused
    {
      get
      {
        return TextBoxControl.Focused;
      }
    }

    [Browsable(false)]
    public bool UserTextChanged
    {
      get { return userTextChanged; }
    }
    #endregion

    #region internal properties
    #endregion

    #region public methods
    public void AppendText(string text)
    {
      TextBoxControl.AppendText(text);
    }

    public override void Clear()
    {
      TextBoxControl.Clear();
    }

    public void ClearUndo()
    {
      TextBoxControl.ClearUndo();
    }

    /// <devdoc>
    ///     EhLib devdoc. Copies the current selection in the text box to the Clipboard.
    /// </devdoc>
    /// 
    /// <summary>
    ///     EhLib summary. Copies the current selection in the text box to the Clipboard.
    /// </summary>
    public override void Copy()
    {
      TextBoxControl.Copy();
    }

    public override void Cut()
    {
      TextBoxControl.Cut();
    }

    public override void Paste()
    {
      TextBoxControl.Paste();
    }

    public void DeselectAll()
    {
      TextBoxControl.DeselectAll();
    }

    public virtual char GetCharFromPosition(Point pos)
    {
      return TextBoxControl.GetCharFromPosition(pos);
    }

    public virtual int GetCharIndexFromPosition(Point pos)
    {
      return TextBoxControl.GetCharIndexFromPosition(pos);
    }

    public int GetFirstCharIndexFromLine(int lineNumber)
    {
      return TextBoxControl.GetFirstCharIndexFromLine(lineNumber);
    }

    public int GetFirstCharIndexOfCurrentLine()
    {
      return TextBoxControl.GetFirstCharIndexOfCurrentLine();
    }

    public virtual int GetLineFromCharIndex(int index)
    {
      return TextBoxControl.GetLineFromCharIndex(index);
    }

    public virtual Point GetPositionFromCharIndex(int index)
    {
      return TextBoxControl.GetPositionFromCharIndex(index);
    }

    public void ScrollToCaret()
    {
      TextBoxControl.ScrollToCaret();
    }

    public void Select(int start, int length)
    {
      TextBoxControl.Select(start, length);
    }

    //internal void InternalSelect(int start, int length)
    //{
    //  EhLibUtils.SendMessage(TextBoxControl, NativeMethods.EM_SETSEL, (IntPtr)start, (IntPtr)start + length);
    //  EhLibUtils.SendMessage(TextBoxControl, NativeMethods.EM_SCROLLCARET, IntPtr.Zero, IntPtr.Zero);
    //}

    public void SelectAll()
    {
      TextBoxControl.SelectAll();
    }

    public override string ToString()
    {
      return TextBoxControl.ToString();
    }

    public void Undo()
    {
      TextBoxControl.Undo();
    }

    public override void HandleInEditControlDownAction(EditItem inEditControl, DownEventArgs e)
    {
      if (inEditControl.DefaultAction || inEditControl.IsDropDown())
        InEditButtonDownDefaultAction(inEditControl, e);
    }

    public override void InEditButtonDownDefaultAction(EditItem inEditControl, DownEventArgs e)
    {
      TextBoxEhManager.DefaultManager.InEditButtonDownDefaultAction(this, inEditControl, e);
    }
    #endregion public methods

    #region internal methos
    #region internal methos.Paddings
    protected override void OnPaddingChanged(EventArgs e)
    {
      base.OnPaddingChanged(e);
      RealignControls();
    }
    #endregion internal methos.Paddings

    #region internal methos.TextBoxControl
    private void TextBoxControl_TextChanged(object sender, EventArgs e)
    {
      if (!codeTextChanged)
      {
        userTextChanged = true;
      }

      this.OnTextChanged(e);
      //OnChanged(source, new EventArgs());
    }

    protected virtual void TextBoxControl_KeyDown(object sender, KeyEventArgs e)
    {
      this.OnKeyDown(e);
    }

    protected virtual void TextBoxControl_KeyUp(object sender, KeyEventArgs e)
    {
      this.OnKeyUp(e);
    }

    protected virtual void TextBoxControl_KeyPress(object sender, KeyPressEventArgs e)
    {
      this.OnKeyPress(e);
    }

    private void TextBoxControl_Click(object sender, EventArgs e)
    {
      this.OnClick(e);
    }

    private void TextBoxControl_MouseDown(object sender, MouseEventArgs e)
    {
      this.OnMouseDown(e);
    }

    private void TextBoxControl_MouseUp(object sender, MouseEventArgs e)
    {
      this.OnMouseUp(e);
    }

    protected virtual void TextBoxControl_GotFocus(object sender, EventArgs e)
    {
      Invalidate(true);
    }

    protected virtual void TextBoxControl_LostFocus(object sender, EventArgs e)
    {
      Invalidate(true);
    }

    protected virtual void TextBoxControl_MouseEnter(object sender, EventArgs e)
    {
      MouseOver = true;
      Invalidate(true);
    }

    protected virtual void TextBoxControl_MouseLeave(object sender, EventArgs e)
    {
      if (this.ClientRectangle.Contains(this.PointToClient(MousePosition)))
      {
        EhLibUtils.DoNothing();
      }
      else
      {
        MouseOver = false;
      }
    }

    protected internal virtual void TextBoxControl_WndProc(ref Message msg, ref bool handled)
    {
      if (handled) return;

      switch (msg.Msg)
      {
        case NativeMethods.WM_PASTE:
          handled = DoCustomPaste();
          break;
        case NativeMethods.WM_CANCELMODE:
          CancelMode();
          break;
      }
    }

    private void CancelMode()
    {
      //;
    }

    protected internal virtual bool TextBoxControl_ProcessCmdKey(ref Message msg, Keys keyData)
    {
      if (InEditControl != null)
        return InEditControl.ProcessCmdKey(ref msg, keyData);
      else
        return false;
    }

    protected virtual bool DoCustomPaste()
    {
      return false;
    }

    internal void IntenalLostFocus(EventArgs e)
    {
      this.OnLostFocus(e);
    }

    internal void IntenalGotFocus(EventArgs e)
    {
      this.OnGotFocus(e);
    }

    internal bool TextBoxControl_IsInputKey(Keys keyData)
    {
      return IsInputKey(keyData);
    }

    protected internal virtual void OnKeyPressProcessed(KeyPressEventArgs e)
    {
    }
    #endregion internal methos.TextBoxControl

    protected virtual TextBoxBase CreateTextEditControl()
    {
      return new TextEditControl(this);
    }

    protected override Control CreateEditControl()
    {
      TextBoxBase result = CreateTextEditControl();
      result.BorderStyle = BorderStyle.None;
      result.GotFocus += TextBoxControl_GotFocus;
      result.LostFocus += TextBoxControl_LostFocus;
      result.MouseEnter += TextBoxControl_MouseEnter;
      result.MouseLeave += TextBoxControl_MouseLeave;
      result.KeyDown += TextBoxControl_KeyDown;
      result.KeyUp += TextBoxControl_KeyUp;
      result.KeyPress += TextBoxControl_KeyPress;
      result.Click += TextBoxControl_Click;
      result.TextChanged += TextBoxControl_TextChanged;
      result.MouseDown += TextBoxControl_MouseDown;
      result.MouseUp += TextBoxControl_MouseUp; ;

      return result;
    }

    protected override void OnLostFocus(EventArgs e)
    {
      base.OnLostFocus(e);
      userTextChanged = false;
    }

    protected override void OnKeyDown(KeyEventArgs e)
    {
      base.OnKeyDown(e);
      if (!e.Handled)
      {
        switch (e.KeyCode)
        {
          case Keys.F2:
            if (SelectionLength == Text.Length)
              Select(Text.Length, 0);
            else
              SelectAll();
            e.Handled = true;
            break;
        }
      }
    }

    protected override void OnFontChanged(EventArgs e)
    {
      base.OnFontChanged(e);
      this.AdjustHeight(false);
    }

    protected override void OnHandleCreated(EventArgs e)
    {
      base.OnHandleCreated(e);
      this.AdjustHeight(true);
    }

    protected virtual void DataBindings_CollectionChanged(object sender, CollectionChangeEventArgs e)
    {
      if (e == null) return;

      Binding bindingObj = (e.Element as Binding);

      if (bindingObj != null)
      {
        if (e.Action == CollectionChangeAction.Add)
        {
          if (bindingObj.PropertyName == "Text")
          {
            bindingObj.BindingComplete += BindingObj_BindingComplete;
            UpdateDataForBinding(bindingObj);
          }
        }
        else if (e.Action == CollectionChangeAction.Remove)
        {
          bindingObj.BindingComplete -= BindingObj_BindingComplete;
        }
      }
    }

    private void BindingObj_BindingComplete(object sender, BindingCompleteEventArgs e)
    {
      UpdateDataForBinding(sender as Binding);
    }

    private void UnboundDataBindings()
    {
      foreach (Binding b in DataBindings)
      {
        b.BindingComplete -= BindingObj_BindingComplete;
      }
    }

    protected virtual bool IsUpdateMaxLengthFromBinding()
    {
      return true;
    }

    private void UpdateDataForBinding(Binding binding)
    {
      PropertyDescriptor propDesc;
      PropertyInfo propInfo;
      object colObj;
      DataColumn dCol;

      if (binding == null || binding.BindingManagerBase == null) return;

      propDesc = binding.BindingManagerBase.GetItemProperties().Find(binding.BindingMemberInfo.BindingField, true);
      if (propDesc == null) return;

      propInfo = propDesc.GetType().GetProperty("Column", BindingFlags.NonPublic | BindingFlags.Instance);
      if (propInfo == null) return;

      colObj = propInfo.GetValue(propDesc, null);
      if (colObj == null) return;

      dCol = (colObj as DataColumn);
      if (dCol == null) return;

      if (IsUpdateMaxLengthFromBinding())
        this.MaxLength = dCol.MaxLength;
    }

    protected override bool AutoHeight()
    {
      if (AutoSize && !Multiline)
        return true;
      else
        return false;
    }

    private void UpdateFixedHeight()
    {
      SetStyle(ControlStyles.FixedHeight, AutoHeight());
    }
    #endregion internal methos

  }

  [DesignTimeVisible(false)]
  //[ToolboxItem(false)]
  public class TextEditControl : TextBox
  {
    internal readonly BaseTextBoxEh TextBox;

    public TextEditControl(BaseTextBoxEh 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;
    }

  }
}
