package org.rstudio.studio.client.common.satellite;

import java.util.ArrayList;

import org.rstudio.core.client.dom.WindowEx;
import org.rstudio.studio.client.workbench.model.Session;

import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.event.logical.shared.CloseEvent;
import com.google.gwt.event.logical.shared.CloseHandler;
import com.google.gwt.user.client.Window;
import com.google.inject.Inject;
import com.google.inject.Singleton;

// TODO: Implement for Desktop

// TODO: make sure gwt url is passed to the popup window

// TODO: re-activation of existing satellites (launch manager)

// TODO: detect when satellites have gone away and remove them from our
// list (could just wait until we see .closed or an exception during
// event delivery)

@Singleton
public class SatelliteManager implements CloseHandler<Window>
{
   @Inject
   public SatelliteManager(Session session)
   {
      session_ = session;
   }
   
   // the main window should call this method during startup to set itself
   // up to manage and communicate with the satellite windows
   public void initialize()
   {
      // export the registration hook used by satellites
      exportSatelliteRegistrationCallback();
      
      // handle onClosed to automatically close all satellites
      Window.addCloseHandler(this);
   }
    
   
   // close all satellite windows
   public void closeAllSatellites()
   {
      for (int i=0; i<satellites_.size(); i++)
      {
         try
         {
            WindowEx satelliteWnd = satellites_.get(i);
            satelliteWnd.close();
         }
         catch(Throwable e)
         {
         }
      } 
      satellites_.clear();  
   }
   
   // dispatch an event to all satellites
   public void dispatchEvent(JavaScriptObject clientEvent)
   {
      for (int i=0; i<satellites_.size(); i++)
      {
         try
         {
            WindowEx satelliteWnd = satellites_.get(i);
            callDispatchEvent(satelliteWnd, clientEvent);
         }
         catch(Throwable e)
         {
         }
      } 
   }
   
   // close all satellites when we are closed
   @Override
   public void onClose(CloseEvent<Window> event)
   {
      closeAllSatellites();
   }
   
   // called by satellites to connect themselves with the main window
   private void registerAsSatellite(JavaScriptObject wnd)
   {
      // get the satellite and add it to our list
      WindowEx satelliteWnd = wnd.<WindowEx>cast();
      satellites_.add(satelliteWnd);
      
      // call setSessionInfo
      callSetSessionInfo(satelliteWnd, session_.getSessionInfo());
   }
   
   // export the global function requried for satellites to register
   private native void exportSatelliteRegistrationCallback() /*-{
      var manager = this;     
      $wnd.registerAsRStudioSatellite = $entry(
         function(satelliteWnd) {
            manager.@org.rstudio.studio.client.common.satellite.SatelliteManager::registerAsSatellite(Lcom/google/gwt/core/client/JavaScriptObject;)(satelliteWnd);
         }
      ); 
   }-*/;
   
   // call setSessionInfo on a satellite
   private native void callSetSessionInfo(JavaScriptObject satellite,
                                          JavaScriptObject sessionInfo) /*-{
      satellite.setRStudioSatelliteSessionInfo(sessionInfo);
   }-*/;
   
   // dispatch event to a satellite
   private native void callDispatchEvent(JavaScriptObject satellite,
                                         JavaScriptObject clientEvent) /*-{
      satellite.dispatchEventToRStudioSatellite(clientEvent);
   }-*/;
   
   private final Session session_;
   private final ArrayList<WindowEx> satellites_ = new ArrayList<WindowEx>();

   
}



@@ -613,168 +613,8 @@
 r)%0A%0A
-// TODO: detect when satellites have gone away and remove them from our%0A// list (could just wait until we see .closed or an exception during%0A// event delivery)%0A
 %0A@Si
@@ -1640,93 +1640,392 @@
     
-for (int i=0; i%3Csatellites_.size(); i++)%0A      %7B%0A         try%0A      
+// list of windows to remove (because they were closed)%0A      ArrayList%3CWindowEx%3E removeWindows = null;%0A        %0A      // iterate over the satellites (make a copy to avoid races if%0A      // for some reason firing an event creates or destroys a satellite)%0A      @SuppressWarnings(%22unchecked%22)%0A
    
-%7B%0A
    
-   
+ArrayList%3CWindowEx%3E satellites = (ArrayList%3CWindowEx%3E)satellites_.clone();%0A
       
+for (
 Wind
@@ -2034,33 +2034,33 @@
 Ex satelliteWnd 
-=
+:
  satellites_.get
@@ -2050,34 +2050,312 @@
 : satellites
-_.get(i);%0A
+)%0A      %7B%0A         try%0A         %7B%0A            if (satelliteWnd.isClosed())%0A            %7B%0A               if (removeWindows == null)%0A                  removeWindows = new ArrayList%3CWindowEx%3E();%0A               removeWindows.add(satelliteWnd);%0A            %7D%0A            else%0A            %7B%0A   
             
@@ -2400,16 +2400,30 @@
 Event);%0A
+            %7D%0A
         
@@ -2484,16 +2484,230 @@
      %7D %0A
+      %0A      // remove windows if necessary%0A      if (removeWindows != null)%0A      %7B%0A         for (WindowEx satelliteWnd : removeWindows)%0A         %7B%0A            satellites_.remove(satelliteWnd);%0A         %7D%0A      %7D%0A
    %7D%0A   

remove satellites from the internal list once they are closed