using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Windows.Forms;
namespace AT.STO.UI.Win
{
	/// 
	/// Control that allows any other control that implements IDropDownAware to be displayed
	/// in it's own combo-like dropdown area. This Control tries to mimic the standard ComboBox
	/// as accurately as possible.
	/// 
	public partial class DropDownPanel : UserControl, IDropDownAware
	{
	#region Private Variable Declarations
		private IDropDownAware			_dropDownControl	= null;
		private DropDownWindowHelper	_dropDownHelper		= null;
		private Form					_owner				= null;
	#endregion
	#region Constructor / Destructor
		/// 
		/// Default constructor
		/// 
		public DropDownPanel()
		{
			InitializeComponent();
			_dropDownHelper = new DropDownWindowHelper();
			_dropDownHelper.DropDownClosed += new DropDownClosedEventHandler(DropDownHelper_DropDownClosed);
			_dropDownHelper.DropDownCancel += new DropDownCancelEventHandler(DropDownHelper_DropDownCancel);
			
			combo.DisplayMember = "Text";
			combo.ValueMember = "Id";
		}
	#endregion
	#region Control Events
		/// 
		/// The owning form is set within this event, wich is required to
		/// force the owning form not to loose focus when the dropdown is
		/// being displayed. Inherited controls should provide the PopupControl
		/// within an overridden implementation of this event.
		/// 
		/// 
		protected override void OnHandleCreated(EventArgs e)
		{	
			_owner = this.FindForm();
			_dropDownHelper.ReleaseHandle();
			
			if (_owner != null)
			{
				_dropDownHelper.AssignHandle(_owner.Handle);
			}
		}
		/// 
		/// Make sure that the overall control's height is exactly the height
		/// of the internal ComboBox and that the ComboBox's widt is exactly the 
		/// width of the control.
		/// 
		/// 
		protected override void OnResize(EventArgs e)
		{
			base.OnResize(e);
			combo.Location = new Point(0, 0);
			combo.Width = this.ClientRectangle.Width;
			this.Height = combo.Height;
		}
	#endregion
	#region Event Handler
		/// 
		/// We make our DropDownForm host the choosen control and show it instead
		/// of the dropdown portion of the ComboBox. 
		/// 
		/// 
		/// 
		private void Combo_DropDown(object sender, EventArgs e)
		{
			if (!_dropDownHelper.DropDownShowing)
			{
				DropDownForm dropDown = new DropDownForm(_dropDownControl);
				
				
				dropDown.FinishEditing += new DropDownValueChangedEventHandler(DropDown_FinishEditing);
				dropDown.ValueChanged += new DropDownValueChangedEventHandler(DropDown_ValueChanged);
				
				combo.DroppedDown = false;
				dropDown.Width = combo.Width;   // KBR ADDED 2/15/08
				_dropDownHelper.ShowDropDown(_owner, dropDown, GetDropDownPosition(dropDown));
			}
			else
			{
				_dropDownHelper.CloseDropDown();
				this.Focus();
			}
		}
		
		private void DropDownHelper_DropDownClosed(object sender, DropDownClosedEventArgs e)
		{
			IDropDownAware dropDown = (e.DropDown as IDropDownAware);
			
			if ((dropDown != null) && (dropDown.Value != null))
			{
				dropDown.FinishEditing -= new DropDownValueChangedEventHandler(DropDown_FinishEditing);
				dropDown.ValueChanged -= new DropDownValueChangedEventHandler(DropDown_ValueChanged);
			}
			
			combo.DroppedDown = false;
		}
		private void DropDownHelper_DropDownCancel(object sender, DropDownCancelEventArgs e)
		{
			if (this.Bounds.Contains(Parent.PointToClient(e.CursorLocation)))
			{
				e.Cancel = true;
			}
			else
			{
				IDropDownAware dropDown = (e.DropDown as IDropDownAware);
				
				if (dropDown != null)
				{
					dropDown.FinishEditing -= new DropDownValueChangedEventHandler(DropDown_FinishEditing);
					dropDown.ValueChanged -= new DropDownValueChangedEventHandler(DropDown_ValueChanged);
				}
			}
		}
		
		private void DropDown_FinishEditing(object sender, DropDownValueChangedEventArgs e)
		{	
			if (e.Value != null)
			{
				SetValue(e.Value as ILookupItem);
			}
			
			if (this.FinishEditing != null)
			{
				this.FinishEditing(this, e);
			}
			
			_dropDownControl.FinishEditing -= new DropDownValueChangedEventHandler(DropDown_FinishEditing);
			_dropDownControl.ValueChanged -= new DropDownValueChangedEventHandler(DropDown_ValueChanged);
			_dropDownHelper.CloseDropDown();
		}
		
		private void DropDown_ValueChanged(object sender, DropDownValueChangedEventArgs e)
		{
			if (this.ValueChanged != null)
			{
				this.ValueChanged(this, e);
			}
		}
	#endregion
	#region Public Properties
		/// 
		/// Get or set the control (has to implement IDropDownAware) that is to 
		/// be displayed as the dropdown portion of the combobox. 
		/// 
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		public IDropDownAware DropDownControl
		{
			get { return _dropDownControl; }
			set 
			{ 
				_dropDownControl = value; 
				
				this.Controls.Add(_dropDownControl as Control);
			}
		}
	#endregion
	#region Public Methods
		public override string ToString()
		{
			return this.Name;
		}
	#endregion
	#region Private Methods
		/// 
		/// Calculate an acceptable position of the DropDownForm even in a 
		/// multi screen environment.
		/// 
		/// 
		/// 
		private Point GetDropDownPosition(DropDownForm DropDown)
		{
			Point		lt		= Parent.PointToScreen(new Point(Left, Top));
			Point		rb		= Parent.PointToScreen(new Point(Right, Bottom));
			Rectangle	screen	= Screen.FromControl(this).Bounds;
			Point		point	= new Point();
			
			if (((lt.X + DropDown.Width) > (screen.X + screen.Width)) && ((rb.X - DropDown.Width) >= screen.X))
			{
				point.X = rb.X - DropDown.Width; 
				
				if ((point.X + DropDown.Width) > (screen.X + screen.Width))
				{
					point.X = ((screen.X + screen.Width) - DropDown.Width); 
				}
			}
			else
			{
				point.X = lt.X;
				
				if (point.X < screen.X)
				{
					point.X = screen.X;
				}
			}
			
			if (((rb.Y + DropDown.Height) > (screen.Y + screen.Height)) && ((lt.Y - DropDown.Height) >= screen.Y))
			{
				point.Y = lt.Y - DropDown.Height;
				
				if (point.Y < screen.Y)
				{
					point.Y = screen.Y;
				}
			}
			else
			{
				point.Y = rb.Y;
				
				if ((point.Y + DropDown.Height) > (screen.Y + screen.Height))
				{
					point.Y = ((screen.Y + screen.Height) - DropDown.Height);
				}
			}
			
			return point;
		}
		
		/// 
		/// In this implementation we don't the user to edit the ComboBox
		/// directly, so we add the new value to the item collection after
		/// clearing it first.
		/// 
		/// 
		/// 
		private void SetValue(ILookupItem Value) where T: struct
		{
			if (DropDownControl != null)
			{
				ILookupItem[] arr = new ILookupItem[0];
				
				combo.DataSource = arr;
				
				if ((Value != null) && (Value is ILookupItem))
				{
					DropDownControl.Value = Value;
					
					arr = new ILookupItem[1]{(ILookupItem) Value};
					combo.DataSource = arr;
					combo.SelectedIndex = 0;
					//combo.Focus();
				}
				else
				{
					DropDownControl.Value = null;
				}
			}
		}
	#endregion
	#region IDropDownAware Implementation
		/// 
		/// Fired either on OK, Cancel or a click outside the control to indicate
		/// that the user has finished editing.
		/// 
		public event DropDownValueChangedEventHandler FinishEditing;
		
		/// 
		/// Fired on any change of the controls's value during the editing process. 
		/// 
		public event DropDownValueChangedEventHandler ValueChanged;
		
		/// 
		/// Gets or sets the controls' value.
		/// 
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		public object Value
		{
			get { return _dropDownControl.Value; }
			set { SetValue(value as ILookupItem); }
		}
	#endregion
	}
}