using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Text;
using System.Windows.Forms;
using System.ComponentModel;
namespace DevComponents.DotNetBar
{
	/// 
	/// Represents overlay class to support bubble effects on BubbleBar control.
	/// 
	[ToolboxItem(false)]
	internal class BubbleBarOverlay:Control
	{
		#region Private Variables
		private BubbleBar m_BubbleBar=null;
		private Timer m_VisibilityTimer=null;
		#endregion
		#region Internal Implementation
		public BubbleBarOverlay(BubbleBar parent)
		{
			if(parent==null)
				throw new InvalidOperationException("Parent BubbleBar object for BubbleBarOverlay cannot be null.");
			m_BubbleBar=parent;
			this.SetStyle(ControlStyles.UserPaint,true);
			this.SetStyle(ControlStyles.AllPaintingInWmPaint,true);
			this.SetStyle(ControlStyles.Opaque,true);
			this.SetStyle(ControlStyles.ResizeRedraw,true);
			//this.SetStyle(DisplayHelp.DoubleBufferFlag,true);
			this.SetStyle(ControlStyles.SupportsTransparentBackColor,true);
			this.SetStyle(ControlStyles.ContainerControl,false);
			this.SetStyle(ControlStyles.Selectable,false);
			this.BackColor=Color.Transparent;
		}
		protected override void OnPaint(PaintEventArgs e){}
		internal void UpdateWindow()
		{
			// Create bitmap for drawing onto
			Bitmap memoryBitmap = new Bitmap(this.Width, this.Height, PixelFormat.Format32bppArgb);
			using(Graphics g = Graphics.FromImage(memoryBitmap))
			{
				// Draw the background area
				g.Clear(Color.Transparent);
				g.CompositingMode=System.Drawing.Drawing2D.CompositingMode.SourceCopy;
                g.TextRenderingHint = DisplayHelp.AntiAliasTextRenderingHint;
				g.SmoothingMode=System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
				PaintEventArgs p=new PaintEventArgs(g,this.DisplayRectangle);
				m_BubbleBar.OnPaintOverlay(p);
				
				// Get hold of the screen DC
				IntPtr hDC = NativeFunctions.GetDC(IntPtr.Zero);
				
				// Create a memory based DC compatible with the screen DC
				IntPtr memoryDC = WinApi.CreateCompatibleDC(hDC);
				
				// Get access to the bitmap handle contained in the Bitmap object
				IntPtr hBitmap = memoryBitmap.GetHbitmap(Color.FromArgb(0));
				// Select this bitmap for updating the window presentation
				IntPtr oldBitmap = WinApi.SelectObject(memoryDC, hBitmap);
				// New window size
				NativeFunctions.SIZE ulwsize;
				ulwsize.cx = this.Width;
				ulwsize.cy = this.Height;
				// New window position
				NativeFunctions.POINT topPos;
				topPos.x = this.Left;
				topPos.y = this.Top;
				// Offset into memory bitmap is always zero
				NativeFunctions.POINT pointSource;
				pointSource.x = 0;
				pointSource.y = 0;
				// We want to make the entire bitmap opaque 
				NativeFunctions.BLENDFUNCTION blend = new NativeFunctions.BLENDFUNCTION();
				blend.BlendOp             = (byte)NativeFunctions.Win23AlphaFlags.AC_SRC_OVER;
				blend.BlendFlags          = 0;
				blend.SourceConstantAlpha = 255;
				blend.AlphaFormat         = (byte)NativeFunctions.Win23AlphaFlags.AC_SRC_ALPHA;
				// Tell operating system to use our bitmap for painting
				NativeFunctions.UpdateLayeredWindow(this.Handle, hDC, ref topPos, ref ulwsize, 
					memoryDC, ref pointSource, 0, ref blend, 
					(int)NativeFunctions.Win32UpdateLayeredWindowsFlags.ULW_ALPHA);
				// Put back the old bitmap handle
				WinApi.SelectObject(memoryDC, oldBitmap);
				// Cleanup resources
                WinApi.ReleaseDC(IntPtr.Zero, hDC);
                WinApi.DeleteObject(hBitmap);
                WinApi.DeleteDC(memoryDC);
			}
		}
		protected override void OnMouseEnter(EventArgs e)
		{
			this.Capture=true;
			base.OnMouseEnter (e);
		}
		protected override void OnMouseMove(MouseEventArgs e)
		{
			if(!this.DisplayRectangle.Contains(e.X,e.Y))
				this.Capture=false;
			m_BubbleBar.MouseMoveMessage(e);
			base.OnMouseMove (e);	
		}
		protected override void OnMouseLeave(EventArgs e)
		{
			m_BubbleBar.MouseLeaveMessage(e);
			base.OnMouseLeave(e);	
		}
		protected override void OnMouseDown(MouseEventArgs e)
		{
			m_BubbleBar.OverlayMouseDownMessage(e);
			base.OnMouseDown (e);
		}
		protected override void OnMouseUp(MouseEventArgs e)
		{
			m_BubbleBar.OverlayMouseUpMessage(e);
			base.OnMouseUp(e);
		}
		protected override void WndProc(ref Message m)
		{
			const int WM_MOUSEACTIVATE = 0x21;
			const int MA_NOACTIVATE = 3;
			if(m.Msg==WM_MOUSEACTIVATE)
			{
				m.Result=new System.IntPtr(MA_NOACTIVATE);
				return;
			}
			base.WndProc(ref m);
		}
		protected override CreateParams CreateParams
		{
			get
			{
				const uint WS_POPUP=0x80000000;
				const uint WS_CLIPSIBLINGS=0x04000000;
				const uint WS_CLIPCHILDREN=0x02000000;
				const int WS_EX_LAYERED=0x00080000;
				const int WS_EX_TOOLWINDOW=0x00000080;
				CreateParams p=base.CreateParams;
				p.ExStyle=(p.ExStyle | WS_EX_LAYERED | WS_EX_TOOLWINDOW);
				p.Style=unchecked((int)(WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN));
				p.Caption="";
				return p;
			}
		}
		private void CreateTimer()
		{
			if(m_VisibilityTimer!=null)
				return;
			m_VisibilityTimer=new Timer();
			m_VisibilityTimer.Tick+=new EventHandler(this.CheckVisibility);
            m_VisibilityTimer.Interval=500;
			m_VisibilityTimer.Enabled=true;
			m_VisibilityTimer.Start();
		}
		private void DestroyTimer()
		{
			if(m_VisibilityTimer==null)
				return;
			m_VisibilityTimer.Stop();
			m_VisibilityTimer.Enabled=false;
			m_VisibilityTimer.Dispose();
			m_VisibilityTimer=null;
		}
		private void CheckVisibility(object sender,EventArgs e)
		{
            try
            {
                BubbleBar bar = m_BubbleBar;
                if (bar == null || bar.IsDisposed)
                {
                    DestroyTimer();
                    return;
                }
                Form form = bar.FindForm();
                IntPtr foregroundWindow = NativeFunctions.GetForegroundWindow();
                Control c = null;
                if (foregroundWindow != IntPtr.Zero)
                    c = Control.FromHandle(foregroundWindow);
                if (c == null)
                {
                    OnOverlayInactive();
                    return;
                }
                if (form != null)
                {
                    if (Form.ActiveForm == form)
                        return;
                    Form parentForm = form;
                    while (parentForm.ParentForm != null)
                    {
                        if (parentForm.ParentForm == Form.ActiveForm)
                            return;
                        parentForm = parentForm.ParentForm;
                    }
                    if (form.IsMdiChild && form.MdiParent != null)
                    {
                        if (form.MdiParent.ActiveMdiChild == form)
                            return;
                    }
                }
                NativeFunctions.POINT p = new NativeFunctions.POINT();
                Point pm = Control.MousePosition;
                p.x = pm.X;
                p.x = pm.Y;
                IntPtr window = IntPtr.Zero;
                if (bar.Parent != null)
                    NativeFunctions.ChildWindowFromPoint(bar.Parent.Handle, p);
                if (window == IntPtr.Zero)
                {
                    window = NativeFunctions.WindowFromPoint(p);
                }
                if (window != IntPtr.Zero && window != this.Handle && window != bar.Handle && bar.Parent != null && bar.Parent.Handle != window)
                {
                    OnOverlayInactive();
                }
                else
                {
                    Point pc = this.PointToClient(pm);
                    if (!this.DisplayRectangle.Contains(pc))
                    {
                        bar.MouseLeaveMessage(e);
                    }
                }
            }
            catch (NullReferenceException)
            {
                DestroyTimer();
            }
		}
		private void OnOverlayInactive()
		{
			this.Capture=false;
			DestroyTimer();
			m_BubbleBar.OverlayInactive();
		}
		/// 
		/// Called just before window is shown.
		/// 
		internal void BeforeShow()
		{
			CreateTimer();
		}
		/// 
		/// Called after window is closed.
		/// 
		internal void AfterClose()
		{
			DestroyTimer();
		}
		#endregion
	}
}