// Copyright © Microsoft Corporation. 
// This source is subject to the Microsoft Source License for Silverlight Controls (March 2008 Release).
// Please see http://go.microsoft.com/fwlink/?LinkID=111693 for details.
// All other rights reserved. 

using System.Diagnostics;
using System.Windows.Input; 
 
namespace System.Windows.Controls
{ 
    /// <summary>
    /// Class to encapsulate drag behavior for a UIElement
    /// </summary> 
    internal class DragValidator
    {
        private UIElement _targetElement; 
        private Point _start; 
        private bool _draggingActive;
 
        /// <summary>
        /// Occurs when a drag operation has started
        /// </summary> 
        public event EventHandler<DragStartedEventArgs> DragStartedEvent;

        /// <summary> 
        /// Occurs when a drag operation has completed 
        /// </summary>
        public event EventHandler<DragCompletedEventArgs> DragCompletedEvent; 

        /// <summary>
        /// Occurs when a drag operation has progressed 
        /// </summary>
        public event EventHandler<DragDeltaEventArgs> DragDeltaEvent;
 
        /// <summary> 
        /// Create an instance of the DragValidator class
        /// </summary> 
        /// <param name="targetElement">UIElement that represents the source of the drag operation</param>
        public DragValidator(UIElement targetElement)
        { 
            Debug.Assert(targetElement != null);

            _targetElement = targetElement; 
 
            _targetElement.MouseLeftButtonDown += new MouseButtonEventHandler(TargetElement_MouseLeftButtonDown);
            _targetElement.MouseLeftButtonUp += new MouseButtonEventHandler(TargetElement_MouseLeftButtonUp); 
            _targetElement.MouseMove += new MouseEventHandler(TargetElement_MouseMove);
        }
 
        /// <summary>
        /// Handle the MouseMove event for the UIElement to update the drag operation
        /// </summary> 
        private void TargetElement_MouseMove(object sender, MouseEventArgs e) 
        {
            if (_draggingActive) 
            {
                OnDragDelta(e);
            } 
        }

        /// <summary> 
        /// Handle the MouseLeftButtonUp event for the UIElement to complete the drag operation 
        /// </summary>
        private void TargetElement_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) 
        {
            _targetElement.ReleaseMouseCapture();
            _draggingActive = false; 
            OnDragCompleted(e, false);
        }
 
        /// <summary> 
        /// Handle the MouseLeftButtonDown event for the UIElement to start a drag operation
        /// </summary> 
        private void TargetElement_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            _start = e.GetPosition(_targetElement); 
            _draggingActive = _targetElement.CaptureMouse();

            // Only start the drag operation if CaptureMouse succeeded 
            if (_draggingActive) 
            {
                OnDragStarted(); 
            }
        }
 
        /// <summary>
        /// Invoke the DragStartedEvent handlers
        /// </summary> 
        private void OnDragStarted() 
        {
            EventHandler<DragStartedEventArgs> handler = this.DragStartedEvent; 
            if (handler != null)
            {
                handler(_targetElement, new DragStartedEventArgs(0.0, 0.0)); 
            }
        }
 
        /// <summary> 
        /// Invoke the DragDeltaEvent handlers
        /// </summary> 
        private void OnDragDelta(MouseEventArgs e)
        {
            EventHandler<DragDeltaEventArgs> handler = this.DragDeltaEvent; 
            if (handler != null)
            {
                Point p = e.GetPosition(_targetElement); 
                handler(_targetElement, new DragDeltaEventArgs(p.X - _start.X, p.Y - _start.Y)); 
            }
        } 

        /// <summary>
        /// Invoke the DragCompletedEvent handlers 
        /// </summary>
        private void OnDragCompleted(MouseEventArgs e, bool canceled)
        { 
            EventHandler<DragCompletedEventArgs> handler = this.DragCompletedEvent; 
            if (handler != null)
            { 
                Point endPoint;
                if (canceled)
                { 
                    endPoint = new Point();
                }
                else 
                { 
                    Point p = e.GetPosition(_targetElement);
                    endPoint = new Point(p.X - _start.X, p.Y - _start.Y); 
                }
                handler(_targetElement, new DragCompletedEventArgs(endPoint.X, endPoint.Y, canceled));
            } 
        }
    }
} 
