package com.intellij.bash.formatter;

import com.intellij.bash.psi.BashFile;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.configurations.GeneralCommandLine;
import com.intellij.execution.process.CapturingProcessAdapter;
import com.intellij.execution.process.OSProcessHandler;
import com.intellij.execution.process.ProcessEvent;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.WriteAction;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.command.undo.UndoConstants;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import com.intellij.psi.codeStyle.ExternalFormatProcessor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.File;

// todo: rewrite with the future API, see IDEA-203568
public class BashExternalFormatter implements ExternalFormatProcessor {
  @Override
  public boolean activeForFile(@NotNull PsiFile file) {
    return file instanceof BashFile;
  }

  @Nullable
  @Override
  public TextRange format(@NotNull PsiFile source, @NotNull TextRange range, boolean canChangeWhiteSpacesOnly) {
    doFormat(source.getProject(), source.getVirtualFile());
    return range;
  }

  private void doFormat(@NotNull Project project, @Nullable VirtualFile file) {
    if (file == null || !file.exists()) return;
    String shFmtExecutable = Registry.stringValue("bash.shfmt.path");
    if (StringUtil.isEmpty(shFmtExecutable)) return;
    String filePath = file.getPath();
    String realPath = FileUtil.toSystemDependentName(filePath);
    if (!new File(realPath).exists()) return;

    FileDocumentManager documentManager = FileDocumentManager.getInstance();
    Document document = FileDocumentManager.getInstance().getDocument(file);
    if (document == null) return;

    long before = document.getModificationStamp();
    documentManager.saveDocument(document);

    try {
      GeneralCommandLine commandLine = new GeneralCommandLine()
          .withParentEnvironmentType(GeneralCommandLine.ParentEnvironmentType.CONSOLE)
          .withExePath(shFmtExecutable)
          .withParameters(realPath);

      OSProcessHandler handler = new OSProcessHandler(commandLine);
      handler.addProcessListener(new CapturingProcessAdapter() {
        @Override
        public void processTerminated(@NotNull ProcessEvent event) {
          int exitCode = event.getExitCode();
          if (exitCode == 0) {
            String text = getOutput().getStdout();
            ApplicationManager.getApplication().invokeLater(() -> {
              long after = document.getModificationStamp();
              if (after > before) {
                // todo: document has already been changed
                return;
              }
              CommandProcessor.getInstance().executeCommand(project, () -> {
                WriteAction.run(() -> {
                  document.setText(text);
                  FileDocumentManager.getInstance().saveDocument(document);
                });
                file.putUserData(UndoConstants.FORCE_RECORD_UNDO, null);
              }, "Reformat Code with " + getId(), null, document);
            });
          }
          else {
            showFailedNotification();
          }
        }
      });
      handler.startNotify();
    }
    catch (ExecutionException e) {
      showFailedNotification();
    }
  }

  private static void showFailedNotification() {
    // todo: add notification
  }

  @NotNull
  @Override
  public String getId() {
    return "shfmt";
  }
}

@@ -31,16 +31,67 @@
 atter;%0A%0A
+import com.intellij.application.options.CodeStyle;%0A
 import c
@@ -630,16 +630,63 @@
 stants;%0A
+import com.intellij.openapi.diagnostic.Logger;%0A
 import c
@@ -1093,24 +1093,113 @@
 si.PsiFile;%0A
+import com.intellij.psi.PsiManager;%0Aimport com.intellij.psi.codeStyle.CodeStyleSettings;%0A
 import com.i
@@ -1241,24 +1241,75 @@
 tProcessor;%0A
+import com.intellij.util.containers.ContainerUtil;%0A
 import org.j
@@ -1402,16 +1402,39 @@
 io.File;
+%0Aimport java.util.List;
 %0A%0A// tod
@@ -1553,16 +1553,102 @@
 essor %7B%0A
+  private final static Logger LOG = Logger.getInstance(BashExternalFormatter.class);%0A%0A
   @Overr
@@ -2650,24 +2650,471 @@
 document);%0A%0A
+    List%3CString%3E params = ContainerUtil.newSmartList();%0A    PsiFile psiFile = PsiManager.getInstance(project).findFile(file);%0A    CodeStyleSettings settings = psiFile == null ? null : CodeStyle.getSettings(psiFile);%0A    if (settings != null) %7B%0A      if (!settings.useTabCharacter(file.getFileType())) %7B%0A        int tabSize = settings.getIndentSize(file.getFileType());%0A        params.add(%22-i=%22 + tabSize);%0A      %7D%0A    %7D%0A    params.add(realPath);%0A%0A
     try %7B%0A  
@@ -3328,24 +3328,22 @@
 ameters(
-realPath
+params
 );%0A%0A    
@@ -4420,24 +4420,47 @@
 otification(
+getOutput().getStderr()
 );%0A         
@@ -4581,16 +4581,30 @@
 ication(
+e.getMessage()
 );%0A    %7D
@@ -4654,16 +4654,29 @@
 ication(
+String stderr
 ) %7B%0A    
@@ -4701,16 +4701,39 @@
 ication%0A
+    LOG.debug(stderr);%0A
   %7D%0A%0A  @

use code style settings