using DevComponents.DotNetBar;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace DevComponents.DotNetBar.Controls
{
    /// 
    /// Component to display flyout popup.
    /// 
    [ToolboxBitmap(typeof(Flyout), "Controls.Flyout.ico")]
    [ToolboxItem(true), Designer("DevComponents.DotNetBar.Design.FlyoutDesigner, DevComponents.DotNetBar.Design, Version=14.1.0.37, Culture=neutral,  PublicKeyToken=90f470f34c89ccaf")]
    [DefaultEvent("PrepareContent")]
    public partial class Flyout : Component, IMessageHandlerClient
    {
        #region Constructor
        public Flyout()
        {
            InitializeComponent();
        }
        public Flyout(IContainer container)
        {
            container.Add(this);
            InitializeComponent();
        }
        #endregion
        /// 
        /// Required designer variable.
        /// 
        private System.ComponentModel.IContainer components = null;
        ///  
        /// Clean up any resources being used.
        /// 
        /// true if managed resources should be disposed; otherwise, false.
        protected override void Dispose(bool disposing)
        {
            Close();
            if (_MessageHandlerInstalled)
            {
                MessageHandler.UnregisterMessageClient(this);
                _MessageHandlerInstalled = false;
            }
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }
        #region Component Designer generated code
        /// 
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// 
        private void InitializeComponent()
        {
            components = new System.ComponentModel.Container();
        }
        #endregion
        #region Implementation
        private bool _DropShadow;
        private bool _TopMost;
        private Control _Content = null;
        /// 
        /// Indicates a control, usually panel with other controls inside of it, that is displayed on the flyout popup.
        /// 
        [DefaultValue(null), Category("Appearance"), Description("Indicates a control, usually panel with other controls inside of it, that is displayed on the flyout popup.")]
        public Control Content
        {
            get { return _Content; }
            set
            {
                if (value != _Content)
                {
                    Control oldValue = _Content;
                    _Content = value;
                    OnContentChanged(oldValue, value);
                }
            }
        }
        /// 
        /// Called when Content property has changed.
        /// 
        /// Old property value
        /// New property value
        protected virtual void OnContentChanged(Control oldValue, Control newValue)
        {
            //OnPropertyChanged(new PropertyChangedEventArgs("Content"));
        }
        private ePointerSide _PointerSide = ePointerSide.Bottom;
        /// 
        /// Indicates the side of the flyout triangle pointer is displayed on.
        /// 
        [DefaultValue(ePointerSide.Bottom), Category("Appearance"), Description("Indicates the side of the flyout triangle pointer is displayed on.")]
        public ePointerSide PointerSide
        {
            get { return _PointerSide; }
            set
            {
                if (value != _PointerSide)
                {
                    ePointerSide oldValue = _PointerSide;
                    _PointerSide = value;
                    OnPointerSideChanged(oldValue, value);
                }
            }
        }
        /// 
        /// Called when PointerSide property has changed.
        /// 
        /// Old property value
        /// New property value
        protected virtual void OnPointerSideChanged(ePointerSide oldValue, ePointerSide newValue)
        {
            //OnPropertyChanged(new PropertyChangedEventArgs("PointerSide"));
        }
        private Control _TargetControl = null;
        /// 
        /// Indicates the target control for the flyout display and positioning.
        /// 
        [DefaultValue(null), Category("Behavior"), Description("Indicates the target control for the flyout display and positioning.")]
        public Control TargetControl
        {
            get { return _TargetControl; }
            set
            {
                if (value != _TargetControl)
                {
                    Control oldValue = _TargetControl;
                    _TargetControl = value;
                    OnTargetControlChanged(oldValue, value);
                }
            }
        }
        /// 
        /// Called when TargetControl property has changed.
        /// 
        /// Old property value
        /// New property value
        protected virtual void OnTargetControlChanged(Control oldValue, Control newValue)
        {
            //OnPropertyChanged(new PropertyChangedEventArgs("TargetControl"));
            if (!this.DesignMode)
            {
                DetachFromControl(oldValue, _DisplayMode, _DeepIntegration);
                AttachToControl(newValue, _DisplayMode, _DeepIntegration);
            }
        }
        private void AttachToControl(Control c, eFlyoutDisplayMode displayMode, bool deepIntegration)
        {
            if (c == null) return;
            if (displayMode == eFlyoutDisplayMode.MouseClick)
            {
                if (deepIntegration && c is SuperTabControl)
                {
                    SuperTabControl tab = (SuperTabControl)c;
                    tab.TabStrip.ItemClick += TabStripMouseEvent;
                }
                else if (deepIntegration && c is AdvTree.AdvTree)
                {
                    AdvTree.AdvTree tree = (AdvTree.AdvTree)c;
                    tree.NodeClick += TreeMouseEvent;
                }
                else if (deepIntegration && c is TokenEditor)
                {
                    TokenEditor token = (TokenEditor)c;
                    token.TokenMouseClick += TokenMouseEvent;
                }
                else if (deepIntegration && c is TabStrip)
                {
                    TabStrip strip = (TabStrip)c;
                    strip.TabMouseClick += TabItemMouseEvent;
                }
                else if (deepIntegration && c is TabControl)
                {
                    TabControl strip = (TabControl)c;
                    strip.TabStrip.TabMouseClick += TabItemMouseEvent;
                }
                else if (deepIntegration && c is Bar)
                {
                    Bar bar = (Bar)c;
                    bar.ItemClick += BaseItemMouseEvent;
                }
                else
                    c.MouseClick += TargetMouseClick;
            }
            else if (displayMode == eFlyoutDisplayMode.MouseHover)
            {
                if (deepIntegration && c is SuperTabControl)
                {
                    SuperTabControl tab = (SuperTabControl)c;
                    tab.TabStrip.MouseHover += TabStripMouseEvent;
                    tab.TabStrip.MouseLeave += TargetMouseLeave;
                }
                else if (deepIntegration && c is AdvTree.AdvTree)
                {
                    AdvTree.AdvTree tree = (AdvTree.AdvTree)c;
                    tree.NodeMouseHover += TreeMouseEvent;
                    tree.NodeMouseLeave += TargetMouseLeave;
                }
                else if (deepIntegration && c is TokenEditor)
                {
                    TokenEditor token = (TokenEditor)c;
                    token.TokenMouseHover += TokenMouseEvent;
                    token.TokenMouseLeave += TargetMouseLeave;
                }
                else if (deepIntegration && c is TabStrip)
                {
                    TabStrip tab = (TabStrip)c;
                    tab.TabMouseHover += TabItemMouseEvent;
                    tab.TabMouseLeave += TargetMouseLeave;
                }
                else if (deepIntegration && c is TabControl)
                {
                    TabControl tab = (TabControl)c;
                    tab.TabStrip.TabMouseHover += TabItemMouseEvent;
                    tab.TabStrip.TabMouseLeave += TargetMouseLeave;
                }
                else if (deepIntegration && c is Bar)
                {
                    Bar bar = (Bar)c;
                    bar.MouseHover += BaseItemMouseEvent;
                    bar.MouseLeave += TargetMouseLeave;
                }
                else
                    c.MouseHover += TargetMouseHover;
            }
            else if (displayMode == eFlyoutDisplayMode.MouseOver)
            {
                if (deepIntegration && c is SuperTabControl)
                {
                    SuperTabControl tab = (SuperTabControl)c;
                    tab.TabStrip.MouseEnter += TabStripMouseEvent;
                    tab.TabStrip.MouseLeave += TargetMouseLeave;
                }
                else if (deepIntegration && c is AdvTree.AdvTree)
                {
                    AdvTree.AdvTree tree = (AdvTree.AdvTree)c;
                    tree.NodeMouseEnter += TreeMouseEvent;
                    tree.NodeMouseLeave += TargetMouseLeave;
                }
                else if (deepIntegration && c is TokenEditor)
                {
                    TokenEditor token = (TokenEditor)c;
                    token.TokenMouseEnter += TokenMouseEvent;
                    token.TokenMouseLeave += TargetMouseLeave;
                }
                else if (deepIntegration && c is TabStrip)
                {
                    TabStrip tab = (TabStrip)c;
                    tab.TabMouseEnter += TabItemMouseEvent;
                    tab.TabMouseLeave += TargetMouseLeave;
                }
                else if (deepIntegration && c is TabControl)
                {
                    TabControl tab = (TabControl)c;
                    tab.TabStrip.TabMouseEnter += TabItemMouseEvent;
                    tab.TabStrip.TabMouseLeave += TargetMouseLeave;
                }
                else if (deepIntegration && c is Bar)
                {
                    Bar bar = (Bar)c;
                    bar.MouseMove += BaseItemMouseEvent;
                    bar.MouseLeave += TargetMouseLeave;
                }
                else
                    c.MouseEnter += TargetMouseEnter;
            }
            c.Leave += TargetLeaveFocus;
            c.VisibleChanged += TargetVisibleChanged;
        }
        private void DetachFromControl(Control c, eFlyoutDisplayMode displayMode, bool deepIntegration)
        {
            if (c == null) return;
            if (displayMode == eFlyoutDisplayMode.MouseClick)
            {
                if (deepIntegration && c is SuperTabControl)
                {
                    SuperTabControl tab = (SuperTabControl)c;
                    tab.TabStrip.ItemClick -= TabStripMouseEvent;
                }
                else if (deepIntegration && c is AdvTree.AdvTree)
                {
                    AdvTree.AdvTree tree = (AdvTree.AdvTree)c;
                    tree.NodeClick -= TreeMouseEvent;
                }
                else if (deepIntegration && c is TokenEditor)
                {
                    TokenEditor token = (TokenEditor)c;
                    token.TokenMouseClick -= TokenMouseEvent;
                }
                else if (deepIntegration && c is TabStrip)
                {
                    TabStrip strip = (TabStrip)c;
                    strip.TabMouseClick -= TabItemMouseEvent;
                }
                else if (deepIntegration && c is TabControl)
                {
                    TabControl strip = (TabControl)c;
                    strip.TabStrip.TabMouseClick -= TabItemMouseEvent;
                }
                else if (deepIntegration && c is Bar)
                {
                    Bar bar = (Bar)c;
                    bar.ItemClick -= BaseItemMouseEvent;
                }
                else
                    c.MouseClick -= TargetMouseClick;
            }
            else if (displayMode == eFlyoutDisplayMode.MouseHover)
            {
                if (deepIntegration && c is SuperTabControl)
                {
                    SuperTabControl tab = (SuperTabControl)c;
                    tab.TabStrip.MouseHover -= TabStripMouseEvent;
                    tab.TabStrip.MouseLeave -= TargetMouseLeave;
                }
                else if (deepIntegration && c is AdvTree.AdvTree)
                {
                    AdvTree.AdvTree tree = (AdvTree.AdvTree)c;
                    tree.NodeMouseHover -= TreeMouseEvent;
                    tree.NodeMouseLeave -= TargetMouseLeave;
                }
                else if (deepIntegration && c is TokenEditor)
                {
                    TokenEditor token = (TokenEditor)c;
                    token.TokenMouseHover -= TokenMouseEvent;
                    token.TokenMouseLeave -= TargetMouseLeave;
                }
                else if (deepIntegration && c is TabStrip)
                {
                    TabStrip tab = (TabStrip)c;
                    tab.TabMouseHover -= TabItemMouseEvent;
                    tab.TabMouseLeave -= TargetMouseLeave;
                }
                else if (deepIntegration && c is TabControl)
                {
                    TabControl tab = (TabControl)c;
                    tab.TabStrip.TabMouseHover -= TabItemMouseEvent;
                    tab.TabStrip.TabMouseLeave -= TargetMouseLeave;
                }
                else if (deepIntegration && c is Bar)
                {
                    Bar bar = (Bar)c;
                    bar.MouseHover -= BaseItemMouseEvent;
                    bar.MouseLeave -= TargetMouseLeave;
                }
                else
                    c.MouseHover -= TargetMouseHover;
            }
            else if (displayMode == eFlyoutDisplayMode.MouseOver)
            {
                if (deepIntegration && c is SuperTabControl)
                {
                    SuperTabControl tab = (SuperTabControl)c;
                    tab.TabStrip.MouseEnter -= TabStripMouseEvent;
                    tab.TabStrip.MouseLeave -= TargetMouseLeave;
                }
                else if (deepIntegration && c is AdvTree.AdvTree)
                {
                    AdvTree.AdvTree tree = (AdvTree.AdvTree)c;
                    tree.NodeMouseEnter -= TreeMouseEvent;
                    tree.NodeMouseLeave -= TargetMouseLeave;
                }
                else if (deepIntegration && c is TokenEditor)
                {
                    TokenEditor token = (TokenEditor)c;
                    token.TokenMouseEnter -= TokenMouseEvent;
                    token.TokenMouseLeave -= TargetMouseLeave;
                }
                else if (deepIntegration && c is TabStrip)
                {
                    TabStrip tab = (TabStrip)c;
                    tab.TabMouseEnter -= TabItemMouseEvent;
                    tab.TabMouseLeave -= TargetMouseLeave;
                }
                else if (deepIntegration && c is TabControl)
                {
                    TabControl tab = (TabControl)c;
                    tab.TabStrip.TabMouseEnter -= TabItemMouseEvent;
                    tab.TabStrip.TabMouseLeave -= TargetMouseLeave;
                }
                else if (deepIntegration && c is Bar)
                {
                    Bar bar = (Bar)c;
                    bar.MouseEnter -= BaseItemMouseEvent;
                    bar.MouseLeave -= TargetMouseLeave;
                }
                else
                    c.MouseEnter -= TargetMouseEnter;
            }
            c.Leave -= TargetLeaveFocus;
            c.VisibleChanged -= TargetVisibleChanged;
        }
        private bool _CloseDelayed = false;
        private void TargetMouseLeave(object sender, EventArgs e)
        {
            if (_IsFlyoutFormShown)
            {
                _CloseDelayed = true;
                BarUtilities.InvokeDelayed(new MethodInvoker(delegate { CloseFlyoutDelayed(); }), 1000);
                //Close();
            }
        }
        private void CloseFlyoutDelayed()
        {
            if (!_CloseDelayed) return;
            _CloseDelayed = false;
            if (_IsFlyoutFormShown && _Flyout!=null && !_Flyout.Bounds.Contains(Control.MousePosition) && 
                _TargetControl!=null && !(new Rectangle(_TargetControl.PointToScreen(_TargetControl.Location), _TargetControl.Size).Contains(Control.MousePosition)))
            {
                Close();
            }
        }
        private void TokenMouseEvent(object sender, EventArgs e)
        {
            if (_IsFlyoutFormShown)
                Close();
            Show(sender);
        }
        private void TreeMouseEvent(object sender, AdvTree.TreeNodeMouseEventArgs e)
        {
            if (_IsFlyoutFormShown)
                Close();
            Show(e.Node);
        }
        private void TabItemMouseEvent(object sender, EventArgs e)
        {
            if (sender is TabItem)
            {
                if (_IsFlyoutFormShown)
                    Close();
                if (!_IsFlyoutFormShown)
                {
                    Show(sender);
                }
            }
            else
                Close();
        }
        private void BaseItemMouseEvent(object sender, EventArgs e)
        {
            if (sender is BaseItem)
            {
                if (_IsFlyoutFormShown)
                    Close();
                if (!_IsFlyoutFormShown)
                {
                    Show(sender);
                }
            }
            else if (sender is Bar)
            {
                Bar bar = (Bar)sender;
                Point p = bar.PointToClient(Control.MousePosition);
                BaseItem item = bar.ItemsContainer.ItemAtLocation(p.X, p.Y);
                if (item != null)
                {
                    if (_TargetItem != null && _TargetItem.IsAlive && _TargetItem.Target == item)
                        return;
                    
                    if (_IsFlyoutFormShown)
                        Close();
                    
                    if (!_IsFlyoutFormShown)
                    {
                        Show(item);
                    }
                }
                else
                    Close();
            }
            else
                Close();
        }
        private void TabStripMouseEvent(object sender, EventArgs e)
        {
            if (sender is SuperTabItem)
            {
                if (_IsFlyoutFormShown)
                    Close();
                if (!_IsFlyoutFormShown)
                {
                    Show(sender);
                }
            }
            else
                Close();
        }
        private bool _DeepIntegration = true;
        /// 
        /// Indicates whether Flyout integrates on item level with DotNetBar controls it recognizes like SuperTabControl, AdvTree etc.
        /// 
        [DefaultValue(true), Category("Behavior"), Description("Indicates whether Flyout integrates on item level with DotNetBar controls it recognizes like SuperTabControl, AdvTree etc.")]
        public bool DeepIntegration
        {
            get { return _DeepIntegration; }
            set
            {
                if (value != _DeepIntegration)
                {
                    bool oldValue = _DeepIntegration;
                    _DeepIntegration = value;
                    OnDeepControlIntegrationChanged(oldValue, value);
                }
            }
        }
        /// 
        /// Called when DeepControlIntegration property has changed.
        /// 
        /// Old property value
        /// New property value
        protected virtual void OnDeepControlIntegrationChanged(bool oldValue, bool newValue)
        {
            //OnPropertyChanged(new PropertyChangedEventArgs("DeepControlIntegration"));
            DetachFromControl(_TargetControl, _DisplayMode, oldValue);
            AttachToControl(_TargetControl, _DisplayMode, newValue);
        }
        private void TargetVisibleChanged(object sender, EventArgs e)
        {
            if ((_CloseMode & eFlyoutCloseMode.TargetControlHidden) == eFlyoutCloseMode.TargetControlHidden && _TargetControl != null && !_TargetControl.Visible)
                Close();
        }
        void TargetLeaveFocus(object sender, EventArgs e)
        {
            if ((_CloseMode & eFlyoutCloseMode.TargetControlLostFocus) == eFlyoutCloseMode.TargetControlLostFocus)
                Close();
        }
        void TargetMouseEnter(object sender, EventArgs e)
        {
            if (!_IsFlyoutFormShown)
                Show();
        }
        void TargetMouseHover(object sender, EventArgs e)
        {
            if (!_IsFlyoutFormShown)
                Show();
        }
        private void TargetMouseClick(object sender, MouseEventArgs e)
        {
            if (!_IsFlyoutFormShown)
                Show();
        }
        private eFlyoutDisplayMode _DisplayMode = eFlyoutDisplayMode.MouseOver;
        /// 
        /// Specifies when the flyout is displayed.
        /// 
        [DefaultValue(eFlyoutDisplayMode.MouseOver), Category("Behavior"), Description("Specifies when the flyout is displayed.")]
        public eFlyoutDisplayMode DisplayMode
        {
            get { return _DisplayMode; }
            set
            {
                if (value != _DisplayMode)
                {
                    eFlyoutDisplayMode oldValue = _DisplayMode;
                    _DisplayMode = value;
                    OnDisplayModeChanged(oldValue, value);
                }
            }
        }
        /// 
        /// Called when DisplayMode property has changed.
        /// 
        /// Old property value
        /// New property value
        protected virtual void OnDisplayModeChanged(eFlyoutDisplayMode oldValue, eFlyoutDisplayMode newValue)
        {
            //OnPropertyChanged(new PropertyChangedEventArgs("DisplayMode"));
            DetachFromControl(_TargetControl, oldValue, _DeepIntegration);
            AttachToControl(_TargetControl, newValue, _DeepIntegration);
        }
        private eFlyoutCloseMode _CloseMode = eFlyoutCloseMode.ClickOutside | eFlyoutCloseMode.ParentFormDeactivate;
        /// 
        /// Indicates when Flyout is automatically closed.
        /// 
        [DefaultValue(eFlyoutCloseMode.ClickOutside | eFlyoutCloseMode.ParentFormDeactivate), Category("Behavior"), Description("Indicates when Flyout is automatically closed.")]
        public eFlyoutCloseMode CloseMode
        {
            get { return _CloseMode; }
            set
            {
                if (value != _CloseMode)
                {
                    eFlyoutCloseMode oldValue = _CloseMode;
                    _CloseMode = value;
                    OnCloseModeChanged(oldValue, value);
                }
            }
        }
        /// 
        /// Called when CloseMode property has changed.
        /// 
        /// Old property value
        /// New property value
        protected virtual void OnCloseModeChanged(eFlyoutCloseMode oldValue, eFlyoutCloseMode newValue)
        {
            //OnPropertyChanged(new PropertyChangedEventArgs("CloseMode"));
        }
        private Rectangle GetTargetBounds(object targetItem)
        {
            if (targetItem is BaseItem)
            {
                BaseItem item = (BaseItem)targetItem;
                return new Rectangle(_TargetControl.PointToScreen(item.Bounds.Location), item.Bounds.Size);
            }
            else if (targetItem is DevComponents.AdvTree.Node)
            {
                DevComponents.AdvTree.Node node = (DevComponents.AdvTree.Node)targetItem;
                return new Rectangle(_TargetControl.PointToScreen(node.Bounds.Location), node.Bounds.Size);
            }
            else if(targetItem is EditToken)
            {
                EditToken token = (EditToken)targetItem;
                return new Rectangle(_TargetControl.PointToScreen(token.Bounds.Location), token.Bounds.Size);
            }
            else if (targetItem is TabItem)
            {
                TabItem tab = (TabItem)targetItem;
                return new Rectangle(_TargetControl.PointToScreen(tab.DisplayRectangle.Location), tab.DisplayRectangle.Size);
            }
            return new Rectangle(_TargetControl.PointToScreen(Point.Empty), _TargetControl.Size);
        }
        /// 
        /// Occurs before the flyout is shown for specific target and allows you to prepare Content for it. Sender of event will be the targeted control or item.
        /// 
        [Description("Occurs before the flyout is shown for specific target and allows you to prepare Content for it. Sender of event will be the targeted control or item.")]
        public event EventHandler PrepareContent;
        /// 
        /// Raises PrepareContent event.
        /// 
        /// Provides event arguments.
        protected virtual void OnPrepareContent(object sender, EventArgs e)
        {
            EventHandler handler = PrepareContent;
            if (handler != null)
                handler(sender, e);
        }
        public virtual void Show(object targetItem)
        {
            OnPrepareContent(targetItem, EventArgs.Empty);
            Rectangle r = new Rectangle();
            ePointerSide pointerSide = _PointerSide;
            int pointerOffset = 10;
            if (_TargetControl != null)
            {
                Rectangle targetBounds = GetTargetBounds(targetItem ?? _TargetControl);
                ScreenInformation si = BarFunctions.ScreenFromControl(_TargetControl);
                if (pointerSide == ePointerSide.Top)
                {
                    // Displaying callout below the control
                    r.Size = GetFlyoutFormSize();
                    if (targetBounds.Bottom + r.Height > si.WorkingArea.Bottom)
                    {
                        // Move flyout above the control
                        pointerSide = ePointerSide.Bottom;
                        r.Location = new Point(targetBounds.X, targetBounds.Y - r.Height);
                    }
                    else
                        r.Location = new Point(targetBounds.X, targetBounds.Bottom);
                    if (targetBounds.Width > r.Width)
                        r.X += (targetBounds.Width - r.Width) / 2;
                    else if (targetBounds.Width < r.Width)
                        r.X -= (r.Width - targetBounds.Width) / 2;
                    if (r.X < si.WorkingArea.X)
                        r.X = si.WorkingArea.X;
                    else if (r.Right > si.WorkingArea.Right)
                        r.X = si.WorkingArea.Right - r.Width;
                    Rectangle intersect = Rectangle.Intersect(r, new Rectangle(targetBounds.X, r.Y, targetBounds.Width, r.Height));
                    if (intersect.IsEmpty)
                        pointerOffset = (Math.Min(targetBounds.Width, r.Width) - FlyoutForm.PointerSize.Width) / 2;
                    else
                        pointerOffset = Math.Abs(intersect.X - r.X) + (intersect.Width - FlyoutForm.PointerSize.Width) / 2;
                }
                else if (pointerSide == ePointerSide.Bottom)
                {
                    // Displaying callout above the control
                    r.Size = GetFlyoutFormSize();
                    if (targetBounds.Y - r.Height < si.WorkingArea.Y)
                    {
                        // Move flyout below the control
                        pointerSide = ePointerSide.Top;
                        r.Location = new Point(targetBounds.X, targetBounds.Bottom);
                    }
                    else
                        r.Location = new Point(targetBounds.X, targetBounds.Y - r.Height);
                    if (targetBounds.Width > r.Width)
                        r.X += (targetBounds.Width - r.Width) / 2;
                    else if (targetBounds.Width < r.Width)
                        r.X -= (r.Width - targetBounds.Width) / 2;
                    if (r.X < si.WorkingArea.X)
                        r.X = si.WorkingArea.X;
                    else if (r.Right > si.WorkingArea.Right)
                        r.X = si.WorkingArea.Right - r.Width;
                    Rectangle intersect = Rectangle.Intersect(r, new Rectangle(targetBounds.X, r.Y, targetBounds.Width, r.Height));
                    if (intersect.IsEmpty)
                        pointerOffset = (Math.Min(targetBounds.Width, r.Width) - FlyoutForm.PointerSize.Width) / 2;
                    else
                        pointerOffset = Math.Abs(intersect.X - r.X) + (intersect.Width - FlyoutForm.PointerSize.Width) / 2;
                }
                else if (pointerSide == ePointerSide.Left)
                {
                    // Displaying callout to the right of the target control
                    r.Size = GetFlyoutFormSize();
                    if (targetBounds.Right + r.Width > si.WorkingArea.Right)
                    {
                        // Move flyout to the left of the control
                        pointerSide = ePointerSide.Right;
                        r.Location = new Point(targetBounds.X - r.Width, targetBounds.Y);
                    }
                    else
                        r.Location = new Point(targetBounds.Right, targetBounds.Y);
                    if (targetBounds.Height > r.Height)
                        r.Y += (targetBounds.Height - r.Height) / 2;
                    pointerOffset = (Math.Min(targetBounds.Height, r.Height) - FlyoutForm.PointerSize.Width) / 2;
                }
                else if (pointerSide == ePointerSide.Right)
                {
                    // Displaying callout to the Left of the target control
                    r.Size = GetFlyoutFormSize();
                    if (targetBounds.X - r.Width < si.WorkingArea.X)
                    {
                        // Move flyout to the right of the control
                        pointerSide = ePointerSide.Left;
                        r.Location = new Point(targetBounds.Right, targetBounds.Y);
                    }
                    else
                        r.Location = new Point(targetBounds.X - r.Width, targetBounds.Y);
                    if (targetBounds.Height > r.Height)
                        r.Y += (targetBounds.Height - r.Height) / 2;
                    pointerOffset = (Math.Min(targetBounds.Height, r.Height) - FlyoutForm.PointerSize.Width) / 2;
                }
            }
            Show(r, pointerSide, pointerOffset, targetItem);
        }
        /// 
        /// Shows flyout with the Content. 
        /// 
        public virtual void Show()
        {
            Show(_TargetControl);
        }
        /// 
        /// Returns the flyout size based on Content size.
        /// 
        /// Proposed flyout size.
        public virtual Size GetFlyoutFormSize()
        {
            if (_Content != null)
            {
                Size size = _Content.Size;
                if (_PointerSide == ePointerSide.Bottom || _PointerSide == ePointerSide.Top)
                {
                    size.Height += FlyoutForm.PointerSize.Height + 4;
                    size.Width += 2;
                }
                else
                {
                    size.Width += FlyoutForm.PointerSize.Height + 4;
                    size.Height += 2;
                }
                return size;
            }
            return new Size(100, 100);
        }
        /// 
        /// Shows flyout at specified location and with specified size. Size can be empty (0,0) and flyout will be automatically sized based on the content.
        /// 
        /// 
        public virtual void Show(Rectangle screenFlyoutBounds)
        {
            Show(screenFlyoutBounds, _PointerSide, 10, _TargetControl);
        }
        [Description("Occurs before the flyout form is shown and allows you to cancel the showing.")]
        public event FlyoutShowingEventHandler FlyoutShowing;
        /// 
        /// Raises FlyoutShowing event.
        /// 
        /// Provides event arguments.
        protected virtual void OnFlyoutShowing(FlyoutShowingEventArgs e)
        {
            FlyoutShowingEventHandler handler = FlyoutShowing;
            if (handler != null)
                handler(this, e);
        }
        /// 
        /// Occurs after flyout has been shown.
        /// 
        [Description("Occurs after flyout has been shown.")]
        public event EventHandler FlyoutShown;
        /// 
        /// Raises FlyoutShown event.
        /// 
        /// Provides event arguments.
        protected virtual void OnFlyoutShown(EventArgs e)
        {
            EventHandler handler = FlyoutShown;
            if (handler != null)
                handler(this, e);
        }
        /// 
        /// Provides opportunity to cancel showing of the flyout before any objects are created and allocated. This is preferred event to cancel flyout showing.
        /// 
        [Description("Provides opportunity to cancel showing of the flyout before any objects are created and allocated. This is preferred event to cancel flyout showing.")]
        public event CancelEventHandler QueryShowFlyout;
        /// 
        /// Raises QueryShowFlyout event.
        /// 
        /// Provides event arguments.
        protected virtual void OnQueryShowFlyout(object sender, CancelEventArgs e)
        {
            CancelEventHandler handler = QueryShowFlyout;
            if (handler != null)
                handler(sender, e);
        }
        private bool _IsFlyoutFormShown = false;
        private Control _OldContentParent = null;
        private WeakReference _TargetItem = null;
        private FlyoutForm _Flyout = null;
        /// 
        /// Gets reference to active FlyoutForm or return null/nothing if flyout is not currently shown.
        /// 
        [Browsable(false)]
        public FlyoutForm FlyoutForm
        {
            get { return _Flyout; }
        }
        /// 
        /// Shows flyout at specified location and with specified size. Size can be empty (0,0) and flyout will be automatically sized based on the content.
        /// 
        /// Screen bounds to display flyout at.
        /// Side of the flyout which will have pointer triangle
        /// Pointer position either x or y depending on which side pointer is displayed on.
        /// Target item for the flyout.
        public virtual void Show(Rectangle screenFlyoutBounds, ePointerSide pointerSide, int pointerOffset, object targetItem)
        {
            _CloseDelayed = false;
            if (_IsFlyoutFormShown)
            {
                if (_Flyout != null) _Flyout.Activate();
                return;
            }
            CancelEventArgs cancelShow = new CancelEventArgs();
            OnQueryShowFlyout(targetItem??_TargetControl,cancelShow);
            if (cancelShow.Cancel) return;
            FlyoutForm form = new FlyoutForm();
            if (!_BorderColor.IsEmpty)
                form.BorderColor = _BorderColor;
            if (!_BackColor.IsEmpty)
                form.BackColor = _BackColor;
            form.PointerSide = pointerSide;
            form.ActivateOnShow = _ActivateOnShow;
            form.TopMost = _TopMost;
            form.DropShadow = _DropShadow;
            form.PointerOffset = pointerOffset;
            if (_Content != null)
            {
                if (screenFlyoutBounds.Size.IsEmpty)
                {
                    screenFlyoutBounds.Size = GetFlyoutFormSize();
                }
                if (_Content.Parent != null)
                {
                    _OldContentParent = _Content.Parent;
                    _OldContentParent.Controls.Remove(_Content);
                }
                form.Controls.Add(_Content);
                _Content.Location = new Point(
                    (pointerSide == ePointerSide.Left) ? FlyoutForm.PointerSize.Height + 1 : 1,
                    (pointerSide == ePointerSide.Top) ? FlyoutForm.PointerSize.Height + 1 : 1);
                _Content.Visible = true;
            }
            form.Size = Size.Empty;
            form.Location = screenFlyoutBounds.Location;
            form.FormClosed += FlyoutFormClosed;
            form.FormClosing += FlyoutFormClosing;
            FlyoutShowingEventArgs eargs = new FlyoutShowingEventArgs(form, targetItem);
            OnFlyoutShowing(eargs);
            if (eargs.Cancel)
            {
                FlyoutCloseCleanup(form);
                form.Close();
                form.Dispose();
                return;
            }
            _IsFlyoutFormShown = true;
            form.Visible = true;
            form.Size = screenFlyoutBounds.Size;
            _Flyout = form;
            _TargetItem = new WeakReference(targetItem);
            OnFlyoutShown(EventArgs.Empty);
            if (!_MessageHandlerInstalled && (_CloseMode & eFlyoutCloseMode.ClickOutside) == eFlyoutCloseMode.ClickOutside)
            {
                MessageHandler.RegisterMessageClient(this);
                _MessageHandlerInstalled = true;
            }
            if (_Parent != null && (_CloseMode & eFlyoutCloseMode.ParentFormDeactivate) == eFlyoutCloseMode.ParentFormDeactivate)
            {
                Form parentForm = null;
                if (_Parent is Form)
                    parentForm = (Form)_Parent;
                else
                    parentForm = _Parent.FindForm();
                if (parentForm != null)
                {
                    parentForm.Deactivate += ParentFormDeactivate;
                    _ParentForm = parentForm;
                }
            }
        }
        /// 
        /// Occurs before flyout is closed and allows you to cancel the closing.
        /// 
        [Description("Occurs before flyout is closed and allows you to cancel the closing.")]
        public event FormClosingEventHandler FlyoutClosing;
        /// 
        /// Raises FlyoutClosing event.
        /// 
        /// Provides event arguments.
        protected virtual void OnFlyoutClosing(FormClosingEventArgs e)
        {
            FormClosingEventHandler handler = FlyoutClosing;
            if (handler != null)
                handler(this, e);
        }
        private void FlyoutFormClosing(object sender, FormClosingEventArgs e)
        {
            OnFlyoutClosing(e);
        }
        /// 
        /// Occurs after flyout is closed.
        /// 
        [Description("Occurs after flyout is closed.")]
        public event FormClosedEventHandler FlyoutClosed;
        /// 
        /// Raises FlyoutClosed event.
        /// 
        /// Provides event arguments.
        protected virtual void OnFlyoutClosed(FormClosedEventArgs e)
        {
            FormClosedEventHandler handler = FlyoutClosed;
            if (handler != null)
                handler(this, e);
        }
        private Form _ParentForm = null;
        void ParentFormDeactivate(object sender, EventArgs e)
        {
            if (_Flyout != null && _Flyout.Bounds.Contains(Control.MousePosition))
                return;
            Close();
        }
        /// 
        /// Closes the flyout form if it was open.
        /// 
        public virtual void Close()
        {
            FlyoutForm flyout = _Flyout;
            if (flyout != null)
            {
                flyout.Close();
                flyout.Dispose();
            }
        }
        private void FlyoutCloseCleanup(FlyoutForm form)
        {
            _TargetItem = null;
            _IsFlyoutFormShown = false;
            form.FormClosed -= FlyoutFormClosed;
            form.Controls.Remove(_Content);
            if (_OldContentParent != null)
            {
                _Content.Visible = false;
                _OldContentParent.Controls.Add(_Content);
                _OldContentParent = null;
            }
            _Flyout = null;
            if (_ParentForm != null)
            {
                _ParentForm.Deactivate -= ParentFormDeactivate;
                _ParentForm = null;
            }
        }
        private void FlyoutFormClosed(object sender, FormClosedEventArgs e)
        {
            FlyoutForm form = (FlyoutForm)sender;
            OnFlyoutClosed(e);
            FlyoutCloseCleanup(form);
        }
        private bool _ActivateOnShow = false;
        /// 
        /// Indicates whether flyout is active/focused when its shown, default value is false.
        /// 
        [DefaultValue(false), Category("Behavior"), Description("Indicates whether flyout is active/focused when its shown, default value is false.")]
        public bool ActivateOnShow
        {
            get { return _ActivateOnShow; }
            set
            {
                if (value != _ActivateOnShow)
                {
                    bool oldValue = _ActivateOnShow;
                    _ActivateOnShow = value;
                    OnActivateOnShowChanged(oldValue, value);
                }
            }
        }
        /// 
        /// Called when ActivateOnShow property has changed.
        /// 
        /// Old property value
        /// New property value
        protected virtual void OnActivateOnShowChanged(bool oldValue, bool newValue)
        {
            //OnPropertyChanged(new PropertyChangedEventArgs("ActivateOnShow"));
        }
        /// 
        /// Indicates whether flyout is made top-most window when shown
        /// 
        [DefaultValue(false), Category("Behavior"), Description("Indicates whether flyout is made top-most window when shown.")]
        public bool TopMost
        {
            get { return _TopMost; }
            set
            {
                if (value != _TopMost)
                {
                    bool oldValue = _TopMost;
                    _TopMost = value;
                    OnTopMostChanged(oldValue, value);
                }
            }
        }
        /// 
        /// Called when TopMost property has changed.
        /// 
        /// Old property value
        /// New property value
        protected virtual void OnTopMostChanged(bool oldValue, bool newValue)
        {
            //OnPropertyChanged(new PropertyChangedEventArgs("TopMost"));
        }
        private Color _BackColor = Color.Empty;
        /// 
        /// Gets or sets the background flyout color. Default value is Color.Empty which indicates that current color scheme will be used.
        /// 
        [Category("Columns"), Description("Indicates background flyout color. Default value is Color.Empty which indicates that current color scheme will be used.")]
        public Color BackColor
        {
            get { return _BackColor; }
            set { _BackColor = value; }
        }
        /// 
        /// Gets whether property should be serialized.
        /// 
        [EditorBrowsable(EditorBrowsableState.Never)]
        public bool ShouldSerializeBackColor()
        {
            return !_BackColor.IsEmpty;
        }
        /// 
        /// Resets property to its default value.
        /// 
        [EditorBrowsable(EditorBrowsableState.Never)]
        public void ResetBackColor()
        {
            this.BackColor = Color.Empty;
        }
        private Color _BorderColor = Color.Empty;
        /// 
        /// Gets or sets the flyout border color. Default value of Color.Empty indicates that color scheme will be used.
        /// 
        [Category("Columns"), Description("Indicates flyout border color. Default value of Color.Empty indicates that color scheme will be used.")]
        public Color BorderColor
        {
            get { return _BorderColor; }
            set { _BorderColor = value; }
        }
        /// 
        /// Gets whether property should be serialized.
        /// 
        [EditorBrowsable(EditorBrowsableState.Never)]
        public bool ShouldSerializeBorderColor()
        {
            return !_BorderColor.IsEmpty;
        }
        /// 
        /// Resets property to its default value.
        /// 
        [EditorBrowsable(EditorBrowsableState.Never)]
        public void ResetBorderColor()
        {
            this.BorderColor = Color.Empty;
        }
        /// 
        /// Indicates whether flyout displays drop shadow.
        /// 
        [DefaultValue(true), Category("Appearance"), Description("Indicates whether flyout displays drop shadow")]
        public bool DropShadow
        {
            get { return _DropShadow; }
            set
            {
                if (value != _DropShadow)
                {
                    bool oldValue = _DropShadow;
                    _DropShadow = value;
                    OnDropShadowChanged(oldValue, value);
                }
            }
        }
        /// 
        /// Called when DropShadow property has changed.
        /// 
        /// Old property value
        /// New property value
        protected virtual void OnDropShadowChanged(bool oldValue, bool newValue)
        {
            //OnPropertyChanged(new PropertyChangedEventArgs("DropShadow"));
        }
        private Control _Parent = null;
        /// 
        /// Gets or sets the parent control for the Flyout. Parent is used to find the parent form so flyout can be closed when form is de-activated.
        /// 
        [Browsable(false), DefaultValue(null)]
        public Control Parent
        {
            get { return _Parent; }
            set
            {
                if (value != _Parent)
                {
                    Control oldValue = _Parent;
                    _Parent = value;
                    OnParentChanged(oldValue, value);
                }
            }
        }
        /// 
        /// Called when Parent property has changed.
        /// 
        /// Old property value
        /// New property value
        protected virtual void OnParentChanged(Control oldValue, Control newValue)
        {
            //OnPropertyChanged(new PropertyChangedEventArgs("Parent"));
        }
        #endregion
        #region IMessageHandlerClient
        private bool _MessageHandlerInstalled = false;
        bool IMessageHandlerClient.OnSysKeyDown(IntPtr hWnd, IntPtr wParam, IntPtr lParam)
        {
            return false;
        }
        bool IMessageHandlerClient.OnSysKeyUp(IntPtr hWnd, IntPtr wParam, IntPtr lParam)
        {
            return false;
        }
        bool IMessageHandlerClient.OnKeyDown(IntPtr hWnd, IntPtr wParam, IntPtr lParam)
        {
            return false;
        }
        bool IMessageHandlerClient.OnMouseDown(IntPtr hWnd, IntPtr wParam, IntPtr lParam)
        {
            if ((_CloseMode & eFlyoutCloseMode.ClickOutside) == eFlyoutCloseMode.ClickOutside)
            {
                FlyoutForm form = _Flyout;
                if (form != null && form.Visible)
                {
                    if (!form.Bounds.Contains(Control.MousePosition))
                    {
                        // Ignore clicks on ComboBox popup
                        string s = NativeFunctions.GetClassName(hWnd);
                        s = s.ToLower();
                        if (s.IndexOf("combolbox") < 0)
                            Close();
                    }
                }
            }
            return false;
        }
        bool IMessageHandlerClient.OnMouseMove(IntPtr hWnd, IntPtr wParam, IntPtr lParam)
        {
            return false;
        }
        bool IMessageHandlerClient.OnMouseWheel(IntPtr hWnd, IntPtr wParam, IntPtr lParam)
        {
            return false;
        }
        bool IMessageHandlerClient.IsModal
        {
            get { return false; }
        }
        #endregion
    }
    /// 
    /// Defines delegate for the FlyoutShowing event.
    /// 
    /// 
    /// 
    public delegate void FlyoutShowingEventHandler(object sender, FlyoutShowingEventArgs e);
    public class FlyoutShowingEventArgs : EventArgs
    {
        /// 
        /// Gets the reference to the flyout form.
        /// 
        public FlyoutForm Flyout;
        /// 
        /// Gets the reference to the flyout target usually TargetControl.
        /// 
        public object Target;
        /// 
        /// Allows you to cancel showing of the flyout by setting this value to true.
        /// 
        public bool Cancel;
        /// 
        /// Initializes a new instance of the FlyoutShowingEventArgs class.
        /// 
        /// 
        /// 
        public FlyoutShowingEventArgs(FlyoutForm flyout, object target)
        {
            Flyout = flyout;
            Target = target;
        }
    }
    /// 
    /// Defines the modes for Flyout display.
    /// 
    public enum eFlyoutDisplayMode
    {
        /// 
        /// Flyout is displayed manually using flyout.Show() method.
        /// 
        Manual,
        /// 
        /// Flyout is displayed when mouse is over TargetControl.
        /// 
        MouseOver,
        /// 
        /// Flyout is displayed when mouse is hovering over TargetControl.
        /// 
        MouseHover,
        /// 
        /// Flyout is displayed when left mouse button is clicked on TargetControl.
        /// 
        MouseClick
    }
    /// 
    /// Defines Flyout closing condition.
    /// 
    [Flags()]
    public enum eFlyoutCloseMode
    {
        /// 
        /// Flyout is closed manually using flyout.Close() method.
        /// 
        Manual,
        /// 
        /// Flyout is closed when user clicks outside of flyout bounds.
        /// 
        ClickOutside,
        /// 
        /// Flyout is closed when TargetControl is hidden.
        /// 
        TargetControlHidden,
        /// 
        /// Flyout is closed when TargetControl loses focus.
        /// 
        TargetControlLostFocus,
        /// 
        /// Flyout is closed when parent forms deactivates.
        /// 
        ParentFormDeactivate
    }
}