//--------------------------------------------------------------------------------------
        /// <summary>
        /// ResolveGestures - Touch events have to coordinated with other touches over time to
        ///                   generate the proper gestures and resolve ambiguity.  Call this on
        ///                   every frame to properly turn recent touches into gestures
        /// </summary>
        //--------------------------------------------------------------------------------------
        public void ResolveGestures(GameTime gameTime)
        {
            IList <VarmintWidget> hitList = null;
            Action <Func <VarmintWidget, EventHandledState> > processHit = (tryEvent) =>
            {
                for (int i = hitList.Count - 1; i >= 0; i--)
                {
                    if (tryEvent(hitList[i]) == EventHandledState.Handled)
                    {
                        break;
                    }
                }
            };

            // Handle orphaned touches
            for (int i = 0; i < _currentTouches.Count;)
            {
                var touchMemory = _currentTouches[i];
                if ((gameTime.TotalGameTime - touchMemory.LastUpdateTime.TotalGameTime).TotalSeconds > TouchOrphanTimeoutSeconds)
                {
                    if (touchMemory.GestureType == GestureType.FreeDrag)
                    {
                        hitList = touchMemory.StartWidgets;
                        processHit(w => w.HandleDragCancel());
                    }
                    Debug.WriteLine("BYE");
                    _currentTouches.RemoveAt(i);
                    continue;
                }
                i++;
            }

            if (_tapInReserve != null)
            {
                Debug.WriteLine("TR:" + _tapInReserve.SecondsAfterStart(gameTime));
                if (_tapInReserve.SecondsAfterStart(gameTime) >= DoubleTapIntervalSeconds)
                {
                    var tapLocation = _tapInReserve.CurrentTouch.Position;
                    hitList = HitTest(tapLocation);
                    processHit(w => w.HandleTap(tapLocation));
                    _tapInReserve = null;
                }
            }

            // One touch could be dragging or holding
            if (_currentTouches.Count == 1)
            {
                var touchMemory = _currentTouches[0];

                if (touchMemory.UnresolvedCount > 0 &&
                    touchMemory.TotalDistance > DragLengthThreshhold &&
                    touchMemory.SecondsAfterStart(gameTime) >= FlickThreshholdSeconds)
                {
                    touchMemory.GestureType = GestureType.FreeDrag;
                    hitList = touchMemory.StartWidgets;
                    processHit(w => w.HandleDrag(touchMemory.CurrentTouch.Position,
                                                 touchMemory.CurrentTouch.Position - touchMemory.LastUnresolvedTouch.Position));
                    touchMemory.MarkResolved();
                }
                else if (touchMemory.TotalDistance < DragLengthThreshhold &&
                         touchMemory.SecondsAfterStart(gameTime) >= ContextHoldThreshholdSeconds)
                {
                    hitList = touchMemory.StartWidgets;
                    // if we haven't started reporting as a hold, report as a hold
                    // and do a context tap
                    if (touchMemory.GestureType != GestureType.Hold)
                    {
                        processHit(w => w.HandleContextTap(touchMemory.CurrentTouch.Position));
                        touchMemory.GestureType = GestureType.Hold;
                        // TODO: Do a haptic vibrate here, using a native method in the game runner
                    }
                    // TODO: Process extended hold events here when needed
                }
            }
        }
        //--------------------------------------------------------------------------------------
        /// <summary>
        /// ReportTouch - This communicates raw touch events to controls and remembers gestures
        /// </summary>
        //--------------------------------------------------------------------------------------
        public void ReportTouch(TouchLocation touch, GameTime gameTime)
        {
            TouchMemory touchMemory = null;
            int         touchIndex  = -1;

            for (int i = 0; i < _currentTouches.Count; i++)
            {
                var trackedTouch = _currentTouches[i];
                if (trackedTouch.FirstTouch.Id == touch.Id)
                {
                    touchMemory = trackedTouch;
                    touchIndex  = i;
                    break;
                }
            }

            if (touchMemory == null)
            {
                touchMemory = new TouchMemory();
                touchMemory.TouchStartTime = gameTime;
                touchIndex = _currentTouches.Count;
                _currentTouches.Add(touchMemory);
            }

            touchMemory.LastUpdateTime = gameTime;

            // If this is a tracked touch, but there is no movement,
            // then do nothing.
            if (touch.State == TouchLocationState.Moved &&
                touchMemory.TouchCount > 0 &&
                touchMemory.CurrentTouch.Position == touch.Position)
            {
                return;
            }

            touchMemory.AddTouch(touch);
            var hitList = HitTest(touch.Position);
            Action <Func <VarmintWidget, EventHandledState> > processHit = (tryEvent) =>
            {
                for (int i = hitList.Count - 1; i >= 0; i--)
                {
                    if (tryEvent(hitList[i]) == EventHandledState.Handled)
                    {
                        break;
                    }
                }
            };

            switch (touch.State)
            {
            case TouchLocationState.Invalid:
            case TouchLocationState.Released:
                processHit(w => w.HandleTouchUp(touch));

                foreach (var widget in touchMemory.PreviousWidgets)
                {
                    if (!widget.PointIsInsideMe(touch.Position))
                    {
                        widget.HandleTouchMove(TouchMoveType.Leave, touch, touchMemory.PreviousTouch);
                    }
                }

                if (touchMemory.TotalDistance > DragLengthThreshhold)
                {
                    if (touchMemory.SecondsAfterStart(gameTime) < FlickThreshholdSeconds)
                    {
                        var location = touchMemory.FirstTouch.Position;
                        hitList = HitTest(location);
                        processHit(w => w.HandleFlick(location, touchMemory.CurrentTouch.Position - location));
                    }
                    else
                    {
                        hitList = touchMemory.StartWidgets;
                        processHit(w => w.HandleDragComplete());
                    }
                }
                else if (_tapInReserve != null)
                {
                    var oldLocation = _tapInReserve.CurrentTouch.Position;
                    if ((touch.Position - oldLocation).Length() < DoubleTapRadius)
                    {
                        processHit(w => w.HandleDoubleTap(oldLocation));
                    }
                    else
                    {
                        processHit(w => w.HandleTap(oldLocation));
                        processHit(w => w.HandleTap(touch.Position));
                    }
                    _tapInReserve = null;
                }
                else
                {
                    Debug.WriteLine("+TR");
                    _tapInReserve = touchMemory;
                }

                _currentTouches.RemoveAt(touchIndex);

                break;

            case TouchLocationState.Pressed:
                processHit(w =>
                {
                    // Remember this widget was touched so we can process a leave correctly later.
                    touchMemory.AddTouchedWidget(w);
                    var returnValue = w.HandleTouchDown(touch);
                    if (returnValue == EventHandledState.Handled)
                    {
                        w.Focus();
                    }
                    return(returnValue);
                });

                for (int i = 0; i < hitList.Count; i++)
                {
                    touchMemory.AddStartWidget(hitList[i]);
                }

                break;

            case TouchLocationState.Moved:
                var previousControls = new List <VarmintWidget>(touchMemory.PreviousWidgets);
                touchMemory.ClearPreviousWidgets();
                processHit(w =>
                {
                    // If the old hitlist contains this widget, it's a move,  otherwise
                    // it's an enter
                    touchMemory.AddTouchedWidget(w);
                    var moveType = TouchMoveType.Enter;
                    if (previousControls.Contains(w))
                    {
                        previousControls.Remove(w);
                        moveType = TouchMoveType.Move;
                    }
                    return(w.HandleTouchMove(moveType, touch, touchMemory.PreviousTouch));
                });

                // Any controls left over?  The touch left those
                foreach (var control in previousControls)
                {
                    control.HandleTouchMove(TouchMoveType.Leave, touch, touchMemory.PreviousTouch);
                }

                break;
            }
        }