package com.intellij.openapi.command.impl;

import com.intellij.localvcs.integration.Checkpoint;
import com.intellij.localvcs.integration.LocalHistory;
import com.intellij.openapi.command.undo.*;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.vfs.*;

class NewFileOperationsUndoProvider extends AbstractFileOperationsUndoProvider {
  private Key<Boolean> DELETE_WAS_UNDOABLE = new Key<Boolean>("DeleteWasUndoable");

  private Project myProject;
  private UndoManagerImpl myUndoManager;
  private boolean myIsInsideCommand;

  public NewFileOperationsUndoProvider(UndoManagerImpl m, Project p) {
    myUndoManager = m;
    myProject = p;

    if (myProject == null) return;
    getFileManager().addVirtualFileListener(this);
  }

  @Override
  public void dispose() {
    if (myProject == null) return;
    getFileManager().removeVirtualFileListener(this);
  }

  private VirtualFileManager getFileManager() {
    return VirtualFileManager.getInstance();
  }

  @Override
  public void commandStarted(Project p) {
    if (myProject != p) return;
    myIsInsideCommand = true;
  }

  @Override
  public void commandFinished(Project p) {
    if (myProject != p) return;
    myIsInsideCommand = false;
  }

  public void fileCreated(VirtualFileEvent e) {
    if (shouldNotProcess(e)) return;
    processEvent(e);
  }

  public void beforeContentsChange(VirtualFileEvent e) {
    if (shouldNotProcess(e)) return;
    if (isUndoable(e)) return;

    createNonUndoableAction(e);
  }

  public void propertyChanged(VirtualFilePropertyEvent e) {
    if (shouldNotProcess(e)) return;
    if (!e.getPropertyName().equals(VirtualFile.PROP_NAME)) return;

    processEvent(e);
  }

  public void fileMoved(VirtualFileMoveEvent e) {
    if (shouldNotProcess(e)) return;
    processEvent(e);
  }

  public void beforeFileDeletion(VirtualFileEvent e) {
    if (shouldNotProcess(e)) return;

    if (isUndoable(e)) {
      e.getFile().putUserData(DELETE_WAS_UNDOABLE, true);
    }
    else {
      createNonUndoableAction(e);
    }
  }

  public void fileDeleted(VirtualFileEvent e) {
    VirtualFile f = e.getFile();

    if (f.getUserData(DELETE_WAS_UNDOABLE) != null) {
      createUndoableAction();
      f.putUserData(DELETE_WAS_UNDOABLE, null);
    }
  }

  private void processEvent(VirtualFileEvent e) {
    if (isUndoable(e)) {
      createUndoableAction();
    }
    else {
      createNonUndoableAction(e);
    }
  }

  private boolean shouldNotProcess(VirtualFileEvent e) {
    return !LocalHistory.isUnderControl(myProject, e.getFile());
  }

  private boolean isUndoable(VirtualFileEvent e) {
    return myIsInsideCommand && !e.isFromRefresh();
  }

  private void createNonUndoableAction(VirtualFileEvent e) {
    VirtualFile f = e.getFile();

    DocumentReference newRef = new DocumentReferenceByVirtualFile(f);
    registerNonUndoableAction(newRef);

    DocumentReference oldRef = myUndoManager.findInvalidatedReferenceByUrl(f.getUrl());
    if (oldRef != null && !oldRef.equals(newRef)) {
      registerNonUndoableAction(oldRef);
    }
  }

  private void registerNonUndoableAction(final DocumentReference r) {
    if (myUndoManager.undoableActionsForDocumentAreEmpty(r)) return;

    myUndoManager.undoableActionPerformed(new NonUndoableAction() {
      public DocumentReference[] getAffectedDocuments() {
        return new DocumentReference[]{r};
      }

      public boolean isComplex() {
        return true;
      }
    });
  }

  private void createUndoableAction() {
    myUndoManager.undoableActionPerformed(new MyUndoableAction());
  }

  private class MyUndoableAction implements UndoableAction {
    private Checkpoint myAfterActionCheckpoint;
    private Checkpoint myBeforeUndoCheckpoint;

    public MyUndoableAction() {
      myAfterActionCheckpoint = LocalHistory.putCheckpoint(myProject);
    }

    public void undo() throws UnexpectedUndoException {
      myBeforeUndoCheckpoint = LocalHistory.putCheckpoint(myProject);
      myAfterActionCheckpoint.revertToPreviousState();
    }

    public void redo() throws UnexpectedUndoException {
      myBeforeUndoCheckpoint.revertToThatState();
    }

    public DocumentReference[] getAffectedDocuments() {
      return DocumentReference.EMPTY_ARRAY;
    }

    public boolean isComplex() {
      return true;
    }
  }
}

@@ -1317,220 +1317,20 @@
     
-if (shouldNotProcess(e)) return;%0A    processEvent(e);%0A  %7D%0A%0A  public void beforeContentsChange(VirtualFileEvent e) %7B%0A    if (shouldNotProcess(e)) return;%0A    if (isUndoable(e)) return;%0A%0A    createNonUndoableAction
+processEvent
 (e);
@@ -1399,45 +1399,8 @@
 ) %7B%0A
-    if (shouldNotProcess(e)) return;%0A
     
@@ -1459,25 +1459,24 @@
 E)) return;%0A
-%0A
     processE
@@ -1547,57 +1547,312 @@
     
-if (shouldNotProcess(e)) return;%0A    processEvent
+processEvent(e);%0A  %7D%0A%0A  private void processEvent(VirtualFileEvent e) %7B%0A    if (isUndoable(e)) %7B%0A      createUndoableAction();%0A    %7D%0A    else %7B%0A      createNonUndoableAction(e);%0A    %7D%0A  %7D%0A%0A  public void beforeContentsChange(VirtualFileEvent e) %7B%0A    if (isUndoable(e)) return;%0A    createNonUndoableAction
 (e);
@@ -1916,46 +1916,8 @@
 ) %7B%0A
-    if (shouldNotProcess(e)) return;%0A%0A
     
@@ -2296,199 +2296,26 @@
 ate 
-void processEvent(VirtualFileEvent e) %7B%0A    if (isUndoable(e)) %7B%0A      createUndoableAction();%0A    %7D%0A    else %7B%0A      createNonUndoableAction(e);%0A    %7D%0A  %7D%0A%0A  private boolean shouldNotProcess
+boolean isUndoable
 (Vir
@@ -2345,16 +2345,37 @@
 return !
+e.isFromRefresh() && 
 LocalHis
@@ -2428,116 +2428,8 @@
  %7D%0A%0A
-  private boolean isUndoable(VirtualFileEvent e) %7B%0A    return myIsInsideCommand && !e.isFromRefresh();%0A  %7D%0A%0A
   pr
@@ -3252,24 +3252,60 @@
 eAction() %7B%0A
+    if (!myIsInsideCommand) return;%0A
     myUndoMa

undo bug fix