content: /**
   * Copied and adapted from plugin
   * <a href="https://github.com/neueda/jetbrains-plugin-graph-database-support">Graph Database Support</a>
   * by Neueda Technologies, Ltd.
   * Modified by Alberto Venturini, 2022
   */
  package com.albertoventurini.graphdbplugin.jetbrains.ui.console.params;
  
  import com.albertoventurini.graphdbplugin.jetbrains.component.datasource.DataSourcesComponent;
  import com.albertoventurini.graphdbplugin.jetbrains.util.FileUtil;
  import com.google.common.base.Throwables;
  import com.intellij.codeInsight.hint.HintManager;
  import com.intellij.icons.AllIcons;
  import com.intellij.json.JsonFileType;
  import com.intellij.openapi.application.ApplicationManager;
  import com.intellij.openapi.components.ServiceManager;
  import com.intellij.openapi.editor.Document;
  import com.intellij.openapi.editor.Editor;
  import com.intellij.openapi.editor.EditorFactory;
  import com.intellij.openapi.fileEditor.*;
  import com.intellij.openapi.fileEditor.impl.EditorTabPresentationUtil;
  import com.intellij.openapi.project.Project;
  import com.intellij.openapi.vfs.VirtualFile;
  import com.intellij.openapi.vfs.VirtualFileEvent;
  import com.intellij.openapi.vfs.VirtualFileListener;
  import com.intellij.openapi.vfs.VirtualFileManager;
  import com.intellij.testFramework.LightVirtualFile;
  import com.intellij.util.messages.MessageBus;
  import com.intellij.util.messages.MessageBusConnection;
  import com.albertoventurini.graphdbplugin.jetbrains.ui.console.GraphConsoleView;
  import com.albertoventurini.graphdbplugin.jetbrains.ui.console.event.QueryParametersRetrievalErrorEvent;
  import com.albertoventurini.graphdbplugin.language.cypher.util.FileTypeExtensionUtil;
  import org.jetbrains.annotations.NotNull;
  
  import javax.swing.*;
  import java.awt.*;
  
  import static com.albertoventurini.graphdbplugin.jetbrains.ui.console.event.QueryParametersRetrievalErrorEvent.*;
  
  public class ParametersPanel implements ParametersProvider {
  
      private static final FileDocumentManager FILE_DOCUMENT_MANAGER = FileDocumentManager.getInstance();
      public static final Icon ICON_HELP = AllIcons.Actions.Help;
  
      private Editor globalParamEditor;
      private Editor fileSpecificParamEditor;
      private GraphConsoleView graphConsoleView;
      private MessageBus messageBus;
      private ParametersService service;
      private Project project;
  
      public void initialize(GraphConsoleView graphConsoleView, Project project) {
          this.graphConsoleView = graphConsoleView;
          this.messageBus = project.getMessageBus();
          this.service = project.getService(ParametersService.class);
          this.project = project;
          setupEditor(project);
          FileEditor selectedEditor = FileEditorManager.getInstance(project).getSelectedEditor();
          if (selectedEditor != null && selectedEditor.getFile() != null &&
                  FileTypeExtensionUtil.isCypherFileTypeExtension(selectedEditor.getFile().getExtension())) {
              setupFileSpecificEditor(project, selectedEditor.getFile());
          }
      }
  
      public String getGlobalParametersJson() {
          return globalParamEditor.getDocument().getText();
      }
  
      public String getFileSpecificParametersJson() {
          return fileSpecificParamEditor != null ? fileSpecificParamEditor.getDocument().getText() : null;
      }
  
      private void initializeUi() {
          graphConsoleView.getGlobalParametersTab().add(globalParamEditor.getComponent(), BorderLayout.CENTER);
          service.registerParametersProvider(this);
          MessageBusConnection mbConnection = messageBus.connect();
          mbConnection.subscribe(QueryParametersRetrievalErrorEvent.QUERY_PARAMETERS_RETRIEVAL_ERROR_EVENT_TOPIC,
                  (exception, editor) -> {
                      if (editor == null) {
                          return;
                      }
                      String errorMessage;
                      if (exception.getMessage() != null) {
                          errorMessage = String.format("%s: %s", PARAMS_ERROR_COMMON_MSG, exception.getMessage());
                      } else {
                          errorMessage = PARAMS_ERROR_COMMON_MSG;
                      }
                      HintManager.getInstance().showErrorHint(editor, errorMessage);
                  });
  
          mbConnection.subscribe(FileEditorManagerListener.FILE_EDITOR_MANAGER, new FileEditorManagerListener() {
              // If file opened, fileOpenedSync->selectionChanged->fileOpened are called
              @Override
              public void selectionChanged(@NotNull FileEditorManagerEvent event) {
                  if (event.getOldFile() != null) {
                      releaseFileSpecificEditor(event.getOldFile());
                  }
                  VirtualFile newFile = event.getNewFile();
                  if (newFile != null && FileTypeExtensionUtil.isCypherFileTypeExtension(newFile.getExtension()) &&
                          project.getService(DataSourcesComponent.class).getDataSourceContainer().isDataSourceExists(getTabTitle(newFile))) {
                      setupFileSpecificEditor(project, newFile);
                  }
              }
          });
      }
  
      private void setupEditor(Project project) {
          ApplicationManager.getApplication().runWriteAction(() -> {
              try {
                  VirtualFile file = FileUtil.getScratchFile(project, "Neo4jGraphDbConsoleParametersPanel.json");
                  Document document = FILE_DOCUMENT_MANAGER.getDocument(file);
                  globalParamEditor = createEditor(project, document);
                  JLabel jLabel = new JLabel("<html>Global parameters:</html>");
                  jLabel.setIcon(ICON_HELP);
                  jLabel.setToolTipText("Enter parameters in JSON format. Will be applied to any data source when executed");
                  globalParamEditor.setHeaderComponent(jLabel);
                  setInitialContent(document);
  
                  initializeUi();
              } catch (Throwable e) {
                  Throwables.throwIfUnchecked(e);
                  throw new RuntimeException(e);
              }
          });
      }
  
      private void setupFileSpecificEditor(Project project, VirtualFile cypherFile) {
          if (project == null || cypherFile == null) {
              return;
          }
          try {
              String params = FileUtil.getParams(cypherFile);
              LightVirtualFile lightVirtualFile = new LightVirtualFile("", JsonFileType.INSTANCE, params);
              Document document = FileDocumentManager.getInstance().getDocument(lightVirtualFile);
              fileSpecificParamEditor = createEditor(project, document);
              VirtualFileManager.getInstance().addVirtualFileListener(new VirtualFileListener() {
                  @Override
                  public void contentsChanged(@NotNull VirtualFileEvent event) {
                      if (event.getFile().equals(cypherFile) && document != null) {
                          FileUtil.setParams(cypherFile, document.getText());
                      }
                  }
              });
              JLabel jLabel = new JLabel("<html>Parameters for data source <b>" +
                      getTabTitle(cypherFile) + "</b>:</html>");
              jLabel.setIcon(ICON_HELP);
              jLabel.setToolTipText("Enter parameters in JSON format. Will be applied to <b>" + getTabTitle(cypherFile) +
                      "</b> data source when executed");
              fileSpecificParamEditor.setHeaderComponent(jLabel);
              if (document != null) {
                  setInitialContent(document);
              }
              graphConsoleView.getFileSpecificParametersTab().add(fileSpecificParamEditor.getComponent(), BorderLayout.CENTER);
          } catch (Throwable e) {
              Throwables.throwIfUnchecked(e);
              throw new RuntimeException(e);
          }
      }
  
      private void releaseFileSpecificEditor(VirtualFile oldFile) {
          if (fileSpecificParamEditor != null) {
              // set params in fileSpecificParamDocument.addDocumentListener() listener is a more universal but worse solution:
              // gets triggered immediately, debounce required
              FileUtil.setParams(oldFile, fileSpecificParamEditor.getDocument().getText());
              graphConsoleView.getFileSpecificParametersTab().remove(fileSpecificParamEditor.getComponent());
              if (!fileSpecificParamEditor.isDisposed()) {
                  EditorFactory.getInstance().releaseEditor(fileSpecificParamEditor);
              }
          }
          fileSpecificParamEditor = null;
      }
  
      private String getTabTitle(VirtualFile file) {
  //        Window window = WindowManagerEx.getInstanceEx().getMostRecentFocusedWindow();
  //        EditorWindow editorWindow = FileEditorManagerEx.getInstanceEx(project).getSplittersFor(window).getCurrentWindow();
  //        return EditorTabPresentationUtil.getEditorTabTitle(project, file, editorWindow);
          return EditorTabPresentationUtil.getEditorTabTitle(project, file);
      }
  
      private void setInitialContent(Document document) {
          if (document != null && document.getText().isEmpty()) {
              final Runnable setTextRunner = () -> document.setText("{}");
              ApplicationManager.getApplication()
                      .invokeLater(() -> ApplicationManager.getApplication().runWriteAction(setTextRunner));
          }
      }
  
      private static Editor createEditor(Project project, Document document) {
          return EditorFactory.getInstance().createEditor(document, project, JsonFileType.INSTANCE, false);
      }
  
  }
  