/*
 * External Code Formatter
 * Copyright (c) 2007 Esko Luontola, www.orfjackal.net
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.orfjackal.extformatter.plugin;

import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import com.intellij.psi.impl.source.codeStyle.CodeStyleManagerEx;
import com.intellij.util.IncorrectOperationException;
import net.orfjackal.extformatter.CodeFormatter;
import org.jetbrains.annotations.NotNull;

import java.io.File;

/**
 * @author Esko Luontola
 * @since 3.12.2007
 */
public class ExternalizedCodeStyleManager extends DelegatingCodeStyleManager {

    @NotNull private final CodeFormatter replacement;

    public ExternalizedCodeStyleManager(@NotNull CodeStyleManagerEx original, @NotNull CodeFormatter replacement) {
        super(original);
        this.replacement = replacement;
    }

    public void reformatText(@NotNull PsiFile file, int startOffset, int endOffset) throws IncorrectOperationException {
        if (wholeFile(file, startOffset, endOffset) && canReformat(file)) {
            // TODO: does not synchronize the file with filesystem
            // TODO: queue all files so that they can be formatted with one command 
            replacement.reformatFile(physicalFile(file));
        } else {
            super.reformatText(file, startOffset, endOffset);
        }
    }

    private static boolean canReformat(@NotNull PsiFile file) {
        VirtualFile virtualFile = file.getVirtualFile();
        return virtualFile != null && canReformat(virtualFile);
    }

    private static boolean wholeFile(@NotNull PsiFile file, int startOffset, int endOffset) {
        return startOffset == 0
                && endOffset == file.getTextRange().getEndOffset();
    }

    @NotNull
    private static File physicalFile(@NotNull PsiFile psiFile) {
        VirtualFile virtualFile = psiFile.getVirtualFile();
        assert virtualFile != null;
        return new File(virtualFile.getPath());
    }

    public static boolean canReformat(@NotNull VirtualFile... selection) {
        return notEmpty(selection)
                && allAreInLocalFileSystem(selection)
                && allAreWritable(selection)
                && (allFileTypesAreSupported(selection) || isOneDirectory(selection));
    }

    private static boolean notEmpty(@NotNull VirtualFile[] selection) {
        return selection.length > 0;
    }

    private static boolean allAreInLocalFileSystem(@NotNull VirtualFile[] selection) {
        for (VirtualFile file : selection) {
            if (!file.isInLocalFileSystem()) {
                return false;
            }
        }
        return true;
    }

    private static boolean allAreWritable(@NotNull VirtualFile[] selection) {
        for (VirtualFile file : selection) {
            if (!file.isWritable()) {
                return false;
            }
        }
        return true;
    }

    private static boolean allFileTypesAreSupported(@NotNull VirtualFile[] selection) {
        for (VirtualFile file : selection) {
            // TODO: ask the formatter if it supports the file type
            if (!file.getFileType().equals(StdFileTypes.JAVA)) {
                return false;
            }
        }
        return true;
    }

    private static boolean isOneDirectory(@NotNull VirtualFile[] selection) {
        return selection.length == 1
                && selection[0].isDirectory();
    }
}

@@ -685,16 +685,76 @@
 lugin;%0A%0A
+import com.intellij.openapi.fileEditor.FileDocumentManager;%0A
 import c
@@ -1523,33 +1523,36 @@
 NotNull PsiFile 
-f
+psiF
 ile, int startOf
@@ -1621,142 +1621,147 @@
     
-if (whole
+Virtual
 File
-(
+ 
 file
-, startOffset, endOffset) && canReformat(file)) %7B%0A            // TODO: does not synchronize the file with filesystem
+ = psiFile.getVirtualFile();%0A        if (file != null && canReformat(file) && wholeFile(psiFile, startOffset, endOffset)) %7B
 %0A   
@@ -1842,16 +1842,66 @@
 ommand %0A
+            try %7B%0A                save(file);%0A    
         
@@ -1929,24 +1929,18 @@
 matFile(
-physical
+io
 File(fil
@@ -1956,14 +1956,21 @@
     
-%7D else
+    %7D finally
  %7B%0A 
@@ -1984,55 +1984,37 @@
     
-super.reformatText(file, startOff
+    file.refresh(fal
 se
-t
 , 
-endOff
+fal
 se
-t
 );%0A 
@@ -2024,201 +2024,105 @@
     
-%7D%0A
     %7D%0A
-%0A
     
-private static boolean canReformat(@NotNull PsiFile file) %7B%0A        VirtualFile virtualFile = file.getVirtualFile();%0A        return virtualFile != null && canReformat(virtualFile);
+    %7D else %7B%0A            super.reformatText(psiFile, startOffset, endOffset);%0A        %7D
 %0A   
@@ -2330,21 +2330,8 @@
  %7D%0A%0A
-    @NotNull%0A
     
@@ -2349,24 +2349,16 @@
 tic 
-File physicalFil
+void sav
 e(@N
@@ -2364,28 +2364,29 @@
 NotNull 
-Psi
+Virtual
 File 
-psiF
+f
 ile) %7B%0A 
@@ -2396,76 +2396,187 @@
     
-VirtualFile virtualFile = psiFile.getVirtualFile(
+FileDocumentManager.getInstance().saveDocument(%0A                FileDocumentManager.getInstance().getDocument(file)
 );%0A    
+%7D%0A%0A
     
-assert v
+@NotNull%0A    private static File ioFile(@NotNull V
 irtu
@@ -2586,16 +2586,15 @@
 ile 
-!= null;
+file) %7B
 %0A   
@@ -2618,16 +2618,9 @@
 ile(
-virtualF
+f
 ile.

Synchronize with file system