
#region ================== Copyright (c) 2007 Pascal vd Heiden

/*
 * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com
 * This program is released under GNU General Public License
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 */

#endregion

#region ================== Namespaces

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Text;
using System.Windows.Forms;
using System.Windows.Forms.VisualStyles;

#endregion

namespace CodeImp.DoomBuilder.Controls
{
	internal class DockersTabsControl : TabControl
	{
		#region ================== Constants

		private const int NOTIFY_BLINK_COUNT = 8; //mxd

		#endregion

		#region ================== Variables
		
		private int highlighttab;
		private readonly StringFormat stringformat;

		//mxd. Tab notify anmimation
		private int notifytab;
		private int notifycounter;
		private Timer notifytimer;
		
		#endregion

		#region ================== Constructor

		// Constructor
		public DockersTabsControl()
		{
			if(VisualStyleInformation.IsSupportedByOS && VisualStyleInformation.IsEnabledByUser)
			{
				// Style settings
				this.SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint | ControlStyles.DoubleBuffer, true);
				this.DrawMode = TabDrawMode.OwnerDrawFixed;
			}
			
			stringformat = new StringFormat {Alignment = StringAlignment.Center, HotkeyPrefix = HotkeyPrefix.None, LineAlignment = StringAlignment.Center};
			highlighttab = -1;

			//mxd. Tab notify anmimation
			notifytimer = new Timer { Interval = 500 };
			notifytimer.Tick += NotifyTimerOnTick;
		}
		
		#endregion

		#region ================== Methods

		//mxd. Start notify animation
		internal void PlayNotifyAnimation(int tabindex)
		{
			notifytab = tabindex;
			notifycounter = 1;
			notifytimer.Start();
		}

		//mxd
		private void DrawTab(Graphics graphics, int index)
		{
			Rectangle bounds = this.GetTabRect(index);
			VisualStyleRenderer renderer;
			bool selected = (index == this.SelectedIndex);

			// Transform bounds?
			switch(this.Alignment)
			{
				case TabAlignment.Right:
					bounds = new Rectangle((selected ? bounds.X - 1 : bounds.X + 1), bounds.Y, bounds.Height, bounds.Width);
					break;

				case TabAlignment.Left:
					bounds = new Rectangle(bounds.X, bounds.Y, bounds.Height, bounds.Width);
					break;

				default:
					if(selected) bounds.Y -= 2;
					break;
			}

			if(selected)
			{
				bounds.Height += 2;
				renderer = new VisualStyleRenderer(VisualStyleElement.Tab.TabItem.Pressed);
			}
			else
			{
				renderer = new VisualStyleRenderer(index == highlighttab ? VisualStyleElement.Tab.TabItem.Hot : VisualStyleElement.Tab.TabItem.Normal);
			}

			Bitmap drawimage = new Bitmap(bounds.Width, bounds.Height, PixelFormat.Format32bppArgb);

			// Draw tab
			using(Graphics g = Graphics.FromImage(drawimage))
			{
				Rectangle bgbounds = new Rectangle(0, 0, bounds.Width, bounds.Height + 1);
				bgbounds.Inflate(-1, 0);

				// Use alternate colors on odd numbers
				if(notifytab == index && notifycounter % 2 != 0)
				{
					g.FillRectangle(SystemBrushes.Highlight, bgbounds);
					g.DrawString(this.TabPages[index].Text, this.Font, SystemBrushes.ControlLightLight, new RectangleF(bgbounds.Location, bounds.Size), stringformat);
				}
				else
				{
					renderer.DrawBackground(g, bgbounds);
					g.DrawString(this.TabPages[index].Text, this.Font, SystemBrushes.ControlText, new RectangleF(bgbounds.Location, bounds.Size), stringformat);
				}
			}

			// Rotate image?
			switch(this.Alignment)
			{
				case TabAlignment.Right:
					drawimage.RotateFlip(RotateFlipType.Rotate270FlipNone);
					break;

				case TabAlignment.Left:
					drawimage.RotateFlip(RotateFlipType.Rotate90FlipNone);
					break;
			}

			graphics.DrawImage(drawimage, bounds.X, bounds.Y);
			drawimage.Dispose();
		}
		
		#endregion
		
		#region ================== Events

		//mxd. Stop notify animation if user selects animated tab
		protected override void OnSelectedIndexChanged(EventArgs e)
		{
			// Stop animation 
			if(notifytab != -1 && this.SelectedIndex == notifytab)
			{
				notifytimer.Stop();
				notifytab = -1;

				// Redraw needed?
				if(notifycounter % 2 != 0) this.Invalidate();
				notifycounter = 0;
			}

			base.OnSelectedIndexChanged(e);
		}
		
		//mxd. Redrawing needed
		protected override void OnPaint(PaintEventArgs e)
		{
			if(VisualStyleInformation.IsSupportedByOS && VisualStyleInformation.IsEnabledByUser)
			{
				// Draw tabs
				for(int i = 0; i < this.TabPages.Count; i++)
				{
					if(i == this.SelectedIndex) continue;
					DrawTab(e.Graphics, i);
				}

				// Draw selected tab
				if(this.SelectedIndex != -1) DrawTab(e.Graphics, this.SelectedIndex);
			}
			else
			{
				base.OnPaint(e);
			}
		}
		
		// Mouse moves
		protected override void OnMouseMove(MouseEventArgs e)
		{
			if(VisualStyleInformation.IsSupportedByOS && VisualStyleInformation.IsEnabledByUser)
			{
				int foundindex = -1;
				Rectangle prect = new Rectangle(e.Location, Size.Empty);
				
				// Check in which tab the mouse is
				for(int i = this.TabPages.Count - 1; i >= 0; i--)
				{
					Rectangle tabrect = this.GetTabRect(i);
					tabrect.Inflate(1, 1);
					if(tabrect.IntersectsWith(prect))
					{
						foundindex = i;
						break;
					}
				}
				
				// Redraw?
				if(foundindex != highlighttab)
				{
					highlighttab = foundindex;
					this.Invalidate();
				}
			}
			
			base.OnMouseMove(e);
		}
		
		// Mouse leaves
		protected override void OnMouseLeave(EventArgs e)
		{
			if(VisualStyleInformation.IsSupportedByOS && VisualStyleInformation.IsEnabledByUser)
			{
				// Redraw?
				if(highlighttab != -1)
				{
					highlighttab = -1;
					this.Invalidate();
				}
			}
			
			base.OnMouseLeave(e);
		}
		
		// Tabs don't process keys
		protected override void OnKeyDown(KeyEventArgs ke)
		{
			DockersControl docker = this.Parent as DockersControl;

			// Only absorb the key press when no focused on an input control, otherwise
			// the input controls may not receive certain keys such as delete and arrow keys
			if(docker != null && !docker.IsFocused) ke.Handled = true;
		}

		// Tabs don't process keys
		protected override void OnKeyUp(KeyEventArgs e)
		{
			DockersControl docker = this.Parent as DockersControl;

			// Only absorb the key press when no focused on an input control, otherwise
			// the input controls may not receive certain keys such as delete and arrow keys
			if(docker != null && !docker.IsFocused) e.Handled = true;
		}

		//mxd. Update notyfy animation
		private void NotifyTimerOnTick(object sender, EventArgs eventArgs)
		{
			if(notifycounter++ == NOTIFY_BLINK_COUNT)
			{
				notifytimer.Stop();
				notifycounter = 0;
				notifytab = -1;
			}

			// Trigger redraw
			this.Invalidate();
		}
		
		#endregion
	}
}
