//
// System.Windows.Forms.DateTimePicker
//
// Author:
//   stubbed out by Richard Baumann (biochem333@nyc.rr.com)
//   Dennis Hayes (dennish@Raytek.com)
//   Aleksey Ryabchuk (ryabchuk@yahoo.com)
//
// (C) Ximian, Inc., 2002/3
//
using System.Drawing;
using System.ComponentModel;
using System.Runtime.InteropServices;

namespace System.Windows.Forms {

	// <summary>
	//	Represents a Windows date-time picker control.
	// </summary>
	public class DateTimePicker : Control {

		//
		//  --- Public Fields
		//
		public static readonly DateTime MaxDateTime;
		public static readonly DateTime MinDateTime;

		//
		//  --- Protected Fields
		//
		protected static readonly Color DefaultMonthBackColor;
		protected static readonly Color DefaultTitleBackColor;
		protected static readonly Color DefaultTitleForeColor;
		protected static readonly Color DefaultTrailingForeColor;

		//
		//  --- Private Fields
		//
		private Font calendarFont;
		private Color calendarForeColor;
		private Color calendarMonthBackground;
		private Color calendarTitleBackColor;
		private Color calendarTitleForeColor;
		private Color calendarTrailingForeColor;
		private bool CHecked;
		private string customFormat;
		private LeftRightAlignment dropDownAlign;
		private DateTimePickerFormat format;
		private DateTime maxDate;
		private DateTime minDate;
		private int preferredHeight;
		private bool showCheckBox;
		private bool showUpDown;
		private DateTime val;

		//
		//  --- Constructors/Destructors
		//

		[MonoTODO]
		public DateTimePicker() : base()
		{
			// defaults :)
			calendarFont = Control.DefaultFont;
			calendarForeColor = ForeColor;
			calendarMonthBackground = DefaultMonthBackColor;
			calendarTitleBackColor = DefaultTitleBackColor;
			calendarTitleForeColor = DefaultTitleForeColor;
			calendarTrailingForeColor = DefaultTrailingForeColor;
			CHecked = true;
			customFormat = null;
			dropDownAlign = LeftRightAlignment.Left;
			format = DateTimePickerFormat.Long;
			maxDate = MaxDateTime;
			minDate = MinDateTime;
			showCheckBox = false;
			showUpDown = false;
			val = DateTime.Now;
		}

		[MonoTODO]
		static DateTimePicker()
		{
			MaxDateTime = new DateTime(9998,12,31,23,59,59);
			MinDateTime = new DateTime(1753,1,1,0,0,0);
			// As usual, the MS docs aren't very helpful...
			// I'm guessing these are all the right colors... not sure though
			// I'll check on Windows when I'm in a more masochistic mood ;)
			DefaultMonthBackColor = System.Drawing.SystemColors.Window;
			DefaultTitleBackColor = System.Drawing.SystemColors.ActiveCaption;
			DefaultTitleForeColor = System.Drawing.SystemColors.ActiveCaptionText;
			DefaultTrailingForeColor = System.Drawing.SystemColors.WindowText;
		}

		//
		//  --- Public Methods
		//
		public override string ToString(){
			return GetType().FullName.ToString() + ", Value: " + Value.ToString( );
		}

		//
		//  --- Protected Methods
		//
		[MonoTODO]
		protected override AccessibleObject CreateAccessibilityInstance()
		{
			//FIXME:
			return base.CreateAccessibilityInstance();;
		}

		[MonoTODO]
		protected override void CreateHandle()
		{
			initCommonControlsLibrary();
			base.CreateHandle();
		}

		[MonoTODO]
		protected override void DestroyHandle()
		{
			//FIXME:
			base.DestroyHandle();
		}

		[MonoTODO]
		protected override void Dispose(bool disposing){
			base.Dispose(disposing);
		}

		[MonoTODO]
		protected override bool IsInputKey(Keys keyData)
		{
			//FIXME:
			return base.IsInputKey(keyData);
		}

		protected virtual void OnCloseUp(EventArgs eventargs) {
			if ( CloseUp != null ) 
				CloseUp( this, eventargs );
		}

		protected virtual void OnDropDown(EventArgs eventargs)	{
			if ( DropDown != null )
				DropDown( this, eventargs );
		}

		[MonoTODO]
		protected override void OnFontChanged(EventArgs e)
		{
			//FIXME:
			//if (FontChanged != null) {
			//	FontChanged(this, e);
			//}
		}

		protected virtual void OnFormatChanged(EventArgs e) {
			if (FormatChanged != null)
				FormatChanged(this, e);
		}

		[MonoTODO]
		protected override void OnSystemColorsChanged(EventArgs e)
		{
			base.OnSystemColorsChanged( e );
		}

		//FIXME: should be inherated
		protected override void OnHandleCreated(EventArgs e) {
			base.OnHandleCreated(e);
			setControlRange( );
			setControlValue( );
			setCalendarColors( );
			setCustomFormat( );
			setCalendarFont( );
		}

		protected virtual void OnValueChanged(EventArgs eventargs) {
			if (ValueChanged != null) 
				ValueChanged(this, eventargs);
		}

		[MonoTODO]
		protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified)
		{
			//FIXME:
			base.SetBoundsCore(x, y, width, height, specified);
		}

		[MonoTODO]
		protected override void WndProc(ref Message m)
		{
			switch ( m.Msg ) {
			case Msg.WM_NOTIFY:
				if ( m.LParam != IntPtr.Zero )
					handleNotification ( ref m );
				else 
					CallControlWndProc(ref m);
				break;
			default:
				CallControlWndProc(ref m);
				break;
			}
		}

		
		//  --- Public Events
		
		public event EventHandler CloseUp;
		public event EventHandler DropDown;
		public event EventHandler FormatChanged;
		public event EventHandler ValueChanged;

		
		//  --- Public Properties
		
		[EditorBrowsable (EditorBrowsableState.Never)]	 
		public override Color BackColor {
			get {	return base.BackColor;	}
			set {	base.BackColor = value; }
		}

		[EditorBrowsable (EditorBrowsableState.Never)]	 
		public override Image BackgroundImage 	{
			get {	return base.BackgroundImage; }
			set {	base.BackgroundImage = value;}
		}

		public Font CalendarFont {
			get {	return calendarFont; }
			set {
				calendarFont = value;
				setCalendarFont( );
			}
		}

		public Color CalendarForeColor {
			get {	return calendarForeColor; }
			set {
				if ( calendarForeColor != value ) {
					calendarForeColor = value;
					setCalendarColor( (int) MonthCalColors.MCSC_TEXT, value );
				}
			}
		}

		public Color CalendarMonthBackground {
			get {	return calendarMonthBackground; }
			set {
				if ( calendarMonthBackground != value ) {
					calendarMonthBackground = value;
					setCalendarColor( (int) MonthCalColors.MCSC_MONTHBK, value );
				}
			}
		}

		public Color CalendarTitleBackColor {
			get {	return calendarTitleBackColor; }
			set {
				if ( calendarTitleBackColor != value ) {
					calendarTitleBackColor = value;
					setCalendarColor( (int) MonthCalColors.MCSC_TITLEBK, value );
				}
			}
		}

		public Color CalendarTitleForeColor {
			get {	return calendarTitleForeColor; }
			set {
				if ( calendarTitleForeColor != value ) 	{
					calendarTitleForeColor = value;
					setCalendarColor( (int) MonthCalColors.MCSC_TITLETEXT, value );
				}
			}
		}

		public Color CalendarTrailingForeColor {
			get {	return calendarTrailingForeColor; }
			set {
				if ( calendarTrailingForeColor != value ) {
					calendarTrailingForeColor = value;
					setCalendarColor( (int) MonthCalColors.MCSC_TRAILINGTEXT, value );
				}
			}
		}

		public bool Checked {
			get {	
				if ( ShowCheckBox )
					getControlValue ( false ); // don't actually update the Value property
				return CHecked;
			}
			set {	
				CHecked = value;
				if ( ShowCheckBox )
					setCheckState ( );
			}
		}

		public string CustomFormat {
			get {	return customFormat; }
			set {
				customFormat = value;
				setCustomFormat ( );
			}
		}

		public LeftRightAlignment DropDownAlign 
		{
			get {	return dropDownAlign; }
			set {	
				if ( !Enum.IsDefined ( typeof(LeftRightAlignment), value ) )
					throw new InvalidEnumArgumentException( "DropDownAlign",
						(int)value,
						typeof(LeftRightAlignment));

				if ( dropDownAlign != value ) {
					dropDownAlign = value;
					if ( IsHandleCreated )
						RecreateHandle();
				}
			}
		}

		[EditorBrowsable (EditorBrowsableState.Never)]
		public override Color ForeColor {
			get {	return base.ForeColor;	}
			set {	base.ForeColor = value;	}
		}

		public DateTimePickerFormat Format {
			get {	return format;	}
			set {
				if ( !Enum.IsDefined ( typeof(DateTimePickerFormat), value ) )
					throw new InvalidEnumArgumentException( "Format",
						(int)value,
						typeof(DateTimePickerFormat));

				if ( format != value ) {
					int StyleToRemove = formatStyle ( format );
					format = value;
					if ( IsHandleCreated )
						Win32.UpdateWindowStyle ( Handle, StyleToRemove, formatStyle ( format ) );
					OnFormatChanged( EventArgs.Empty );
				}
			}
		}

		public DateTime MaxDate {
			get {	return maxDate; }
			set {
				if ( value == maxDate )
					return;
				
				if ( value < MinDate )
					throw new ArgumentException (
						string.Format ("'{0}' is not a valid value for 'MaxDate'. 'MaxDate'  must be greater than or equal to MinDate", value ) );

				if ( value > MaxDateTime )
					throw new ArgumentException (
						string.Format ("DateTimePicker does not support dates after {0}.", MaxDateTime ) );

				maxDate = value;
				setControlRange	( );
			}
		}

		public DateTime MinDate {
			get {	return minDate;	}
			set {
				if ( value == minDate )
					return;

				if ( value >= MaxDate )
					throw new ArgumentException (
					string.Format ("'{0}' is not a valid value for 'MinDate'. 'MinDate' must be less than MaxDate.", value ) );

				if ( value < MinDateTime )
					throw new ArgumentException (
					string.Format ("DateTimePicker does not support dates before {0}.", MinDateTime ) );

				minDate = value;
				setControlRange	( );
			}
		}

		[MonoTODO]
		public int PreferredHeight {
			get{
				return 300;
			}
		}

		public bool ShowCheckBox {
			get {	return showCheckBox; }
			set {
				if ( showCheckBox != value ) {
					showCheckBox = value;
					if ( IsHandleCreated )
						RecreateHandle();
				}
			}
		}

		public bool ShowUpDown {
			get {	return showUpDown; }
			set {	
				if ( showUpDown != value ) {
					showUpDown = value;
					if ( IsHandleCreated )
						RecreateHandle();
				}
			}
		}

		[EditorBrowsable (EditorBrowsableState.Never)]
		public override string Text {
			get {	return base.Text; }
			set {	base.Text = value;}
		}

		[MonoTODO]
		public DateTime Value {
			get {
				getControlValue( true );
				return val;
			}
			set {
				if ( val != value ) {
					val = value; // do we need to check that the value is in the range ?
					setControlValue( );
					OnValueChanged ( EventArgs.Empty );
				}
			}
		}

		
		//  --- Protected Properties
		
		[MonoTODO]
		protected override CreateParams CreateParams {
			get {
				CreateParams createParams = base.CreateParams;

				createParams.ClassName = "SysDateTimePick32";

				createParams.Style = (int) (
					WindowStyles.WS_CHILDWINDOW | 
					WindowStyles.WS_VISIBLE |
					WindowStyles.WS_CLIPCHILDREN|
					WindowStyles.WS_CLIPSIBLINGS);
				
				if ( ShowUpDown )
					createParams.Style |= (int) DateTimePickerControlStyles.DTS_UPDOWN;

				if ( ShowCheckBox )
					createParams.Style |= (int) DateTimePickerControlStyles.DTS_SHOWNONE;

				if ( DropDownAlign == LeftRightAlignment.Right )
					createParams.Style |= (int) DateTimePickerControlStyles.DTS_RIGHTALIGN;

				createParams.Style |= formatStyle ( Format );

				return createParams;
			}		
		}

		protected override Size DefaultSize {
			get{	return new System.Drawing.Size(200,20);	}
		}

		private void initCommonControlsLibrary	( ) {
			if ( !RecreatingHandle ) {
				INITCOMMONCONTROLSEX	initEx = new INITCOMMONCONTROLSEX();
				initEx.dwICC = CommonControlInitFlags.ICC_DATE_CLASSES;
				Win32.InitCommonControlsEx(initEx);
			}
		}

		private SYSTIME toSysTime ( DateTime val ) {
			SYSTIME systime = new SYSTIME() ;
			systime.wDay = (ushort)val.Day;
			systime.wHour = (ushort)val.Hour;
			systime.wMilliseconds = (ushort)val.Millisecond;
			systime.wMinute = (ushort)val.Minute;
			systime.wMonth = (ushort)val.Month;
			systime.wSecond = (ushort)val.Second;
			systime.wYear = (ushort)val.Year;
			return systime;
		}

		private DateTime toDateTime ( ref SYSTIME val ) {
			return new DateTime(	val.wYear, val.wMonth, val.wDay,
						val.wHour, val.wMinute, val.wSecond,
						val.wMilliseconds );
		}

		private void setControlValue ( ) {
			if ( IsHandleCreated ) 	{
				SYSTIME systime = toSysTime ( val ) ;

				IntPtr ptr = Marshal.AllocHGlobal ( Marshal.SizeOf ( systime ) );
				Marshal.StructureToPtr( systime, ptr, false );
				Win32.SendMessage ( Handle, (int)DateTimePickerMessages.DTM_SETSYSTEMTIME,
							(int)DateTimePickerFlags.GDT_VALID, ptr );
				Marshal.FreeHGlobal ( ptr );
			}
		}

		private void setCheckState ( ) {
			if ( Checked )
				setControlValue ();
			else {
				if ( IsHandleCreated ) 	{
					Win32.SendMessage ( Handle, (int)DateTimePickerMessages.DTM_SETSYSTEMTIME,
						(int)DateTimePickerFlags.GDT_NONE, 0 );
				}
			}
		}

		private void getControlValue ( bool updateProp ) {
			if ( IsHandleCreated ) 	{
				SYSTIME systime = new SYSTIME();
				IntPtr ptr = Marshal.AllocHGlobal ( Marshal.SizeOf ( systime ) );
				Marshal.StructureToPtr( systime, ptr, false );
				int res = Win32.SendMessage ( Handle, (int)DateTimePickerMessages.DTM_GETSYSTEMTIME,
							      0	, ptr ).ToInt32();
				if ( res == (int)DateTimePickerFlags.GDT_VALID ) {
					systime = Marshal.PtrToStructure ( ptr, systime.GetType ( ) ) as SYSTIME;
					DateTime newValue = toDateTime ( ref systime );

					CHecked = !( newValue == val || newValue == DateTime.Now );

					if ( updateProp )
						val = newValue;
				}
				else
					CHecked = false;
				Marshal.FreeHGlobal ( ptr );
			}
		}

		private void setControlRange (  ) {
			if ( IsHandleCreated ) {
				SYSTIME[] range = { toSysTime ( MinDate ), toSysTime ( MaxDate ) };
				IntPtr buffer = Marshal.AllocHGlobal ( Marshal.SizeOf( range[0] ) * 2 );
				IntPtr current = buffer;
				Marshal.StructureToPtr ( range[0], current, false );
				current = (IntPtr)( current.ToInt32() + Marshal.SizeOf( range[0] ) );
				Marshal.StructureToPtr ( range[1], current, false );
				Win32.SendMessage( Handle, (int)DateTimePickerMessages.DTM_SETRANGE,
							(int)( DateTimePickerFlags.GDTR_MIN | DateTimePickerFlags.GDTR_MAX ),
							 buffer.ToInt32() );
				Marshal.FreeHGlobal ( buffer );
			}
		}

		private void setCalendarColor ( int ColorFlag, Color clr ) {
			if ( IsHandleCreated )
				Win32.SendMessage ( Handle, (int)DateTimePickerMessages.DTM_SETMCCOLOR, ColorFlag, Win32.RGB(clr) );
		}

		private void setCalendarColors ( ) {
			if ( calendarForeColor != ForeColor )
				setCalendarColor( (int) MonthCalColors.MCSC_TEXT, calendarForeColor );
			if ( calendarMonthBackground != DefaultMonthBackColor )
				setCalendarColor( (int) MonthCalColors.MCSC_MONTHBK, calendarMonthBackground );
			if ( calendarTitleBackColor != DefaultTitleBackColor )
				setCalendarColor( (int) MonthCalColors.MCSC_TITLEBK, calendarTitleBackColor );
			if ( calendarTitleForeColor != DefaultTitleForeColor )
				setCalendarColor( (int) MonthCalColors.MCSC_TITLETEXT, calendarTitleForeColor );
			if ( calendarTrailingForeColor != DefaultTrailingForeColor )
				setCalendarColor( (int) MonthCalColors.MCSC_TRAILINGTEXT, calendarTrailingForeColor );
		}

		private int formatStyle ( DateTimePickerFormat format ) {
			int style = 0;

			switch ( format ) {
			case DateTimePickerFormat.Long:
				style = (int)DateTimePickerControlStyles.DTS_LONGDATEFORMAT;
			break;
			case DateTimePickerFormat.Short:
				style = (int)DateTimePickerControlStyles.DTS_SHORTDATEFORMAT;
			break;
			case DateTimePickerFormat.Time:
				style = (int)DateTimePickerControlStyles.DTS_TIMEFORMAT;
			break;
			}
			return style;
		}

		private void setCustomFormat ( ) {
			if ( Format == DateTimePickerFormat.Custom && IsHandleCreated ) 
				Win32.SendMessage ( Handle, (int)DateTimePickerMessages.DTM_SETFORMATA, 0, CustomFormat );
		}
		
		private void setCalendarFont ( ) {
			// This code will not work because Font.Equals is not implemented
			/*
			if ( IsHandleCreated && !CalendarFont.Equals( Control.DefaultFont ) ) 
				Win32.SendMessage ( Handle, (int)DateTimePickerMessages.DTM_SETMCFONT,
							CalendarFont.ToHfont().ToInt32(), 0 );
			*/
		}

		private void handleNotification ( ref Message m ) {
			NMHDR nmhdr = (NMHDR)Marshal.PtrToStructure ( m.LParam,	typeof ( NMHDR ) );
			
			m.Result = IntPtr.Zero;

			switch ( nmhdr.code ) {
			case (int)DateTimePickerNotifications.DTN_CLOSEUP:
				OnCloseUp ( EventArgs.Empty );
			break;
			case (int)DateTimePickerNotifications.DTN_DROPDOWN:
				OnDropDown ( EventArgs.Empty );
			break;
			case (int)DateTimePickerNotifications.DTN_DATETIMECHANGE:
				getControlValue ( true );
				OnValueChanged ( EventArgs.Empty );
			break;
			default:
				CallControlWndProc ( ref m );		
			break;
			}
		}
	}
}
