/*
 * MouseTracker.java
 *
 * Copyright (C) 2022 by Posit Software, PBC
 *
 * Unless you have received this program directly from Posit Software pursuant
 * to the terms of a commercial license agreement with Posit Software, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */
package org.rstudio.core.client;

import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Event.NativePreviewEvent;
import com.google.gwt.user.client.Event.NativePreviewHandler;
import com.google.inject.Singleton;

@Singleton
public class MouseTracker
{
   public MouseTracker()
   {
      Event.addNativePreviewHandler(new NativePreviewHandler()
      {
         @Override
         public void onPreviewNativeEvent(NativePreviewEvent preview)
         {
            int type = preview.getTypeInt();
            if ((type & Event.MOUSEEVENTS) == 0)
            {
               lastEventType_ = type;
               return;
            }

            NativeEvent event = preview.getNativeEvent();

            // Safari triggers certain mouse events when modifier keys are
            // pressed. Suppress those as they can cause unexpected behavior
            // when e.g. typing in a textbox while the mouse happens to lie
            // over a popup panel (e.g. C++ completions, Find in Files)
            if (isSafariModifierKeyCausingMouseEvent(event, type))
            {
               event.stopPropagation();
               event.preventDefault();
            }

            lastEventType_ = type;
            lastMouseX_ = event.getClientX();
            lastMouseY_ = event.getClientY();
         }
      });
   }

   private boolean isSafariModifierKeyCausingMouseEvent(NativeEvent event,
                                                        int type)
   {
      if (!BrowseCap.isSafari())
         return false;

      // If the last event was a key event, and we just got a mouse event,
      // suppress it. (The intention here is to suppress a 'mouseout' or
      // 'mouseover' event synthesized in respond to a modifier keypress.
      //
      // Note that these events can be emitted in response to a keyup as
      // well; in such a case the associated modifier key will no longer
      // be held down and so we just globally suppress 'mouseover' and
      // 'mouseout' following a keyevent.
      if ((lastEventType_ & Event.KEYEVENTS) != 0 &&
          (type == Event.ONMOUSEOVER || type == Event.ONMOUSEOUT))
      {
         suppressing_ = true;
         return true;
      }

      // Stop suppressing once we get a new non-mouseover/mouseout event.
      else if (type != Event.ONMOUSEOVER && type != Event.ONMOUSEOUT)
      {
         suppressing_ = false;
         return false;
      }

      // For other intermediate events, return status of suppressing flag.
      return suppressing_;
   }

   public int getLastMouseX()
   {
      return lastMouseX_;
   }

   public int getLastMouseY()
   {
      return lastMouseY_;
   }

   private int lastMouseX_;
   private int lastMouseY_;

   private int lastEventType_;
   private boolean suppressing_;
}