package org.rstudio.studio.client.workbench.views.console.shell.assist;

import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArrayString;

import org.rstudio.core.client.JsArrayUtil;
import org.rstudio.studio.client.common.filetypes.TextFileType;
import org.rstudio.studio.client.workbench.views.source.editors.text.AceEditor;
import org.rstudio.studio.client.workbench.views.source.editors.text.DocDisplay;
import org.rstudio.studio.client.workbench.views.source.editors.text.ace.AceEditorNative;

import java.util.ArrayList;

public class SnippetHelper
{
   static class SnippetManager extends JavaScriptObject
   {
      protected SnippetManager() {}
   }
   
   public SnippetHelper(AceEditor editor)
   {
      editor_ = editor;
      native_ = editor.getWidget().getEditor();
      manager_ = getSnippetManager();
   }
   
   private static final native SnippetManager getSnippetManager() /*-{
      return $wnd.require("ace/snippets").snippetManager;
   }-*/;
   
   public ArrayList<String> getCppSnippets()
   {
      ensureSnippetsLoaded();
      return JsArrayUtil.fromJsArrayString(
            getAvailableSnippetsImpl(manager_, "c_cpp"));
   }
   
   public ArrayList<String> getAvailableSnippets()
   {
      ensureSnippetsLoaded();
      
      String mode = editor_.getLanguageMode(
            editor_.getCursorPosition());
      
      if (mode == null)
         mode = "r";
      
      return JsArrayUtil.fromJsArrayString(
            getAvailableSnippetsImpl(manager_, mode));
   }
   
   private final void ensureSnippetsLoaded()
   {
      ensureAceSnippetsLoaded("r", manager_);
      ensureAceSnippetsLoaded("c_cpp", manager_);
   }
   
   private static final native void ensureAceSnippetsLoaded(
         String mode,
         SnippetManager manager) /*-{

      var snippetsForMode = manager.snippetNameMap[mode];
      if (!snippetsForMode) {
         var id = "ace/snippets/" + mode;
         var m = $wnd.require(id);
         if (!m) {
            console.log("Failed load Ace snippets for mode '" + mode + "'");
            return;
         }
         
         if (!manager.files)
            manager.files = {};
            
         manager.files[id] = m;
         if (!m.snippets && m.snippetText)
         m.snippets = manager.parseSnippetFile(m.snippetText);
         manager.register(m.snippets || [], m.scope);
      }
      
   }-*/;
   
   public void applySnippet(String token, String snippetName)
   {
      editor_.expandSelectionLeft(token.length());
      applySnippetImpl(snippetName, manager_, editor_.getWidget().getEditor());
   }
   
   public final native void applySnippetImpl(
         String snippetName,
         SnippetManager manager,
         AceEditorNative editor) /*-{
      var content = manager.getSnippetByName(snippetName, editor).content;
      manager.insertSnippet(editor, content);
   }-*/;
   
   private static final native JsArrayString getAvailableSnippetsImpl(
         SnippetManager manager,
         String mode) /*-{
      var snippetsForMode = manager.snippetNameMap[mode];
      if (snippetsForMode)
         return Object.keys(snippetsForMode);
      return [];
   }-*/;

   private final AceEditor editor_;
   private final AceEditorNative native_;
   private final SnippetManager manager_;
}

@@ -849,21 +849,60 @@
 ager();%0A
+      customCppSnippetsAdded_ = false;%0A
    %7D%0A
-
    %0A   p
@@ -1115,24 +1115,62 @@
 tsLoaded();%0A
+      ensureCppCustomSnippetsAdded();%0A
       return
@@ -1255,24 +1255,24 @@
  %22c_cpp%22));%0A
-
    %7D%0A   %0A   
@@ -1260,32 +1260,965 @@
 pp%22));%0A   %7D%0A   %0A
+   private void ensureCppCustomSnippetsAdded()%0A   %7B%0A      if (!customCppSnippetsAdded_)%0A      %7B%0A         addCustomCppSnippets(manager_);%0A         customCppSnippetsAdded_ = true;%0A      %7D%0A   %7D%0A   %0A   private final native void addCustomCppSnippets(SnippetManager manager)%0A   /*-%7B%0A      var snippetText = %5B%0A         %22## Header guard%22,%0A         %22snippet once%22,%0A         %22%5Ct#ifndef $%7B1%7D%22,%0A         %22%5Ct#define $%7B1%7D%22,%0A         %22%22,%0A         %22%5Ct$%7B0%7D%22,%0A         %22%22,%0A         %22%5Ct#endif // $%7B1%7D%22,%0A         %22##%22,%0A         %22## Anonymous namespace%22,%0A         %22snippet ans%22,%0A         %22%5Ctnamespace %7B%22,%0A         %22%5Ct$%7B0%7D%22,%0A         %22%5Ct%7D // anonymous namespace%22,%0A         %22##%22,%0A         %22## Named namespace%22,%0A         %22snippet ns%22,%0A         %22%5Ctnamespace $%7B1%7D %7B%22,%0A         %22%5Ct$%7B0%7D%22,%0A         %22%5Ct%7D // namespace $%7B1%7D%22%0A      %5D.join(%22%5Cn%22);%0A      %0A      var parsed = manager.parseSnippetFile(snippetText);%0A      manager.register(parsed, %22c_cpp%22);%0A   %7D-*/;%0A   %0A
    public ArrayL
@@ -4317,10 +4317,54 @@
 nager_;%0A
+   private boolean customCppSnippetsAdded_;%0A
 %7D%0A

embed custom snippets for C++