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

using System;
using System.Windows.Forms;
using System.Windows.Forms.VisualStyles;
using System.Drawing;
using System.Diagnostics;

namespace EhLib
{

  internal enum InternalUpDownButtonId
  {
    None = 0,
    Up = 1,
    Down = 2,
  }

  public class InEditUpDownButton : EditItem
  {

    #region constructor
    public InEditUpDownButton()
    {
    }
    #endregion constructor

    #region methods
    protected override EditItemControl CreateInEditWinControl()
    {
      return new InEditWinUpDownButton(this);
    }
    #endregion methods
  }

  internal class InEditWinUpDownButton : EditItemControl
  {

    private const int DefaultTimerInterval = 500;

    // Button state
    private InternalUpDownButtonId pushed = InternalUpDownButtonId.None;
    private InternalUpDownButtonId captured = InternalUpDownButtonId.None;
    private InternalUpDownButtonId mouseOver = InternalUpDownButtonId.None;

    // Timer
    private Timer timer;
    private int timerInterval;

    //private bool doubleClickFired = false;

    #region constructor
    public InEditWinUpDownButton(EditItem inEditControl) : base(inEditControl)
    {
      //SetStyle(ControlStyles.Opaque | ControlStyles.FixedHeight | ControlStyles.FixedWidth, true);
      SetStyle(ControlStyles.Selectable, false);
    }
    #endregion constructor

    private void BeginButtonPress(MouseEventArgs e)
    {

      int halfHeight = Size.Height / 2;

      if (e.Y < halfHeight)
      {
        pushed = InternalUpDownButtonId.Up;
        captured = InternalUpDownButtonId.Up;
        Invalidate();
      }
      else
      {
        pushed = InternalUpDownButtonId.Down;
        captured = InternalUpDownButtonId.Down;
        Invalidate();
      }

      Capture = true;

      OnUpDown(new UpDownEventArgs(InternalUpDownButtonIdToUpDownButtonId(pushed)));

      StartTimer();
    }

    private void EndButtonPress()
    {

      pushed = InternalUpDownButtonId.None;
      captured = InternalUpDownButtonId.None;

      StopTimer();
      Capture = false;
      Invalidate();
    }

    protected override void OnMouseDown(MouseEventArgs e)
    {
      //this.parent.FocusInternal();
      InEditControl.EditControl.Focus();

      if (/*!parent.ValidationCancelled &&*/ e.Button == MouseButtons.Left)
      {
        BeginButtonPress(e);
      }
      //if (e.Clicks == 2 && e.Button == MouseButtons.Left)
      //{
      //  doubleClickFired = true;
      //}

      Debug.Assert(!(pushed != InternalUpDownButtonId.None && captured == InternalUpDownButtonId.None),
                   "Invalid button pushed/captured combination");

      //parent.OnMouseDown(parent.TranslateMouseEvent(this, e));
    }

    protected override void OnMouseMove(MouseEventArgs e)
    {
      if (Capture)
      {

        // Determine button area

        Rectangle rect = ClientRectangle;
        rect.Height /= 2;

        if (captured == InternalUpDownButtonId.Down)
        {
          rect.Y += rect.Height;
        }

        // Test if the mouse has moved outside the button area

        if (rect.Contains(e.X, e.Y))
        {

          // Inside button
          // Repush the button if necessary

          if (pushed != captured)
          {

            // Restart the timer
            StartTimer();

            pushed = captured;
            Invalidate();
          }

        }
        else
        {

          // Outside button
          // Retain the capture, but pop the button up whilst
          // the mouse remains outside the button and the
          // mouse button remains pressed.

          if (pushed != InternalUpDownButtonId.None)
          {

            // Stop the timer for updown events
            StopTimer();

            pushed = InternalUpDownButtonId.None;
            Invalidate();
          }
        }
      }

      //Logic for seeing which button is Hot if any
      Rectangle rectUp = ClientRectangle;
      Rectangle rectDown = ClientRectangle;

      rectUp.Height /= 2;
      rectDown.Y += rectDown.Height / 2;

      //Check if the mouse is on the upper or lower button. Note that it could be in neither.
      if (rectUp.Contains(e.X, e.Y))
      {
        mouseOver = InternalUpDownButtonId.Up;
        Invalidate();
      }
      else if (rectDown.Contains(e.X, e.Y))
      {
        mouseOver = InternalUpDownButtonId.Down;
        Invalidate();
      }

      // At no stage should a button be pushed, and the mouse
      // not captured.
      Debug.Assert(!(pushed != InternalUpDownButtonId.None && captured == InternalUpDownButtonId.None),
                   "Invalid button pushed/captured combination");

      //parent.OnMouseMove(parent.TranslateMouseEvent(this, e));
    }

    protected override void OnMouseUp(MouseEventArgs e)
    {

      if (/*!Parent.ValidationCancelled && */e.Button == MouseButtons.Left)
      {
        EndButtonPress();
      }

      Debug.Assert(!(pushed != InternalUpDownButtonId.None && captured == InternalUpDownButtonId.None),
                   "Invalid button pushed/captured combination");

      //Point pt = new Point(e.X, e.Y);
      //pt = PointToScreen(pt);

      //MouseEventArgs me = parent.TranslateMouseEvent(this, e);
      //if (e.Button == MouseButtons.Left)
      //{
      //  if (!parent.ValidationCancelled && UnsafeNativeMethods.WindowFromPoint(pt.X, pt.Y) == Handle)
      //  {
      //    if (!doubleClickFired)
      //    {
      //      this.parent.OnClick(me);
      //    }
      //    else
      //    {
      //      doubleClickFired = false;
      //      this.parent.OnDoubleClick(me);
      //      this.parent.OnMouseDoubleClick(me);
      //    }
      //  }
      //  doubleClickFired = false;
      //}

      //parent.OnMouseUp(me);
    }

    protected override void OnMouseLeave(EventArgs e)
    {
      mouseOver = InternalUpDownButtonId.None;
      Invalidate();

      //parent.OnMouseLeave(e);
    }

    protected override void OnPaint(PaintEventArgs e)
    {
      bool themed = Application.RenderWithVisualStyles;
      Rectangle btnRect = ClientRectangle;
      if (themed) btnRect.Inflate(1, 1);

      e.Graphics.FillRectangle(new SolidBrush(Color.Red), ClientRectangle);

      int halfHeight = btnRect.Height / 2;

      /* Draw the up and down buttons */

      if (Application.RenderWithVisualStyles)
      {
        VisualStyleRenderer vsr;
        if (mouseOver == InternalUpDownButtonId.Up)
          vsr = new VisualStyleRenderer(VisualStyleElement.Spin.Up.Hot);
        else
          vsr = new VisualStyleRenderer(VisualStyleElement.Spin.Up.Normal);

        if (!Enabled)
        {
          vsr.SetParameters(VisualStyleElement.Spin.Up.Disabled);
        }
        else if (pushed == InternalUpDownButtonId.Up)
        {
          vsr.SetParameters(VisualStyleElement.Spin.Up.Pressed);
        }

        vsr.DrawBackground(e.Graphics, new Rectangle(btnRect.Left, btnRect.Top, btnRect.Width, halfHeight));

        if (!Enabled)
        {
          vsr.SetParameters(VisualStyleElement.Spin.Down.Disabled);
        }
        else if (pushed == InternalUpDownButtonId.Down)
        {
          vsr.SetParameters(VisualStyleElement.Spin.Down.Pressed);
        }
        else
        {
          if (mouseOver == InternalUpDownButtonId.Down)
            vsr.SetParameters(VisualStyleElement.Spin.Down.Hot);
          else
            vsr.SetParameters(VisualStyleElement.Spin.Down.Normal);
        }

        vsr.DrawBackground(e.Graphics, new Rectangle(btnRect.Left, btnRect.Top + halfHeight, btnRect.Width, halfHeight));
      }
      else
      {
        ControlPaint.DrawScrollButton(e.Graphics,
                                      new Rectangle(0, 0, Width, halfHeight),
                                      ScrollButton.Up,
                                      pushed == InternalUpDownButtonId.Up ? ButtonState.Pushed : (Enabled ? ButtonState.Normal : ButtonState.Inactive));

        ControlPaint.DrawScrollButton(e.Graphics,
                                      new Rectangle(0, halfHeight, Width, halfHeight),
                                      ScrollButton.Down,
                                      pushed == InternalUpDownButtonId.Down ? ButtonState.Pushed : (Enabled ? ButtonState.Normal : ButtonState.Inactive));
      }

      if (halfHeight != (btnRect.Height + 1) / 2)
      {
        // When control has odd height, a line needs to be drawn below the buttons with the backcolor.
        using (Pen pen = new Pen(this.Parent.BackColor))
        {
          Rectangle clientRect = btnRect;
          e.Graphics.DrawLine(pen, clientRect.Left, clientRect.Bottom - 1, clientRect.Right - 1, clientRect.Bottom - 1);
        }
      }

      //base.OnPaint(e); // raise paint event, just in case this inner class goes public some day
    }

    protected virtual void OnUpDown(UpDownEventArgs upevent)
    {
      InEditControl.OnUpDown(upevent);
    }

    protected void StartTimer()
    {
      //parent.OnStartTimer();
      if (timer == null)
      {
        timer = new Timer();
        timer.Tick += TimerHandler;
      }

      this.timerInterval = DefaultTimerInterval;

      timer.Interval = this.timerInterval;
      timer.Start();
    }

    protected void StopTimer()
    {
      if (timer != null)
      {
        timer.Stop();
        timer.Dispose();
        timer = null;
      }
      //parent.OnStopTimer();
    }

    private void TimerHandler(object source, EventArgs args)
    {

      if (!Capture)
      {
        EndButtonPress();
        return;
      }

      OnUpDown(new UpDownEventArgs(InternalUpDownButtonIdToUpDownButtonId(pushed)));

      // Accelerate timer.
      this.timerInterval = this.timerInterval * 7;
      this.timerInterval = this.timerInterval / 10;

      if (this.timerInterval < 1)
      {
        this.timerInterval = 1;
      }

      timer.Interval = this.timerInterval;
    }

    protected UpDownButtonId InternalUpDownButtonIdToUpDownButtonId(InternalUpDownButtonId btnId)
    {
      Debug.Assert(btnId != InternalUpDownButtonId.None);
      if (btnId == InternalUpDownButtonId.Down)
        return UpDownButtonId.Down;
      else
        return UpDownButtonId.Up;
    }

  }

}
