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

using System;
using System.Collections;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using System.Windows.Forms.VisualStyles;

namespace EhLib
{

  public enum VisibleMode
  {
    Always,
    Auto,
    Never
  }

  public enum SizeGripPosition
  {
    TopLeft,
    TopRight,
    BottomRight,
    BottomLeft
  }

  public enum GripActiveStatus
  {
    Never,
    Auto,
    Always
  }

  public enum PictureSizeMode
  {
    Normal, Fit, Zoom, Stretch
  }

  public enum CollectionExtendedChangeAction
  {
    ItemAdding, ItemAdded,
    ItemChanging, ItemChanged,
    ItemRemoving, ItemRemoved,
    CollectionChanging, CollectionChanged
  }

  [System.ComponentModel.DesignerCategory("Code")]
  [ToolboxItem(false)]
  public class SizeGrip : Control
  {
    #region privates
    private Point initScreenMousePos;
    private Rectangle initHostScreenBounds;
    private bool internalMove;
    private Point oldMouseMovePos = new Point(-1,-1);
    private Rectangle parentRect;
    private SizeGripPosition position;
    private bool triangleWindow;
    private Control hostControl;
    #endregion privates

    public SizeGrip()
    {
      //ControlStyle = ControlStyle + [csReplicatable, csParentBackground, csCaptureMouse];
      this.SetStyle(ControlStyles.Selectable, false);
      Width = SystemInformation.HorizontalScrollBarHeight;
      Height = SystemInformation.VerticalScrollBarWidth;

      triangleWindow = true;
      position = SizeGripPosition.BottomRight;

      InitData();
    }

    private void InitData()
    {
      BackColor = SystemColors.ButtonFace;
    }

    #region properties
    public SizeGripPosition Position
    {
      get
      {
        return position;
      }

      set
      {
        if (position == value) return;

        position = value;
        if (IsHandleCreated)
        {
          RecreateHandle();
        }
      }
    }

    public bool TriangleWindow
    {
      get
      {
        return triangleWindow;
      }

      set
      {
        if (triangleWindow == value) return;
        triangleWindow = value;
        UpdateWindowRegion();
      }
    }

    public Control HostControl
    {
      get
      {
        if (hostControl != null)
          return hostControl;
        else
          return Parent;
      }

      set
      {
        hostControl = value;
      }
    }

    #endregion privates

    #region methods
    public void UpdateWindowRegion()
    {
      Point[] points = new Point[3];

      if (!IsHandleCreated) return;
      if (TriangleWindow)
      {
        if (Position == SizeGripPosition.BottomRight)
        {
          points[0] = new Point(0, Height);
          points[1] = new Point(Width, Height);
          points[2] = new Point(Width, 0);
          Cursor = Cursors.SizeNWSE;
        }
        else if (Position == SizeGripPosition.BottomLeft)
        {
          points[0] = new Point(Width, Height);
          points[1] = new Point(0, Height);
          points[2] = new Point(0, 0);
          Cursor = Cursors.SizeNESW;
        }
        else if (Position == SizeGripPosition.TopLeft)
        {
          points[0] = new Point(Width - 1, 0);
          points[1] = new Point(0, 0);
          points[2] = new Point(0, Height - 1);
          Cursor = Cursors.SizeNWSE;
        }
        else if (Position == SizeGripPosition.TopRight)
        {
          points[0] = new Point(Width, Height - 1);
          points[1] = new Point(Width, 0);
          points[2] = new Point(1, 0);
          Cursor = Cursors.SizeNESW;
        }

        using (GraphicsPath gp = new GraphicsPath())
        {
          gp.AddClosedCurve(points);
          this.Region = new Region(gp);
        }
        UpdatePosition();
      }
      else
      {
        this.Region = null;
        UpdatePosition();
      }
    }

    public void UpdatePosition()
    {
      if (!HostControl.IsHandleCreated) return;
      internalMove = true;
      switch (Position)
      {
        case SizeGripPosition.BottomRight:
          SetBounds(HostControl.ClientRectangle.Width - Width, HostControl.ClientRectangle.Height - Height,
                    Width, Height);
          break;
        case SizeGripPosition.BottomLeft:
          SetBounds(0, HostControl.ClientRectangle.Height - Height, Width, Height);
          break;
        case SizeGripPosition.TopLeft:
          SetBounds(0, 0, Width, Height);
          break;
        case SizeGripPosition.TopRight:
          SetBounds(HostControl.ClientRectangle.Width - Width, 0, Width, Height);
          break;
      }
      internalMove = false;
    }

    protected override void CreateHandle()
    {
      base.CreateHandle();
      UpdateWindowRegion();
      UpdatePosition();
      BringToFront();
    }

    protected void DrawLine(Graphics g, Color colorPen, Point startPos, Point finishPos)
    {
      Pen pen = new Pen(colorPen);
      try
      {
        g.DrawLine(pen, startPos, finishPos);
      }
      finally
      {
        pen.Dispose();
      }
    }

    protected virtual void PaintByVisualStyles(PaintEventArgs e)
    {
      VisualStyleElement element = null;
      VisualStyleRenderer render;

      switch (Position)
      {
        case SizeGripPosition.BottomRight:
          element = VisualStyleElement.ScrollBar.SizeBox.RightAlign;
          break;
        case SizeGripPosition.BottomLeft:
          element = VisualStyleElement.ScrollBar.SizeBox.LeftAlign;
          break;
        case SizeGripPosition.TopLeft:
          element = VisualStyleElement.ScrollBar.SizeBox.LeftAlign;
          break;
        case SizeGripPosition.TopRight:
          element = VisualStyleElement.ScrollBar.SizeBox.RightAlign;
          break;
      }
      Debug.Assert(element != null, @"element != null");
      render = new VisualStyleRenderer(element);


      if ((Position == SizeGripPosition.TopLeft) ||
          (Position == SizeGripPosition.TopRight))
      {
        using (Bitmap bmFlipBm = new Bitmap(Height, Width))
        {
          using (Graphics gFlip = Graphics.FromImage(bmFlipBm))
          {
            render.DrawBackground(gFlip, ClientRectangle);
            bmFlipBm.RotateFlip(RotateFlipType.RotateNoneFlipY);
            e.Graphics.DrawImage(bmFlipBm, ClientRectangle, ClientRectangle, GraphicsUnit.Pixel);
          }
        }
      } else
      {
        render.DrawBackground(e.Graphics, ClientRectangle);
      }
    }

    protected virtual void PaintByItSelf(PaintEventArgs e)
    {
      int i;
      int xi;
      int yi;

      int[] xArray;
      int[] yArray;
      int xIndex;
      int yIndex;

      Color btnHighlightColor;
      Color btnShadowColor;
      Color btnFaceColor;

      i = 1;
      xArray = new int[2];
      yArray = new int[2];
      if (Position == SizeGripPosition.BottomRight)
      {
        xi = 1;
        yi = 1;
        xIndex = 0;
        yIndex = 1;
        xArray[0] = 0;
        yArray[0] = Width;
        xArray[1] = Width;
        yArray[1] = 0;
      }
      else if (Position == SizeGripPosition.BottomLeft)
      {
        xi = -1;
        yi = 1;
        xIndex = 1;
        yIndex = 0;
        xArray[0] = 0;
        yArray[0] = 1;
        xArray[1] = Width - 1;
        yArray[1] = Width;
      }
      else if (Position == SizeGripPosition.TopLeft)
      {
        xi = -1;
        yi = -1;
        xIndex = 0;
        yIndex = 1;
        xArray[0] = Width - 1;
        yArray[0] = -1;
        xArray[1] = -1;
        yArray[1] = Width - 1;
      }
      else
      {
        xi = 1;
        yi = -1;
        xIndex = 1;
        yIndex = 0;
        xArray[0] = Width;
        yArray[0] = Width - 1;
        xArray[1] = 0;
        yArray[1] = -1;
      }

      btnHighlightColor = SystemColors.ButtonHighlight;
      btnShadowColor = SystemColors.ButtonShadow;
      btnFaceColor = SystemColors.ButtonFace;

      while (i < Width)
      {
        DrawLine(e.Graphics, btnHighlightColor, new Point(xArray[0], yArray[0]), new Point(xArray[1], yArray[1]));
        i++;
        xArray[xIndex] = xArray[xIndex] + xi;
        yArray[yIndex] = yArray[yIndex] + yi;

        DrawLine(e.Graphics, btnShadowColor, new Point(xArray[0], yArray[0]), new Point(xArray[1], yArray[1]));
        i++;
        xArray[xIndex] = xArray[xIndex] + xi;
        yArray[yIndex] = yArray[yIndex] + yi;
        DrawLine(e.Graphics, btnShadowColor, new Point(xArray[0], yArray[0]), new Point(xArray[1], yArray[1]));
        i++;
        xArray[xIndex] = xArray[xIndex] + xi;
        yArray[yIndex] = yArray[yIndex] + yi;

        DrawLine(e.Graphics, btnFaceColor, new Point(xArray[0], yArray[0]), new Point(xArray[1], yArray[1]));
        i++;
        xArray[xIndex] = xArray[xIndex] + xi;
        yArray[yIndex] = yArray[yIndex] + yi;
      }

    }

    protected override void OnPaint(PaintEventArgs e)
    {
      if (Application.RenderWithVisualStyles)
        PaintByVisualStyles(e);
      else
        PaintByItSelf(e);
    }

    protected override void OnMouseDown(MouseEventArgs e)
    {
      base.OnMouseDown(e);
      initScreenMousePos = PointToScreen(e.Location);
      parentRect.Width = HostControl.Width;
      parentRect.Height = HostControl.Height;
      parentRect.X = HostControl.ClientRectangle.Width;
      parentRect.Y = HostControl.ClientRectangle.Height;
      initHostScreenBounds = HostControl.Bounds;
    }

    protected override void OnMouseMove(MouseEventArgs e)
    {
      base.OnMouseMove(e);

      Point newMousePos;
      Point parentWidthHeight = new Point();
      int minimumWidth;
      int minimumHeight;
      Rectangle workArea;
      Rectangle newHostScreenBounds;

      if ((e.Button == MouseButtons.Left) && Capture && !internalMove)
      {
        newMousePos = PointToScreen(e.Location);
        parentWidthHeight.X = HostControl.ClientRectangle.Width;
        parentWidthHeight.Y = HostControl.ClientRectangle.Height;

        if ((oldMouseMovePos.X == newMousePos.X) &&
            (oldMouseMovePos.Y == newMousePos.Y))
          return;

        Screen myScreen = Screen.FromRectangle(initHostScreenBounds);
        workArea = myScreen.WorkingArea;

        newHostScreenBounds = initHostScreenBounds;
        if (initScreenMousePos.X != newMousePos.X)
        {
          if ((Position == SizeGripPosition.BottomRight) ||
            (Position == SizeGripPosition.TopRight))
          {
            newHostScreenBounds.Width = newHostScreenBounds.Width + (newMousePos.X - initScreenMousePos.X);
            if (newHostScreenBounds.Right > workArea.Right)
              newHostScreenBounds.Width = newHostScreenBounds.Width - (newHostScreenBounds.Right - workArea.Right);
          }
          else
          {
            newHostScreenBounds.X = newHostScreenBounds.X + (newMousePos.X - initScreenMousePos.X);
            if (newHostScreenBounds.X < workArea.X)
              newHostScreenBounds.X = workArea.X;
            newHostScreenBounds.Width = initHostScreenBounds.Right - newHostScreenBounds.X;
            var form = HostControl as Form;
            if ((form != null) && (form.MinimumSize.Width > newHostScreenBounds.Width))
            {
              minimumWidth = form.MinimumSize.Width;
              newHostScreenBounds.X = newHostScreenBounds.X + (newHostScreenBounds.Width - minimumWidth);
              newHostScreenBounds.Width = minimumWidth;
            }
          }
        }

        if (initScreenMousePos.Y != newMousePos.Y)
        {
          if ((Position == SizeGripPosition.BottomRight) ||
            (Position == SizeGripPosition.BottomLeft))
          {
            newHostScreenBounds.Height = newHostScreenBounds.Height + (newMousePos.Y - initScreenMousePos.Y);
            if (newHostScreenBounds.Bottom > workArea.Bottom)
              newHostScreenBounds.Height = newHostScreenBounds.Height - (newHostScreenBounds.Bottom - workArea.Bottom);
          }
          else
          {
            newHostScreenBounds.Y = newHostScreenBounds.Y + (newMousePos.Y - initScreenMousePos.Y);
            if (newHostScreenBounds.Y < workArea.Y)
              newHostScreenBounds.Y = workArea.Y;
            newHostScreenBounds.Height = initHostScreenBounds.Bottom - newHostScreenBounds.Y;
            var control = HostControl as Form;
            if ((control != null) && (control.MinimumSize.Height > newHostScreenBounds.Height))
            {
              minimumHeight = control.MinimumSize.Height;
              newHostScreenBounds.Y = newHostScreenBounds.Y + (newHostScreenBounds.Height - minimumHeight);
              newHostScreenBounds.Height = minimumHeight;
            }
          }
        }

        HostControl.Bounds = newHostScreenBounds;

        oldMouseMovePos = newMousePos;

        if ((initHostScreenBounds.Width != HostControl.Width) ||
            (initHostScreenBounds.Height != HostControl.Height))
          ParentResized();
        UpdatePosition();
      }
    }
    
    protected override void OnMouseUp(MouseEventArgs e)
    {
      base.OnMouseUp(e);
    }

    private void ParentResized()
    {
    }

    #endregion methods
  }

  [ToolboxItem(false)]
  public class ButtonEh : Button
  {

    #region privates
    private readonly Timer timer;
    private bool autoRepeat;
    private static readonly object EventMouseDownRepeat = new object();
    #endregion privates

    public ButtonEh()
    {
      timer = new Timer();
      timer.Tick += Timer_Elapsed;
      timer.Interval = 400;
      timer.Enabled = false;
    }

    #region properties
    [DefaultValue(true)]
    public bool Focusable
    {
      get { return GetStyle(ControlStyles.Selectable); }
      set { SetStyle(ControlStyles.Selectable, value); }
    }

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

    public event MouseDownRepeatEventHandler MouseDownRepeat
    {
      add { Events.AddHandler(EventMouseDownRepeat, value); }
      remove { Events.RemoveHandler(EventMouseDownRepeat, value); }
    }
    #endregion properties

    #region methods
    protected virtual void Timer_Elapsed(object value, EventArgs args)
    {
      var handler = Events[EventMouseDownRepeat] as MouseDownRepeatEventHandler;
      if (handler != null)
        handler(this, EventArgs.Empty);
      timer.Interval = 100;
    }

    protected override void OnMouseDown(MouseEventArgs e)
    {
      base.OnMouseDown(e);
      if (AutoRepeat && Capture)
      {
        timer.Interval = 400;
        timer.Enabled = true;
      }
    }

    protected override void OnMouseUp(MouseEventArgs e)
    {
      base.OnMouseUp(e);
      timer.Enabled = false;
    }

    protected override void OnMouseCaptureChanged(EventArgs e)
    {
      base.OnMouseCaptureChanged(e);
      timer.Enabled = false;
    }

    #endregion methods

  }

  [ToolboxItem(false)]
  public class SearchBoxTextEditor : TextBoxEh
  {
    private readonly DataGridSearchBoxControl searchBox;

    public SearchBoxTextEditor(DataGridSearchBoxControl searchBox)
    {
      this.searchBox = searchBox;
      TabStop = false;
    }

    protected override void OnGotFocus(EventArgs e)
    {
      searchBox.FindEditorGotFocus();
    }

    protected override void OnLostFocus(EventArgs e)
    {
      searchBox.FindEditorLostFocus();
    }

    protected override void OnTextChanged(EventArgs e)
    {
      searchBox.TextEditorTextChanged();
    }

    protected override void OnKeyDown(KeyEventArgs e)
    {
      base.OnKeyDown(e);
      if (e.Handled) return;

      searchBox.TextEditorKeyDown(e);
    }

    protected override void OnKeyUp(KeyEventArgs e)
    {
      base.OnKeyUp(e);
      if (e.Handled) return;

      searchBox.TextEditorKeyUp(e);
    }

    protected override void OnKeyPress(KeyPressEventArgs e)
    {
      base.OnKeyPress(e);
      if (e.Handled) return;

      searchBox.TextEditorKeyPress(e);
    }

  }

  public interface IObjectCollectionObserver
  {
    void OnCollectionChangeAction(ObjectCollection collection, CollectionExtendedChangeAction action, int index, object oldItem, object newItem);
    int OnCompareCollectionItems(object item1, object item2);
  }

  internal class ItemComparer : IComparer
  {
    private readonly ObjectCollection collection;

    public ItemComparer(ObjectCollection collection)
    {
      this.collection = collection;
    }

    public int Compare(object item1, object item2)
    {
      if (item1 == null)
      {
        if (item2 == null)
          return 0; //both null, then they are equal

        return -1; //item1 is null, but item2 is valid (greater)
      }
      if (item2 == null)
        return 1; //item2 is null, so item 1 is greater

      return collection.Compare(item1, item2);

    }
  }

  [ListBindable(false)]
  public class ObjectCollection : IList
  {

    private readonly IObjectCollectionObserver observer;
    private ArrayList innerList;
    private IComparer comparer;
    private bool sorted;

    public ObjectCollection(IObjectCollectionObserver observer)
    {
      this.observer = observer;
    }

    [DefaultValue(false)]
    public bool Sorted
    {
      get
      {
        return sorted;
      }
      set
      {
        if (sorted != value)
        {
          sorted = value;
          RefreshItems();
        }
      }
    }

    private IComparer Comparer
    {
      get
      {
        if (comparer == null)
        {
          comparer = new ItemComparer(this);
        }
        return comparer;
      }
    }

    private ArrayList InnerList
    {
      get
      {
        if (innerList == null)
        {
          innerList = new ArrayList();
        }
        return innerList;
      }
    }

    public int Count
    {
      get
      {
        return InnerList.Count;
      }
    }

    object ICollection.SyncRoot
    {
      get
      {
        return this;
      }
    }

    bool ICollection.IsSynchronized
    {
      get
      {
        return false;
      }
    }

    bool IList.IsFixedSize
    {
      get
      {
        return false;
      }
    }

    public bool IsReadOnly
    {
      get
      {
        return false;
      }
    }

    public int Add(object item)
    {
      observer.OnCollectionChangeAction(this, CollectionExtendedChangeAction.ItemAdding, -1, null, item);
      int index = AddInternal(item);
      observer.OnCollectionChangeAction(this, CollectionExtendedChangeAction.ItemAdded, index, null, item);
      return index;
    }
 
    private int AddInternal(object item)
    {

      if (item == null)
      {
        throw new ArgumentNullException("item");
      }
      int index = -1;
      if (!Sorted)
      {
        InnerList.Add(item);
      }
      else
      {
        index = InnerList.BinarySearch(item, Comparer);
        if (index < 0)
        {
          index = ~index; // getting the index of the first element that is larger than the search value
        }

        Debug.Assert(index >= 0 && index <= InnerList.Count, "Wrong index for insert");

        InnerList.Insert(index, item);
      }

      return index;
    }

    int IList.Add(object item)
    {
      return Add(item);
    }

    public void AddRange(object[] items)
    {
      if (items.Length == 0) return;

      observer.OnCollectionChangeAction(this, CollectionExtendedChangeAction.CollectionChanging, -1, null, null);
      try
      {
        AddRangeInternal(items);
      }
      finally
      {
        observer.OnCollectionChangeAction(this, CollectionExtendedChangeAction.CollectionChanged, -1, null, null);
      }
    }

    internal void AddRangeInternal(IList items)
    {

      if (items == null)
        throw new ArgumentNullException("items");

      foreach (object item in items)
      {
        AddInternal(item);
      }
    }

    [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    public virtual object this[int index]
    {
      get
      {
        if (index < 0 || index >= InnerList.Count)
        {
          throw new ArgumentOutOfRangeException("index", @"InvalidArgument");
        }

        return InnerList[index];
      }

      set
      {
        object oldItem = this[index];
        observer.OnCollectionChangeAction(this, CollectionExtendedChangeAction.ItemChanging, index, oldItem, value);
        SetItemInternal(index, value);
        observer.OnCollectionChangeAction(this, CollectionExtendedChangeAction.ItemChanged, index, oldItem, value);
      }
    }

    public void Clear()
    {
      if (Count > 0)
      {
        observer.OnCollectionChangeAction(this, CollectionExtendedChangeAction.CollectionChanging, -1, null, null);
        ClearInternal();
        observer.OnCollectionChangeAction(this, CollectionExtendedChangeAction.CollectionChanged, -1, null, null);
      }
    }

    internal void ClearInternal()
    {
      InnerList.Clear();
    }

    public bool Contains(object value)
    {
      return IndexOf(value) != -1;
    }

    public void CopyTo(object[] destination, int arrayIndex)
    {
      InnerList.CopyTo(destination, arrayIndex);
    }

    public object[] ToArray()
    {
      return InnerList.ToArray();
    }

    void ICollection.CopyTo(Array destination, int index)
    {
      InnerList.CopyTo(destination, index);
    }

    public IEnumerator GetEnumerator()
    {
      return InnerList.GetEnumerator();
    }

    public int IndexOf(object value)
    {
      if (value == null)
      {
        throw new ArgumentNullException("value");
      }

      return InnerList.IndexOf(value);
    }

    public void Insert(int index, object value)
    {
      observer.OnCollectionChangeAction(this, CollectionExtendedChangeAction.ItemAdding, index, null, value);

      if (value == null)
      {
        throw new ArgumentNullException("value");
      }

      if (index < 0 || index > InnerList.Count)
      {
        throw new ArgumentOutOfRangeException("index", @"InvalidArgument");
      }

      if (Sorted)
        index = Add(value);
      else
        InnerList.Insert(index, value);

      observer.OnCollectionChangeAction(this, CollectionExtendedChangeAction.ItemAdded, index, null, value);
    }

    public void RemoveAt(int index)
    {
      object oldItem = this[index];
      observer.OnCollectionChangeAction(this, CollectionExtendedChangeAction.ItemRemoving, index, null, oldItem);

      if (index < 0 || index >= InnerList.Count)
        throw new ArgumentOutOfRangeException("index", @"InvalidArgument");

      InnerList.RemoveAt(index);

      observer.OnCollectionChangeAction(this, CollectionExtendedChangeAction.ItemRemoved, index, oldItem, null);
    }

    public void Remove(object value)
    {

      int index = InnerList.IndexOf(value);

      if (index != -1)
      {
        RemoveAt(index);
      }
    }

    internal void SetItemInternal(int index, object value)
    {
      if (value == null)
        throw new ArgumentNullException("value");

      if (index < 0 || index >= InnerList.Count)
        throw new ArgumentOutOfRangeException("index", @"InvalidArgument");

      InnerList[index] = value;

    }

    private void RefreshItems()
    {
      if (Sorted && Count > 0)
      {
        observer.OnCollectionChangeAction(this, CollectionExtendedChangeAction.CollectionChanging, -1, null, null);
        innerList.Sort(Comparer);
        observer.OnCollectionChangeAction(this, CollectionExtendedChangeAction.CollectionChanged, -1, null, null);
      }
    }

    internal int Compare(object item1, object item2)
    {
      return observer.OnCompareCollectionItems(item1, item2);
    }
  }

  internal class MouseHoverToolTip : ToolTip
  {
    private System.Windows.Forms.Timer mouseHoverTimer = new System.Windows.Forms.Timer();
    private string text;
    private Control window;
    private int duration;
    private Nullable<Point> position;

    public MouseHoverToolTip()
    {
      mouseHoverTimer.Interval = SystemInformation.MouseHoverTime;
      mouseHoverTimer.Tick += new EventHandler(OnTick);

      ShowAlways = false;
      InitialDelay = 0;
      UseFading = true;
      UseAnimation = false;
      AutoPopDelay = 0;
    }

    public new void Show(string text, Control window, int delay, int duration)
    {
      Show(text, window, null, delay, duration);
    }

    public new void Show(string text, Control window, Point position, int delay, int duration)
    {
      Show(text, window, (Nullable<Point>)position, delay, duration);
    }

    internal void Show(string text, Control window, Nullable<Point> position, int delay, int duration)
    {
      this.text = text;
      this.window = window;
      this.duration = duration;
      this.position = position;

      if (delay == 0)
      {
        OnTick(this, EventArgs.Empty);
      }
      else
      {
        if (delay == -1)
          mouseHoverTimer.Interval = SystemInformation.MouseHoverTime;
        else
          mouseHoverTimer.Interval = delay;
        mouseHoverTimer.Enabled = true;
      }
    }

    public void Hide()
    {
      if (mouseHoverTimer.Enabled || Active)
      {
        Active = false;
        Hide(window);
        Cancel();
      }
    }

    public void Cancel()
    {
      mouseHoverTimer.Enabled = false;
    }

    protected override void Dispose(bool disposing)
    {
      if (disposing && mouseHoverTimer != null)
      {
        Cancel();
        mouseHoverTimer.Dispose();
        mouseHoverTimer = null;
      }
    }

    private void OnTick(object sender, EventArgs e)
    {
      mouseHoverTimer.Enabled = false;

      Point hintShowPos;

      if (position.HasValue)
      {
        hintShowPos = position.Value;
      }
      else
      {
        hintShowPos = window.PointToClient(Cursor.Position);
        hintShowPos.Offset(0, 20);
      }

      Active = true;
      Show(this.text, this.window, hintShowPos, this.duration);
    }
  }

}
