/*
 * JavaScriptEventHistory.java
 *
 * Copyright (C) 2009-17 by RStudio, Inc.
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, 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.studio.client;

import org.rstudio.core.client.JsVector;
import org.rstudio.core.client.widget.MiniPopupPanel;

import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.inject.Singleton;

// This class maintains a simple JavaScript event history, primarily to be used
// by classes that need to know what sequence of unfortunate events might have
// led to the current application state (whatever that might be).

@Singleton
public class JavaScriptEventHistory
{
   public static class EventData extends JavaScriptObject
   {
      protected EventData()
      {
      }
      
      public static final native EventData create(NativeEvent event)
      /*-{
         return {
            "type"   : event.type,
            "button" : event.button
         };
      }-*/;
      
      public final native String getType() /*-{ return this["type"];   }-*/;
      public final native int getButton()  /*-{ return this["button"]; }-*/;
   }
   
   public interface Predicate
   {
      public boolean accept(EventData event);
   }
   
   public JavaScriptEventHistory()
   {
      queue_ = JavaScriptObject.createArray().cast();
      registerEventListeners();
   }
   
   private final native void registerEventListeners()
   /*-{
      var self = this;
      
      // get reference to document body
      var document = $doc;
      var body = document.body;
      
      // define our handler (we can just use a single one)
      var handler = $entry(function(event) {
         self.@org.rstudio.studio.client.JavaScriptEventHistory::onEvent(Lcom/google/gwt/dom/client/NativeEvent;)(event);
      });
      
      // define the events that we want to listen to
      var events = [
         "cut", "copy", "paste",
         "resize", "scroll",
         "keydown", "keypress", "keyup",
         "mouseenter", "mouseleave", "mouseover", "mousemove", "mouseout",
         "mousedown", "mouseup", "click", "dblclick", "contextmenu", "wheel",
         "drag", "dragstart", "dragend", "dragenter", "dragover", "dragleave", "drop"
      ];
         
      
      // iterate through all keys on body object, and
      // register handlers for any prefixed with 'on'
      for (var i = 0, n = events.length; i < n; i++) {
         body.addEventListener(events[i], handler, true)
      }
      
   }-*/;
   
   private void onEvent(NativeEvent event)
   {
      EventData eventData = EventData.create(event);
      queue_.unshift(eventData);
      queue_.setLength(QUEUE_LENGTH);
   }
   
   public EventData findEvent(Predicate predicate)
   {
      for (int i = 0, n = queue_.length(); i < n; i++)
         if (predicate.accept(queue_.get(i)))
            return queue_.get(i);
      return null;
   }
   
   // primarily for debug use (see what events are being emitted over time)
   private MiniPopupPanel historyPanel_ = null;
   public void debugHistory()
   {
      if (historyPanel_ == null)
         historyPanel_ = new MiniPopupPanel(false, false);
      
      VerticalPanel contentPanel = new VerticalPanel();
      contentPanel.add(new HTML("<h4 style='margin: 0;'>JavaScript Event History</h2><hr />"));
      for (int i = 0, n = Math.min(10, queue_.length()); i < n; i++)
      {
         EventData event = queue_.get(i);
         contentPanel.add(new HTML("Event: " + event.getType()));
      }
      
      historyPanel_.setWidget(contentPanel);
      if (!historyPanel_.isShowing())
      {
         historyPanel_.setPopupPosition(10, 10);
         historyPanel_.show();
      }
   }
   
   private final JsVector<EventData> queue_;
   
   private static final int QUEUE_LENGTH = 10;
}

@@ -633,41 +633,54 @@
 ort 
-org.rstudio.core.client.JsVector;
+java.util.LinkedList;%0Aimport java.util.Queue;%0A
 %0Aimp
@@ -1862,43 +1862,33 @@
 _ = 
-JavaScriptObject.createArray().cast
+new LinkedList%3CEventData%3E
 ();%0A
@@ -3177,15 +3177,11 @@
 ue_.
-unshift
+add
 (eve
@@ -3199,16 +3199,20 @@
     
+if (
 queue_.s
 etLe
@@ -3211,17 +3211,16 @@
 e_.s
-etLength(
+ize() %3E 
 QUEU
@@ -3228,16 +3228,41 @@
 _LENGTH)
+%0A         queue_.remove()
 ;%0A   %7D%0A 
@@ -3335,50 +3335,31 @@
 or (
-int i = 0, n = queue_.length(); i %3C n; i++
+EventData data : queue_
 )%0A  
@@ -3386,29 +3386,20 @@
 .accept(
-queue_.get(i)
+data
 ))%0A     
@@ -3412,29 +3412,20 @@
  return 
-queue_.get(i)
+data
 ;%0A      
@@ -3862,21 +3862,23 @@
 ;%0A      
-for (
+%0A      
 int i = 
@@ -3908,48 +3908,28 @@
 ue_.
-length()); i %3C n; i++)%0A      %7B%0A         
+size());%0A      for (
 Even
@@ -3944,23 +3944,66 @@
 ent 
-=
+:
  queue_
-.get(i)
+)%0A      %7B%0A         if (i++ == n)%0A            break
 ;%0A  
@@ -4290,16 +4290,13 @@
 nal 
-JsVector
+Queue
 %3CEve
@@ -4311,20 +4311,16 @@
 queue_;%0A
-   %0A
    priva

use Queue rather than Vector