using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using SHDocVw;
using System.Reflection;
using System.Diagnostics;
using System.Drawing;
using System.ComponentModel;
using Microsoft.Win32;

using System.Runtime.InteropServices;

[assembly: AssemblyVersion("1.0.0.0")]

namespace Foo.Deskband.BandObject
{
	/// <summary>
	/// Contains main desk band functionality 
	/// </summary>
	/// <example>
	/// </example>
    public class BandObjectBase : UserControl, IObjectWithSite, IDeskBand, IDeskBand2, IDockingWindow, IOleWindow, IInputObject
	{
        [DllImport("user32.dll")]
        public static extern Int32 GetWindowLong(IntPtr hwnd, int nIndex);

        [DllImport("user32.dll")]
        public static extern Int32 SetWindowLong(IntPtr hwnd, int nIndex, Int32 dwNewLong);

        [DllImport("user32.dll")]
        public static extern bool SetLayeredWindowAttributes(IntPtr hwnd, Int32 crKey, int bAlpha, uint dwFlags);

        public const int GWL_EXSTYLE = -20;
        public const int LWA_COLORKEY = 0x00000001;
        public const int WS_EX_LAYERED = 0x00080000;

        [StructLayout(LayoutKind.Sequential)]
        struct COLORREF
        {
            public byte R;
            public byte G;
            public byte B;
        }

		public BandObjectBase()
		{
			InitializeComponent();
		}

        private void InitializeComponent()
        {
            this.SuspendLayout();
            // 
            // BandObjectBase
            // 
            this.BackColor = System.Drawing.Color.Black;
            this.Margin = new System.Windows.Forms.Padding(0);
            this.Name = "BandObjectBase";
            this.Size = new System.Drawing.Size(24, 24);
            this.MinimumSize = new System.Drawing.Size(24, 24);
            this.MaximumSize = new System.Drawing.Size(24, 24);
            this.ResumeLayout(false);
            this.HandleCreated += new System.EventHandler(Band_HandleCreated);
            this.HandleDestroyed += new System.EventHandler(Band_HandleDestroyed);
        }

        private void Band_HandleCreated(object sender, EventArgs e)
        {
            IntPtr hWnd = this.Handle;
            SetWindowLong(hWnd, GWL_EXSTYLE, GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_LAYERED);
            SetLayeredWindowAttributes(hWnd, 0x00000000, 0, LWA_COLORKEY);            
        }

        private void Band_HandleDestroyed(object sender, EventArgs e)
        {
            
        }

        #region IObjectWithSite

        public virtual void GetSite(ref Guid riid, out Object ppvSite)
        {
            ppvSite = BandObjectSite;
        }

        public virtual void SetSite(Object pUnkSite)
        {
            if (BandObjectSite != null)
                Marshal.ReleaseComObject(BandObjectSite);

            if (Explorer != null)
            {
                Marshal.ReleaseComObject(Explorer);
                Explorer = null;
            }

            BandObjectSite = (IInputObjectSite)pUnkSite;
            if (BandObjectSite != null)
            {
                //pUnkSite is a pointer to object that implements IOleWindowSite or something  similar
                //we need to get access to the top level object - explorer itself
                //to allows this explorer objects also implement IServiceProvider interface
                //(don't mix it with System.IServiceProvider!)
                //we get this interface and ask it to find WebBrowserApp
                _IServiceProvider sp = BandObjectSite as _IServiceProvider;
                Guid guid = ExplorerGUIDs.IID_IWebBrowserApp;
                Guid riid = ExplorerGUIDs.IID_IUnknown;

                try
                {
                    object w;
                    sp.QueryService(
                        ref guid,
                        ref riid,
                        out w);

                    //once we have interface to the COM object we can create RCW from it
                    Explorer = (WebBrowserClass)Marshal.CreateWrapperOfType(
                        w as IWebBrowser,
                        typeof(WebBrowserClass)
                        );

                    OnExplorerAttached(EventArgs.Empty);
                }
                catch (COMException)
                {
                    //we anticipate this exception in case our object instantiated 
                    //as a Desk Band. There is no web browser service available.
                }
            }
        }

        #endregion
      
        #region IDeskband

        public virtual void GetWindow(out System.IntPtr phwnd)
		{		
			phwnd = Handle;
		}

		public virtual void ContextSensitiveHelp(bool fEnterMode){}

        /// <summary>
        /// Called by explorer when band object needs to be showed or hidden.
        /// </summary>
        /// <param name="fShow"></param>
        public virtual void ShowDW(bool fShow)
        {
            if (fShow)
                Show();
            else
                Hide();
        }

        /// <summary>
        /// Called by explorer when window is about to close.
        /// </summary>
        public virtual void CloseDW(UInt32 dwReserved)
        {
            Dispose(true);
        }

        /// <summary>
        /// Not used.
        /// </summary>
        public virtual void ResizeBorderDW(IntPtr prcBorder, Object punkToolbarSite, bool fReserved) { }

        public virtual void GetBandInfo(UInt32 dwBandID, UInt32 dwViewMode, ref DESKBANDINFO dbi)
        {

            if ((dbi.dwMask & DBIM.MINSIZE) != 0)
            {
                dbi.ptMinSize.X = MinSize.Width;
                dbi.ptMinSize.Y = MinSize.Height;
            }

            if ((dbi.dwMask & DBIM.MAXSIZE) != 0)
            {
                dbi.ptMaxSize.X = MaxSize.Width;
                dbi.ptMaxSize.Y = MaxSize.Height;
            }

            if ((dbi.dwMask & DBIM.INTEGRAL) != 0)
            {
                dbi.ptIntegral.X = IntegralSize.Width;
                dbi.ptIntegral.Y = IntegralSize.Height;
            }

            if ((dbi.dwMask & DBIM.ACTUAL) != 0)
            {
                dbi.ptActual.X = Size.Width;
                dbi.ptActual.Y = Size.Height;
            }

            if ((dbi.dwMask & DBIM.TITLE) != 0)
            {
                dbi.wszTitle = Title;
            }            

            //Use the default background color by removing this flag
            dbi.dwMask &= ~DBIM.BKCOLOR;                       

            //COLORREF bkColor;
            //bkColor.R = Color.Black.R;
            //bkColor.G = Color.Black.G;
            //bkColor.B = Color.Black.B;
            //Color.to
            //dbi.crBkgnd = Int32.MaxValue;
            //dbi.crBkgnd = 0xDEDEDE;

            dbi.dwModeFlags = (DBIMF)DBIM.TITLE | (DBIMF)DBIM.ACTUAL | (DBIMF)DBIM.MAXSIZE | (DBIMF)DBIM.MINSIZE | (DBIMF)DBIM.INTEGRAL;
            dbi.dwModeFlags = dbi.dwModeFlags | DBIMF.ALWAYSGRIPPER | DBIMF.VARIABLEHEIGHT | DBIMF.ADDTOFRONT;            
        }

        #endregion

        #region IDeskBand2

        public void CanRenderComposited(ref bool pfCanRenderComposited)
        {
            pfCanRenderComposited = true;
        }

        public void GetCompositionState(ref bool pfCompositionEnabled)
        {
            pfCompositionEnabled = _compositionEnabled;
        }

        public void SetCompositionState(bool fCompositionEnabled)
        {
            _compositionEnabled = fCompositionEnabled;
        }

        #endregion

        #region IInputObject

        /// <summary>
		/// Called explorer when focus has to be chenged.
		/// </summary>
		public virtual void UIActivateIO(Int32 fActivate, ref MSG Msg)
		{
			if( fActivate != 0 ) 
			{
				Control ctrl = GetNextControl(this,true);//first
				if( ModifierKeys == Keys.Shift )
					ctrl = GetNextControl(ctrl,false );//last

				if( ctrl != null ) ctrl.Select();
				this.Focus();
			}
		}

		public virtual Int32 HasFocusIO()
		{
			return this.ContainsFocus ? 0 : 1; //S_OK : S_FALSE;
		}

		/// <summary>
		/// Called by explorer to process keyboard events. Undersatands Tab and F6.
		/// </summary>
		/// <param name="msg"></param>
		/// <returns>S_OK if message was processed, S_FALSE otherwise.</returns>
		public virtual Int32 TranslateAcceleratorIO(ref MSG msg)
		{
			if( msg.message == 0x100 )//WM_KEYDOWN
				if( msg.wParam == (uint)Keys.Tab || msg.wParam == (uint)Keys.F6 )//keys used by explorer to navigate from control to control
					if( SelectNextControl( 
							ActiveControl,
							ModifierKeys == Keys.Shift  ? false : true,
							true,
							true,
							false ) 
						)
						return 0;//S_OK

			return 1;//S_FALSE
        }

        #endregion

        #region EventHandlers

        /// <summary>
		/// Override this method to handle ExplorerAttached event.
		/// </summary>
		/// <param name="ea"></param>
		protected virtual void OnExplorerAttached(EventArgs ea)
		{
			if ( ExplorerAttached != null )
				ExplorerAttached(this, ea);
		}

		/// <summary>
		/// Notifies explorer of focus change.
		/// </summary>
		protected override void OnGotFocus(System.EventArgs e)
		{
			base.OnGotFocus(e);
			BandObjectSite.OnFocusChangeIS(this as IInputObject, 1);		
		}
		/// <summary>
		/// Notifies explorer of focus change.
		/// </summary>
		protected override void OnLostFocus(System.EventArgs e)
		{
			base.OnLostFocus(e);
			if( ActiveControl == null )
				BandObjectSite.OnFocusChangeIS(this as IInputObject, 0);
        }

        #endregion

        #region COM_Bookkeeping

        /// <summary>
		/// Called when derived class is registered as a COM server.
		/// </summary>
		[ComRegisterFunctionAttribute]
		public static void Register(Type t)
		{
			string guid = t.GUID.ToString("B");
			
			RegistryKey rkClass = Registry.ClassesRoot.CreateSubKey(@"CLSID\"+guid );
			RegistryKey rkCat = rkClass.CreateSubKey("Implemented Categories");

			BandObjectAttribute[] boa = (BandObjectAttribute[])t.GetCustomAttributes(
				typeof(BandObjectAttribute),
				false );

			string name = t.Name;
			string help = t.Name;
			BandObjectStyle style = 0;
			if( boa.Length == 1 )
			{
				if( boa[0].Name != null )
					name = boa[0].Name;

				if( boa[0].HelpText != null )
					help = boa[0].HelpText;

				style = boa[0].Style;
			}

			rkClass.SetValue(null, name );
			rkClass.SetValue("MenuText", name );
			rkClass.SetValue("HelpText", help );

			if( 0 != (style & BandObjectStyle.Vertical) )
				rkCat.CreateSubKey("{00021493-0000-0000-C000-000000000046}");

			if( 0 != (style & BandObjectStyle.Horizontal) )
				rkCat.CreateSubKey("{00021494-0000-0000-C000-000000000046}");

			if( 0 != (style & BandObjectStyle.TaskbarToolBar) )
				rkCat.CreateSubKey("{00021492-0000-0000-C000-000000000046}");

			if( 0 != (style & BandObjectStyle.ExplorerToolbar) )
				Registry.LocalMachine.CreateSubKey(@"SOFTWARE\Microsoft\Internet Explorer\Toolbar").SetValue(guid,name);

		}
   
		/// <summary>
		/// Called when derived class is unregistered as a COM server.
		/// </summary>
		[ComUnregisterFunctionAttribute]
		public static void Unregister(Type t)
		{
			string guid = t.GUID.ToString("B");
			BandObjectAttribute[] boa = (BandObjectAttribute[])t.GetCustomAttributes(
				typeof(BandObjectAttribute),
				false );

			BandObjectStyle style = 0;
			if( boa.Length == 1 ) style = boa[0].Style;

			if( 0 != (style & BandObjectStyle.ExplorerToolbar) )
				Registry.LocalMachine.CreateSubKey(@"SOFTWARE\Microsoft\Internet Explorer\Toolbar").DeleteValue(guid,false);

			Registry.ClassesRoot.CreateSubKey(@"CLSID").DeleteSubKeyTree(guid);
        }

        #endregion

        #region Properties

        /// <summary>
        /// Title of band object. Displayed at the left or on top of the band object.
        /// </summary>
        [Browsable(true)]
        [DefaultValue("")]
        public String Title
        {
            get
            {
                return _title;
            }
            set
            {
                _title = value;
            }
        }

        /// <summary>
        /// Minimum size of the band object. Default value of -1 sets no minimum constraint.
        /// </summary>
        [Browsable(true)]
        [DefaultValue(typeof(Size), "-1,-1")]
        public Size MinSize
        {
            get
            {
                return _minSize;
            }
            set
            {
                _minSize = value;
            }
        }

        /// <summary>
        /// Maximum size of the band object. Default value of -1 sets no maximum constraint.
        /// </summary>
        [Browsable(true)]
        [DefaultValue(typeof(Size), "-1,-1")]
        public Size MaxSize
        {
            get
            {
                return _maxSize;
            }
            set
            {
                _maxSize = value;
            }
        }

        /// <summary>
        /// Says that band object's size must be multiple of this size. Defauilt value of -1 does not set this constraint.
        /// </summary>
        [Browsable(true)]
        [DefaultValue(typeof(Size), "-1,-1")]
        public Size IntegralSize
        {
            get
            {
                return _integralSize;
            }
            set
            {
                _integralSize = value;
            }
        }

        #endregion

        #region Member Variables

        /// <summary>
        /// This event is fired after reference to hosting explorer is retreived and stored in Explorer property.
        /// </summary>
        public event EventHandler ExplorerAttached;

        protected WebBrowserClass Explorer;
        protected IInputObjectSite BandObjectSite;

        private bool _compositionEnabled = true;
        private String _title;
        private Size _minSize = new Size(-1, -1);
        private Size _integralSize = new Size(-1, -1);
        private Size _maxSize = new Size(-1, -1);

        #endregion
    }
}