// This is a part of the Smart PropertyGrid Library
// Copyright (C) 2001-2006 VisualHint
// All rights reserved.
//
// This source code can be used, distributed or modified
// only under terms and conditions
// of the accompanying license agreement.

using System;
using System.Windows.Forms;
using System.Drawing;
using System.Runtime.InteropServices;
using System.ComponentModel;
using System.Security.Permissions;

namespace VisualHint.SmartPropertyGrid
{
    /// <summary>
    /// Inplace control shown in the value column when edition of a date or time is needed.
    /// </summary>
    [ToolboxItem(false)]
    public class PropInPlaceDateTime : DateTimePicker, IInPlaceControl, IMouseHookClient, IMessageHookClient
    {
        private MouseHook _mouseHook;

        private MessageHook _messageHook;

        private IntPtr _closeByClickOnWindow;

        private bool _calendarClosedByKey = false;

        private bool _justVisible = false;

        private DateTime _oldValue;

        private PropertyEnumerator _ownerPropertyEnum;

        public PropInPlaceDateTime()
        {
            _mouseHook = new MouseHook(this, this);
            _messageHook = new MessageHook(this, this);
        }

        /// <summary>
        /// Links a Property to this inplace control, so that its value will be displayed inside.
        /// </summary>
        /// <value>An enumerator to the Property to link to this control.</value>
        public PropertyEnumerator OwnerPropertyEnumerator
        {
            set
            {
                _ownerPropertyEnum = (PropertyEnumerator)value.Clone();

                Enabled = !ReadOnly;

                // Store the current value
                _oldValue = (DateTime)_ownerPropertyEnum.Property.Value.GetValue();
            }

            get { return _ownerPropertyEnum; }
        }

        public bool ReadOnly
        {
            get { return _ownerPropertyEnum.Property.Value.IsReadOnly(_ownerPropertyEnum); }
        }

        protected override void OnHandleCreated(EventArgs e)
        {
            Win32Calls.ModifyStyleEx(Handle, Win32Calls.WS_EX_CLIENTEDGE, 0, Win32Calls.SWP_FRAMECHANGED);

            Cursor = Cursors.Arrow;

            base.OnHandleCreated(e);
        }

        protected override void WndProc(ref Message m)
        {
            if (m.Msg == Win32Calls.WM_NCCALCSIZE)
            {
                Win32Calls.NCCALCSIZE_PARAMS csp;

                csp = (Win32Calls.NCCALCSIZE_PARAMS)Marshal.PtrToStructure(m.LParam, typeof(Win32Calls.NCCALCSIZE_PARAMS));

            	csp.rgrc0.Left += PropertyGrid.GlobalTextMargin - 2;
                Marshal.StructureToPtr(csp, m.LParam, false);
            }
            else if (m.Msg == Win32Calls.WM_NCPAINT)
            {
                IntPtr hDC = Win32Calls.GetWindowDC(m.HWnd);
                Graphics graphics = Graphics.FromHdc(hDC);

                Rectangle rect = ClientRectangle;
                rect.Width = PropertyGrid.GlobalTextMargin - 2;
                _ownerPropertyEnum.Property.ParentGrid.DrawManager.DrawPropertyValueBackground(graphics,
                    rect, _ownerPropertyEnum);

                Win32Calls.ReleaseDC(m.HWnd, hDC);

                return;
            }
            else if (m.Msg == Win32Calls.WM_ERASEBKGND)
            {
                if (_justVisible == false)
                {
                    _ownerPropertyEnum.Property.ParentGrid.DrawManager.DrawPropertyValueBackground(
                        Graphics.FromHdc(m.WParam), ClientRectangle, _ownerPropertyEnum);
                }

                m.Result = (IntPtr)1;
                return;
            }
            else if (m.Msg == Win32Calls.WM_PAINT)
            {
                if (_justVisible)
                {
                    Graphics graphics = CreateGraphics();
                    _ownerPropertyEnum.Property.ParentGrid.DrawManager.DrawPropertyValueBackground(
                        graphics, ClientRectangle, _ownerPropertyEnum);
                    graphics.Dispose();

                    _justVisible = false;
                }
            }

            base.WndProc(ref m);
        }

        protected override void OnVisibleChanged(EventArgs e)
        {
            if (Visible)
            {
                PropertyDateTimeLook look = (_ownerPropertyEnum.Property.Value.Look as PropertyDateTimeLook);
                if (look != null)
                {
                    CustomFormat = look.CustomFormat;
                    Format = look.Format;
                    ShowUpDown = look.ShowUpDown;
                }
            }
            else
                _justVisible = true;

            base.OnVisibleChanged(e);
        }

        protected override void  OnTextChanged(EventArgs e)
        {
            PropertyValue value = _ownerPropertyEnum.Property.Value;

            // Store the previous value
            DateTime oldValue = (DateTime)value.GetValue();

            // Assign the new value based on the edit content
            if (Value != oldValue)
            {
                // Freeze painting because updating the property may also update other properties in the grid.
                _ownerPropertyEnum.Property.ParentGrid.BeginUpdate();

                _ownerPropertyEnum.Property.Value.PreviousValue = oldValue;

                value.SetValue(Value);

                // Defreeze painting
                _ownerPropertyEnum.Property.ParentGrid.EndUpdate();

                // Notify the properties control if the value has really changed
                if ((DateTime)value.GetValue() != oldValue)
                    _ownerPropertyEnum.Property.ParentGrid.NotifyPropertyChanged(new PropertyChangedEventArgs(_ownerPropertyEnum));

                Value = (DateTime)value.GetValue();

                _ownerPropertyEnum.Property.ParentGrid.Grid.Refresh();
            }
        }

        [UIPermission(SecurityAction.LinkDemand, Window = UIPermissionWindow.AllWindows)]
        protected override bool ProcessDialogKey(Keys keyData)
        {
            Keys key = (keyData & Keys.KeyCode);

            if (key == Keys.Return)
            {
                Parent.Focus();
                return true;
            }
            else if (key == Keys.Escape)
            {
                // Restore the old string in the edit box
                Value = _oldValue;

                Parent.Focus();
                return true;
            }

            return base.ProcessDialogKey(keyData);
        }

        public void OnF4()
        {
            Focus();
        }

        public bool OnParentKeyDown(KeyEventArgs e)
        {
            return false;
        }

        public bool RealtimeChange
        {
            set { }
            get { return true; }
        }

        public void RepositionChildren()
        {
        }

        public bool OnForbidClickThrough(Point pt)
        {
            Rectangle rect = ClientRectangle;
            rect.X = rect.Right - SystemInformation.VerticalScrollBarWidth;
            rect.Width = SystemInformation.VerticalScrollBarWidth;
            return rect.Contains(pt);
        }

        protected override void OnDropDown(EventArgs eventargs)
        {
            _mouseHook.HookMessages = true;
            _messageHook.HookMessages = true;
            base.OnDropDown(eventargs);
        }

        protected override void OnCloseUp(EventArgs eventargs)
        {
            _mouseHook.HookMessages = false;
            _messageHook.HookMessages = false;

            Control control = FromHandle(_closeByClickOnWindow);
            if (control != null)
            {
                Point pt = MousePosition;
                pt = control.PointToClient(pt);
                Win32Calls.PostMessage(_closeByClickOnWindow, Win32Calls.WM_LBUTTONDOWN, 0,
                    Win32Calls.MakeLong(pt.X, pt.Y));
            }
            else if (_calendarClosedByKey)
                Parent.Focus();

            _calendarClosedByKey = false;

            base.OnCloseUp(eventargs);
        }

        [CLSCompliant(false)]
        public bool OnMouseHooked(IntPtr hWnd, uint message, Point pt, uint hitTest)
        {
            if (message == Win32Calls.WM_LBUTTONDOWN)
                _closeByClickOnWindow = hWnd;

            return false;
        }

        [CLSCompliant(false)]
        public bool OnMessageHooked(Control control, uint message, IntPtr wParam, IntPtr lParam)
        {
            if (message == 0x004E/*Win32Calls.WM_NOTIFY*/)
            {
                Win32Calls.NMHDR nmhdr = (Win32Calls.NMHDR)Marshal.PtrToStructure(lParam, typeof(Win32Calls.NMHDR));
                if (nmhdr.code == -753) // MCN_SELECT
                    _calendarClosedByKey = true;
            }

            return false;
        }

        public bool GoToNextPropertyOnTab(bool forward)
        {
            return true;
        }

        public virtual bool ScrollValueOnDoubleClick(bool forward, bool force)
        {
            return false;
        }
    }
}
