package org.rstudio.studio.client.workbench.addins;

import com.google.gwt.user.client.Command;
import com.google.inject.Inject;
import com.google.inject.Singleton;

import org.rstudio.core.client.CommandWithArg;
import org.rstudio.core.client.Pair;
import org.rstudio.core.client.command.AddinCommandBinding;
import org.rstudio.core.client.command.EditorCommandManager.EditorKeyBindings;
import org.rstudio.core.client.command.KeyMap;
import org.rstudio.core.client.command.KeyMap.CommandBinding;
import org.rstudio.core.client.command.KeyMap.KeyMapType;
import org.rstudio.core.client.command.KeyboardShortcut.KeySequence;
import org.rstudio.core.client.command.ShortcutManager;
import org.rstudio.core.client.files.FileBacked;
import org.rstudio.studio.client.application.events.EventBus;
import org.rstudio.studio.client.workbench.addins.Addins.RAddin;
import org.rstudio.studio.client.workbench.addins.Addins.RAddins;
import org.rstudio.studio.client.workbench.addins.events.AddinRegistryUpdatedEvent;
import org.rstudio.studio.client.workbench.events.SessionInitEvent;
import org.rstudio.studio.client.workbench.events.SessionInitHandler;
import org.rstudio.studio.client.workbench.model.Session;

import java.util.ArrayList;
import java.util.List;

@Singleton
public class AddinsCommandManager
{
   @Inject
   public AddinsCommandManager(EventBus events, 
                               final Session session)
   {
      events_ = events;
     
      bindings_ = new FileBacked<EditorKeyBindings>(
            KEYBINDINGS_PATH,
            false,
            EditorKeyBindings.create());
      
      // load addin bindings on session init
      events_.addHandler(
            SessionInitEvent.TYPE,
            new SessionInitHandler()
            {
               @Override
               public void onSessionInit(SessionInitEvent sie)
               {
                  rAddins_ = session.getSessionInfo().getAddins();
                  loadBindings();
               }
            });
      
      // set bindings when updated (e.g. through ModifyKeyboardShortcuts widget)
      events_.addHandler(
            AddinsKeyBindingsChangedEvent.TYPE,
            new AddinsKeyBindingsChangedEvent.Handler()
            {
               @Override
               public void onAddinsKeyBindingsChanged(AddinsKeyBindingsChangedEvent event)
               {
                  registerBindings(event.getBindings(), null);
               }
            });
      
      // update addin bindings when registry populated
      events_.addHandler(
            AddinRegistryUpdatedEvent.TYPE,
            new AddinRegistryUpdatedEvent.Handler()
            {
               @Override
               public void onAddinRegistryUpdated(AddinRegistryUpdatedEvent event)
               {
                  rAddins_ = event.getData();
                  loadBindings();
               }
            });
   }
   
   public void addBindingsAndSave(final EditorKeyBindings newBindings,
                                  final CommandWithArg<EditorKeyBindings> onLoad)
   {
      bindings_.execute(new CommandWithArg<EditorKeyBindings>()
      {
         @Override
         public void execute(EditorKeyBindings currentBindings)
         {
            currentBindings.insert(newBindings);
            bindings_.set(currentBindings, new Command()
            {
               @Override
               public void execute()
               {
                  loadBindings(onLoad);
               }
            });
         }
      });
   }
   
   public void loadBindings()
   {
      loadBindings(null);
   }
   
   public void loadBindings(final CommandWithArg<EditorKeyBindings> afterLoad)
   {
      bindings_.execute(new CommandWithArg<EditorKeyBindings>()
      {
         @Override
         public void execute(EditorKeyBindings bindings)
         {
            registerBindings(bindings, afterLoad);
         }
      });
   }
   
   private void registerBindings(final EditorKeyBindings bindings,
                                 final CommandWithArg<EditorKeyBindings> afterLoad)
   {
      List<Pair<List<KeySequence>, CommandBinding>> commands =
            new ArrayList<Pair<List<KeySequence>, CommandBinding>>();
   
      for (String id : bindings.iterableKeys())
      {
         List<KeySequence> keyList = bindings.get(id).getKeyBindings();
         RAddin addin = rAddins_.get(id);
         if (addin == null)
            continue;
         CommandBinding binding = new AddinCommandBinding(addin);
         commands.add(new Pair<List<KeySequence>, CommandBinding>(keyList, binding));
      }
   
      KeyMap map = ShortcutManager.INSTANCE.getKeyMap(KeyMapType.ADDIN);
      for (int i = 0; i < commands.size(); i++)
      {
         map.setBindings(
               commands.get(i).first,
               commands.get(i).second);
      }
   
      if (afterLoad != null)
         afterLoad.execute(bindings);
   }
   
   public void resetBindings()
   {
      resetBindings(null);
   }
   
   public void resetBindings(final Command afterReset)
   {
      bindings_.set(EditorKeyBindings.create(), new Command()
      {
         @Override
         public void execute()
         {
            if (afterReset != null)
               afterReset.execute();
         }
      });
   }
   
   public RAddins getRAddins()
   {
      return rAddins_;
   }
   
   private RAddins rAddins_ = RAddins.create().cast(); 
   
   private final FileBacked<EditorKeyBindings> bindings_;
   private static final String KEYBINDINGS_PATH = "~/.R/rstudio/keybindings/addins.json";
   
   
   // Injected ----
   private EventBus events_;
}
