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

// Через WM_MOUSEACTIVATE отловить, что конрол по которому будет нажатие уже не должен опять открывать
//  DropDownForm, чтобы смоделировать закрытие DropDownForm по нажатию на кнопку.
// в lParam смотри координаты нажатия 

using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Windows.Forms.VisualStyles;

namespace EhLib
{
  [Flags]
  public enum DropDownFormElements
  {
    LeftGrip = 1,
    RightGrip = 2,
    CloseButton = 4,
    SizingBar = 32
  };

  public enum VerticalDropLayout
  {
    AboveControl,
    UnderControl
  }

  public class DropDownFormCloseEventArgs : EventArgs
  {
    private readonly DialogResult closeResult;
    private readonly bool ignoreMouseScreenRectPressed;

    public DropDownFormCloseEventArgs(DialogResult closeResult, bool ignoreMouseScreenRectPressed)
    {
      this.closeResult = closeResult;
      this.ignoreMouseScreenRectPressed = ignoreMouseScreenRectPressed;
    }

    public virtual DialogResult CloseResult
    {
      get { return closeResult; }
    }

    public virtual bool IgnoreMouseScreenRectPressed
    {
      get { return ignoreMouseScreenRectPressed; }
    }
  }

  public delegate void DropDownFormCloseHandler(object sender, DropDownFormCloseEventArgs e);

  public class DropDownForm : Form
  {

    private readonly System.ComponentModel.IContainer components = null;

    #region DropDownForm privates
    //private DropDownAlign align;
    private readonly SizeGrip sizeGrip;
    private readonly SizeGrip sizeGrip2;
    private DropDownFormElements formElements;
    private int borderWidth;
    //private VerticalDropLayout dropLayout;
    private DropDownFormCloseHandler closeHandler;
    private Control masterControl;
    private bool dropDownBorder = true;

    protected bool dropDownMode;
    private Rectangle ignoreMouseScreenRect;
    protected bool ignoreMouseScreenRectPressed;
    #endregion

    public DropDownForm()
    {
      InitializeComponent();

      sizeGrip = new SizeGrip();
      sizeGrip.HostControl = this;
      sizeGrip.TriangleWindow = true;
      sizeGrip.Visible = false;
      sizeGrip.Parent = this;
      sizeGrip.Name = "SizeGrip";


      sizeGrip2 = new SizeGrip();
      sizeGrip2.HostControl = this;
      sizeGrip2.TriangleWindow = true;
      sizeGrip2.Position = SizeGripPosition.BottomLeft;
      sizeGrip2.Visible = false;
      sizeGrip2.Parent = this;
      sizeGrip2.Name = "SizeGrip2";

      formElements = DropDownFormElements.LeftGrip |
                      DropDownFormElements.RightGrip |
                      DropDownFormElements.CloseButton |
                      DropDownFormElements.SizingBar;

      ControlBox = false;
      FormBorderStyle = FormBorderStyle.None;
      ShowInTaskbar = false;
      dropDownMode = true;
      InitData();
    }

    private void InitData()
    {
      MinimumSize = new Size(SystemInformation.HorizontalScrollBarHeight,
        SystemInformation.VerticalScrollBarWidth);
    }

    #region DropDownForm properties
    public DropDownFormElements FormElements
    {
      get
      {
        return formElements;
      }

      set
      {
        formElements = value;
      }
    }

    [DefaultValue(true)]
    public bool DropDownBorder
    {
      get
      {
        return dropDownBorder;
      }

      set
      {
        dropDownBorder = value;
      }
    }

    protected override CreateParams CreateParams
    {
      get
      {
        CreateParams cp = base.CreateParams;
        cp.ClassStyle |= NativeMethods.CS_DROPSHADOW;
        return cp;
      }
    }

    [Browsable(false)]
    public Control MasterControl
    {
      get
      {
        return masterControl;
      }
    }
    #endregion DropDownForm properties

    #region DropDownForm methods

    private void InitializeComponent()
    {
      this.SuspendLayout();
      // 
      // DropDownForm
      // 
      this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
      this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
      this.ClientSize = new System.Drawing.Size(284, 262);
      this.Name = "DropDownForm";
      this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
      this.Text = @"Form1";
      this.Deactivate += this.DropDownForm_Deactivate;
      this.ResumeLayout(false);

    }

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

    protected override void CreateHandle()
    {
      if (Environment.OSVersion.Version.Major >= 6)
        borderWidth = 3;
      else
        borderWidth = 2;
      base.CreateHandle();
      if (!DesignMode && dropDownMode)
      {
        sizeGrip.UpdatePosition();
        sizeGrip.Visible = true;
        sizeGrip.BringToFront();
        sizeGrip2.UpdatePosition();
        sizeGrip2.Visible = true;
        sizeGrip2.BringToFront();
      }
    }

    protected override void OnDeactivate(EventArgs e)
    {
//      return;
      base.OnDeactivate(e);

      var mouseButtonsAsyn = EhLibUtils.MouseButtonsAsyn;
      Point pos = Cursor.Position;
      if (mouseButtonsAsyn == MouseButtons.Left && ignoreMouseScreenRect.Contains(pos))
      {
        ignoreMouseScreenRectPressed = true;
        //EhLibUtils.DoNothing();
      }
      else if (!EhLibManager.DefaultEhLibManager.DropDownDebug && dropDownMode)
      {
        ignoreMouseScreenRectPressed = false;
        //Hide();
      }
      Hide();
    }

    protected override void OnVisibleChanged(EventArgs e)
    {
      base.OnVisibleChanged(e);
      if(!Visible && dropDownMode)
        CloseNomodal(DialogResult);
    }

    protected virtual void CloseNomodal(DialogResult dialogResult)
    {
      if (closeHandler != null)
      {
//        DropDownFormCloseHandler closeHandler
        closeHandler(this, new DropDownFormCloseEventArgs(DialogResult, ignoreMouseScreenRectPressed));
      }
    }

    public void ExecuteNomodal(Rectangle hostScreenRect, DropDownAlign align, DropDownFormCloseHandler closeHandler, Control masterControl)
    {
      ExecuteNomodal(hostScreenRect, align, closeHandler, masterControl, new Rectangle(0, 0, -1, -1));
    }
    
    public void ExecuteNomodal(Rectangle hostScreenRect, DropDownAlign align, DropDownFormCloseHandler closeHandler, Control masterControl, Rectangle ignoreMouseScreenRect)
    {
      if (masterControl.RightToLeft == RightToLeft.Yes)
      {
        RightToLeft = RightToLeft.Yes;
        RightToLeftLayout = true;
      }
      else
      {
        RightToLeft = RightToLeft.Inherit;
        RightToLeftLayout = false;
      }

      Visible = false;

      AdjustDropDownForm(hostScreenRect, align, RightToLeft);
      if (PointToScreen(new Point(0, 0)).Y < hostScreenRect.Top)
      {
        sizeGrip.Position = SizeGripPosition.TopRight;
        sizeGrip2.Position = SizeGripPosition.TopLeft;
      }
      else
      {
        sizeGrip.Position = SizeGripPosition.BottomRight;
        sizeGrip2.Position = SizeGripPosition.BottomLeft;
      }

      this.masterControl = masterControl;
      InitElements();

      this.closeHandler = closeHandler;
      DialogResult = DialogResult.None;
      this.ignoreMouseScreenRect = ignoreMouseScreenRect;
      this.ignoreMouseScreenRectPressed = false;

      Visible = true;
    }

    public void Close(DialogResult dialogResult)
    {
      DialogResult = dialogResult;
      Hide();
    }

    protected VerticalDropLayout AdjustDropDownForm(Rectangle hostScreenRect, DropDownAlign align, RightToLeft rtl)
    {
      Rectangle workArea;
      Point hostP;
      VerticalDropLayout result = VerticalDropLayout.UnderControl;
      Rectangle newBounds = Bounds;

      if (rtl == RightToLeft.Yes)
      {
        if (align == DropDownAlign.Left)
          align = DropDownAlign.Right;
        else if (align == DropDownAlign.Right)
          align = DropDownAlign.Left;
      }

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

      hostP = hostScreenRect.Location;

      newBounds.Location = new Point(hostP.X, hostP.Y + (hostScreenRect.Bottom - hostScreenRect.Top) + 1);

      switch (align)
      {
        case DropDownAlign.Right:
          newBounds.X = newBounds.X - (Width - (hostScreenRect.Right - hostScreenRect.Left));
          break;
        case DropDownAlign.Center:
          newBounds.X = newBounds.X - ((Width - (hostScreenRect.Right - hostScreenRect.Left)) / 2);
          break;
      }

      if (newBounds.Width > workArea.Right - workArea.Left)
        newBounds.Width = workArea.Right - workArea.Left;

      if (newBounds.Left + newBounds.Width > workArea.Right)
        newBounds.X = workArea.Right - newBounds.Width;
      if (newBounds.Left < workArea.Left)
        newBounds.X = workArea.Left;

      if (newBounds.Top + newBounds.Height > workArea.Bottom)
      {
        if (hostP.Y - workArea.Top > workArea.Bottom - hostP.Y - (hostScreenRect.Bottom - hostScreenRect.Top))
        {
          result = VerticalDropLayout.AboveControl;
          newBounds.Y = hostP.Y - newBounds.Height;
        }
      }

      if (newBounds.Top < workArea.Top)
      {
        newBounds.Height = newBounds.Height - (workArea.Top - newBounds.Top);
        newBounds.Y = workArea.Top;
      }
      if (newBounds.Top + newBounds.Height > workArea.Bottom)
      {
        newBounds.Height = workArea.Bottom - newBounds.Top;
      }

      Bounds = newBounds;
      return result;

    }

    protected virtual void InitElements()
    {
      sizeGrip.Visible = (DropDownFormElements.RightGrip & FormElements) != 0;
      sizeGrip2.Visible = (DropDownFormElements.LeftGrip & FormElements) != 0;
    }

    protected override void OnLayout(LayoutEventArgs levent)
    {
      base.OnLayout(levent);
      if ((sizeGrip != null) && sizeGrip.Visible)
      {
        sizeGrip.UpdatePosition();
        sizeGrip.BringToFront();
      }
      if ((sizeGrip2 != null) && sizeGrip2.Visible)
      {
        sizeGrip2.UpdatePosition();
        sizeGrip2.BringToFront();
      }
    }

    protected override void OnResize(EventArgs e)
    {
      base.OnResize(e);
    }

    public virtual void DrawFormBorder(Graphics g, Rectangle bounds)
    {
      Rectangle rTop;

      if (!Application.RenderWithVisualStyles)
      {
        bounds.Width -= 1;
        bounds.Height -= 1;
        g.DrawRectangle(new Pen(SystemColors.ControlDarkDark, 1), bounds);
        bounds.Inflate(-1, -1);
        g.DrawRectangle(new Pen(SystemColors.ControlLightLight, 1), bounds);
        bounds.Inflate(-1, -1);
        g.DrawRectangle(new Pen(SystemColors.ControlLightLight, 1), bounds);
      }
      else
      {
        VisualStyleElement element;
        VisualStyleRenderer render;

        rTop = new Rectangle(bounds.Left, bounds.Top, bounds.Width, borderWidth);
        element = VisualStyleElement.Window.SmallCaption.Active;
        render = new VisualStyleRenderer(element);
        render.DrawBackground(g, rTop);

        bounds.Y = bounds.Y + borderWidth;
        bounds.Height = bounds.Height - borderWidth;
        element = VisualStyleElement.Window.SmallFrameBottom.Active;
        render = new VisualStyleRenderer(element);
        render.DrawBackground(g, bounds);
      }
    }

    public void DrawBorder()
    {
      Rectangle r;
      Rectangle clientR;

      using (ControlGraphicsWrapper cgw = new ControlGraphicsWrapper(this))
      {
        r = EhLibUtils.GetControlWindowRect(this);
        r.Offset(-r.Left, -r.Top);
        clientR = new Rectangle(r.Left + borderWidth, r.Top + borderWidth, r.Width - borderWidth*2, r.Height - borderWidth*2);
        cgw.Graphics.SetClip(clientR, CombineMode.Exclude);
        DrawFormBorder(cgw.Graphics, r);
      }
    }

    protected override void WndProc(ref Message m)
    {
      switch (m.Msg)
      {
        case NativeMethods.WM_NCCALCSIZE:
            WmNCCalcSize(ref m);
          break;
        case NativeMethods.WM_NCPAINT:
          WmNCPaint(ref m);
          break;
        default:
          base.WndProc(ref m);
          break;
      }
    }

    private void AdjustClientRect(ref NativeMethods.RECT rcClient)
    {
      rcClient.left += borderWidth;
      rcClient.top += borderWidth;
      rcClient.right -= borderWidth;
      rcClient.bottom -= borderWidth;
    }

    private void WmNCPaint(ref Message m)
    {
      if (dropDownMode && DropDownBorder)
        DrawBorder();
      else
        base.WndProc(ref m);
    }

    private void WmNCCalcSize(ref Message m)
    {
      base.WndProc(ref m);
      if ((dropDownMode) && (!DesignMode) && DropDownBorder)
      {
        if (m.WParam != IntPtr.Zero)
        {
          NativeMethods.NCCALCSIZE_PARAMS rcsize = (NativeMethods.NCCALCSIZE_PARAMS)Marshal.PtrToStructure(m.LParam, typeof(NativeMethods.NCCALCSIZE_PARAMS));
          AdjustClientRect(ref rcsize.rcNewWindow);
          Marshal.StructureToPtr(rcsize, m.LParam, false);
        }
        else
        {
          NativeMethods.RECT rcsize = (NativeMethods.RECT)Marshal.PtrToStructure(m.LParam, typeof(NativeMethods.RECT));
          AdjustClientRect(ref rcsize);
          Marshal.StructureToPtr(rcsize, m.LParam, false);
        }
        m.Result = new IntPtr(1);
      }
    }

    #endregion DropDownForm methods

    private void DropDownForm_Deactivate(object sender, EventArgs e)
    {

    }

  }

}
