// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

// Description: HWND-based Rebar Proxy

using System;
using System.Collections;
using System.Text;
using System.Runtime.InteropServices;
using System.ComponentModel;
using System.Windows.Automation;
using System.Windows.Automation.Provider;
using System.Windows;
using MS.Win32;

namespace MS.Internal.AutomationProxies
{
    class WindowsRebar: ProxyHwnd, IRawElementProviderHwndOverride
    {
        // ------------------------------------------------------
        //
        // Constructors
        //
        // ------------------------------------------------------

        #region Constructors

        WindowsRebar (IntPtr hwnd, ProxyFragment parent, int item)
            : base( hwnd, parent, item )
        {
            _sType = SR.LocalizedControlTypeRebar;
            _fIsContent = false;

            // support for events
            _createOnEvent = new WinEventTracker.ProxyRaiseEvents (RaiseEvents);
        }

        #endregion

        #region Proxy Create

        // Static Create method called by UIAutomation to create this proxy.
        // returns null if unsuccessful
        internal static IRawElementProviderSimple Create(IntPtr hwnd, int idChild, int idObject)
        {
            return Create(hwnd, idChild);
        }

        private static IRawElementProviderSimple Create(IntPtr hwnd, int idChild)
        {
            // Something is wrong if idChild is not zero 
            ArgumentOutOfRangeException.ThrowIfNotEqual(idChild, 0);

            return new WindowsRebar(hwnd, null, idChild);
        }

        // Static Create method called by the event tracker system
        internal static void RaiseEvents (IntPtr hwnd, int eventId, object idProp, int idObject, int idChild)
        {
            if (idObject != NativeMethods.OBJID_VSCROLL && idObject != NativeMethods.OBJID_HSCROLL)
            {
                WindowsRebar wtv = new WindowsRebar (hwnd, null, -1);
                wtv.DispatchEvents (eventId, idProp, idObject, idChild);
            }
        }

        #endregion

        //------------------------------------------------------
        //
        //  Patterns Implementation
        //
        //------------------------------------------------------

        #region ProxyFragment Interface

        // Returns the next sibling element in the raw hierarchy.
        // Peripheral controls have always negative values.
        // Returns null if no next child
        internal override ProxySimple GetNextSibling (ProxySimple child)
        {
            int item = child._item;

            // If the index of the next node would be out of range...
            if (item >= 0 && (item + 1) < Count)
            {
                // return a node to represent the requested item.
                return CreateRebarItem (item + 1);
            }
            return null;
        }

        // Returns the previous sibling element in the raw hierarchy.
        // Peripheral controls have always negative values.
        // Returns null is no previous
        internal override ProxySimple GetPreviousSibling (ProxySimple child)
        {
            // If the index of the previous node would be out of range...
            int item = child._item;
            if (item > 0 && item < Count)
            {
                return CreateRebarItem (item - 1);
            }
            return null;
        }

        // Returns the first child element in the raw hierarchy.
        internal override ProxySimple GetFirstChild ()
        {
            return Count > 0 ? CreateRebarItem (0) : null;
        }

        // Returns the last child element in the raw hierarchy.
        internal override ProxySimple GetLastChild ()
        {
            int count = Count;
            return count > 0 ? CreateRebarItem (count - 1) : null;
        }

        // Returns a Proxy element corresponding to the specified screen coordinates.
        internal override ProxySimple ElementProviderFromPoint (int x, int y)
        {
            int x1 = x;
            int y1 = y;
            NativeMethods.Win32Rect rebarRect = new NativeMethods.Win32Rect ();
            if (!Misc.GetWindowRect(_hwnd, ref rebarRect))
            {
                return null;
            }

            if (x >= rebarRect.left && x <= rebarRect.right &&
            y >= rebarRect.top && y <= rebarRect.bottom)
            {
                x = x - rebarRect.left;
                y = y - rebarRect.top;

                NativeMethods.Win32Point pt = new NativeMethods.Win32Point (x, y);

                int BandID = getRebarBandIDFromPoint (pt);

                if (-1 != BandID)
                {
                    return CreateRebarItem (BandID).ElementProviderFromPoint (x1, y1);
                }

            }
            return null;
        }

        #endregion

        #region IRawElementProviderHwndOverride Interface 

        //------------------------------------------------------
        //
        //  Interface IRawElementProviderHwndOverride
        //
        //------------------------------------------------------
        IRawElementProviderSimple IRawElementProviderHwndOverride.GetOverrideProviderForHwnd (IntPtr hwnd)
        {
            // return the appropriate placeholder for the given hwnd...
            // loop over all the band to find it.
        
            for (RebarBandItem band = (RebarBandItem) GetFirstChild (); band != null; band = (RebarBandItem) GetNextSibling (band))
            {
                if (band.HwndBand == hwnd)
                {
                    return new RebarBandChildOverrideProxy (hwnd, band, band._item);
                }
            }

            // Should never get here
            return null;
        }

        #endregion IRawElementProviderHwndOverride Interface 

        // ------------------------------------------------------
        //
        // Private Methods
        //
        // ------------------------------------------------------        

        #region Private Methods

        // Creates a list item RawElementBase Item
        private RebarBandItem CreateRebarItem (int index)
        {
            return new RebarBandItem (_hwnd, this, index);
        }

        private int Count
        {
            get
            {
                return Misc.ProxySendMessageInt(_hwnd, NativeMethods.RB_GETBANDCOUNT, IntPtr.Zero, IntPtr.Zero);
            }
        }

        private unsafe int getRebarBandIDFromPoint (NativeMethods.Win32Point pt)
        {
            NativeMethods.RB_HITTESTINFO rbHitTestInfo = new NativeMethods.RB_HITTESTINFO ();
            rbHitTestInfo.pt = pt;
            rbHitTestInfo.uFlags = 0;
            rbHitTestInfo.iBand = 0;

            return XSendMessage.XSendGetIndex(_hwnd, NativeMethods.RB_HITTEST, IntPtr.Zero, new IntPtr(&rbHitTestInfo), Marshal.SizeOf(rbHitTestInfo.GetType()));
        }

        #endregion

        //------------------------------------------------------
        //
        //  Private Fields
        //
        //------------------------------------------------------

        #region Private Fields

        private enum CommonControlStyles
        {
//                CCS_TOP = 0x00000001,
//                CCS_NOMOVEY = 0x00000002,
//                CCS_BOTTOM = 0x00000003,
//                CCS_NORESIZE = 0x00000004,
//                CCS_NOPARENTALIGN = 0x00000008,
//                CCS_ADJUSTABLE = 0x00000020,
//                CCS_NODIVIDER = 0x00000040,
            CCS_VERT = 0x00000080,
//                CCS_LEFT = (CCS_VERT | CCS_TOP),
//                CCS_RIGHT = (CCS_VERT | CCS_BOTTOM),
//                CCS_NOMOVEX = (CCS_VERT | CCS_NOMOVEY)
        }

        private const int RBBIM_CHILD = 0x10;

        #endregion

        // ------------------------------------------------------
        //
        //  RebarBandItem Private Class
        //
        //------------------------------------------------------

        #region RebarBandItem

        class RebarBandItem: ProxyFragment, IInvokeProvider
        {
            // ------------------------------------------------------
            //
            // Constructors
            //
            // ------------------------------------------------------

            #region Constructors

            internal RebarBandItem (IntPtr hwnd, ProxyFragment parent, int item)
            : base (hwnd, parent, item)
            {
                // Set the strings to return properly the properties.
                _sType = SR.LocalizedControlTypeRebarBand;
                _fIsContent = false;
            }

            #endregion

            //------------------------------------------------------
            //
            //  Patterns Implementation
            //
            //------------------------------------------------------

            #region ProxySimple

            // Gets the bounding rectangle for this element
            internal override Rect BoundingRectangle
            {
                get
                {
                    return GetBoundingRectangle (_hwnd, _item);
                }
            }

            //Gets the controls help text
            internal override string HelpText
            {
                get
                {
                    IntPtr hwndToolTip = Misc.ProxySendMessage(_hwnd, NativeMethods.RB_GETTOOLTIPS, IntPtr.Zero, IntPtr.Zero);
                    return Misc.GetItemToolTipText(_hwnd, hwndToolTip, _item);
                }
            }

            //Gets the localized name
            internal override string LocalizedName
            {
                get
                {
                    return SR.LocalizedNameWindowsReBarBandItem;
                }
            }

            #endregion

            #region ProxyFragment Interface

            // Returns the next sibling element in the raw hierarchy.
            // Peripheral controls have always negative values.
            // Returns null if no next child
            internal override ProxySimple GetNextSibling (ProxySimple child)
            {
                return null;
            }

            // Returns the previous sibling element in the raw hierarchy.
            // Peripheral controls have always negative values.
            // Returns null is no previous
            internal override ProxySimple GetPreviousSibling (ProxySimple child)
            {
                return null;
            }

            // Returns the first child element in the raw hierarchy.
            internal override ProxySimple GetFirstChild ()
            {
                IntPtr hwndBand = HwndBand;

                if (hwndBand != IntPtr.Zero)
                {
                    return new RebarBandChildOverrideProxy (HwndBand, this, _item);
                }
                return null;
            }

            // Returns the last child element in the raw hierarchy.
            internal override ProxySimple GetLastChild ()
            {
                // By construction, a rebar band can only have one children
                return GetFirstChild ();
            }

            // Returns a Proxy element corresponding to the specified screen coordinates.
            internal override ProxySimple ElementProviderFromPoint (int x, int y)
            {
                IntPtr hwndBand = HwndBand;

                if (hwndBand != IntPtr.Zero && Misc.PtInWindowRect(hwndBand, x, y))
                {
                    return null;
                }

                return this;
            }

            internal override object GetElementProperty(AutomationProperty idProp)
            {
                if (idProp == AutomationElement.IsControlElementProperty)
                {
                    //
                    // The Rebar band should not be in the control view.
                    // 
                    return false;
                }

                return base.GetElementProperty(idProp);
            }

            #endregion

            #region InvokeInteropPattern

            void IInvokeProvider.Invoke ()
            {
                // Make sure that the control is enabled
                if (!SafeNativeMethods.IsWindowEnabled(_hwnd))
                {
                    throw new ElementNotEnabledException();
                }

                Misc.PostMessage(_hwnd, NativeMethods.RB_PUSHCHEVRON, (IntPtr)_item, IntPtr.Zero);
            }

            #endregion

            //------------------------------------------------------
            //
            //  Internal Methods
            //
            //------------------------------------------------------

            #region Internal Methods

            // Returns the bounding rectangle of the control.
            internal static Rect GetBoundingRectangle (IntPtr hwnd, int item)
            {
                NativeMethods.Win32Rect rectW32 = NativeMethods.Win32Rect.Empty;

                unsafe
                {
                    if (!XSendMessage.XSend(hwnd, NativeMethods.RB_GETRECT, new IntPtr(item), new IntPtr(&rectW32), Marshal.SizeOf(rectW32.GetType()), XSendMessage.ErrorValue.Zero))
                    {
                        return Rect.Empty;
                    }
                }

                if (!Misc.MapWindowPoints(hwnd, IntPtr.Zero, ref rectW32, 2))
                {
                    return Rect.Empty;
                }

                // Work around a bug in the common control. Swap the X and Y value for vertical
                // rebar bands 
                if (Misc.IsBitSet(Misc.GetWindowStyle(hwnd), (int)CommonControlStyles.CCS_VERT))
                {
                    return new Rect (rectW32.left, rectW32.top, rectW32.bottom - rectW32.top, rectW32.right - rectW32.left);
                }
                else
                {
                    return rectW32.ToRect(Misc.IsControlRTL(hwnd));
                }
            }

            internal IntPtr HwndBand
            {
                get
                {
                    if (_hwndBand == IntPtr.Zero)
                    {
                        NativeMethods.REBARBANDINFO rebarBandInfo = new NativeMethods.REBARBANDINFO();
                        rebarBandInfo.fMask = RBBIM_CHILD;

                        unsafe
                        {
                            if (XSendMessage.XSend(_hwnd, NativeMethods.RB_GETBANDINFOA, new IntPtr(_item), new IntPtr(&rebarBandInfo), Marshal.SizeOf(rebarBandInfo.GetType()), XSendMessage.ErrorValue.Zero))
                            {
                                _hwndBand = rebarBandInfo.hwndChild;
                            }
                        }
                    }
                    return _hwndBand;
                }
            }

            #endregion

            //------------------------------------------------------
            //
            //  Private Fields
            //
            //------------------------------------------------------

            #region Private Fields

            IntPtr _hwndBand = IntPtr.Zero;

            #endregion

        }

        #endregion

        // ------------------------------------------------------
        //
        //  RebarBandChildOverrideProxy Private Class
        //
        //------------------------------------------------------

        #region RebarBandChildOverrideProxy

        class RebarBandChildOverrideProxy: ProxyHwnd
        {
            // ------------------------------------------------------
            //
            // Constructors
            //
            // ------------------------------------------------------

            #region Constructors

            internal RebarBandChildOverrideProxy (IntPtr hwnd, ProxyFragment parent, int item)
            : base (hwnd, parent, item)
            {
                _fIsContent = false;
            }

            #endregion

            //------------------------------------------------------
            //
            //  Patterns Implementation
            //
            //------------------------------------------------------

            #region ProxySimple Interface

            internal override ProviderOptions ProviderOptions
            {
                get
                {
                    return base.ProviderOptions | ProviderOptions.OverrideProvider;
                }
            }

            // Process all the Logical and Raw Element Properties
            internal override object GetElementProperty (AutomationProperty idProp)
            {
                if (idProp == AutomationElement.IsControlElementProperty)
                {
                    //
                    // The panes under the rebar band should not be in the control view.
                    //

                    // In IE6, the rebar band HWND tree was only one level deep:
                    //
                    // rebar / rebar band / rebar item (Toolbar32)
                    //
                    // In IE7, the HWND tree is the same but the rebar item is 
                    // a window acting as another container, for instance:
                    //
                    // rebar / rebar band / rebar item (FavBandClass) / children (Toolbar32)
                    //

                    // Hide windows that are intermediate containers from the control view
                    Accessible accThis = Accessible.Wrap(this.AccessibleObject);
                    if ((accThis != null) && (accThis.ChildCount == 1))
                    {
                        Accessible accWind = accThis.FirstChild;
                        if ((accWind != null) && (accWind.Role == AccessibleRole.Window))
                        {
                            return false;
                        }
                    }
                }

                // No property should be handled by the override proxy
                // Overrides the ProxySimple implementation.
                return null;
            }

            #endregion
        }

        #endregion
    }
}
