package com.intellij.lang.properties;

import com.intellij.AppTopics;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.components.ApplicationComponent;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileEditor.FileDocumentManagerAdapter;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.FileTypeManager;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.vfs.*;
import com.intellij.openapi.vfs.encoding.EncodingManager;
import com.intellij.util.containers.ConcurrentHashSet;
import com.intellij.util.messages.MessageBus;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * @author max
 */
public class PropertiesFilesManager implements ApplicationComponent {
  private final Set<VirtualFile> myPropertiesFiles = new ConcurrentHashSet<VirtualFile>();
  private final VirtualFileListener myVirtualFileListener;
  private final VirtualFileManager myVirtualFileManager;
  private final FileTypeManager myFileTypeManager;
  private final List<PropertiesFileListener> myPropertiesFileListeners = new CopyOnWriteArrayList<PropertiesFileListener>();

  public static PropertiesFilesManager getInstance() {
    return ApplicationManager.getApplication().getComponent(PropertiesFilesManager.class);
  }

  public PropertiesFilesManager(VirtualFileManager virtualFileManager, FileTypeManager fileTypeManager, MessageBus messageBus) {
    myVirtualFileManager = virtualFileManager;
    myFileTypeManager = fileTypeManager;
    myVirtualFileListener = new VirtualFileAdapter() {
      public void fileCreated(VirtualFileEvent event) {
        addNewFile(event);
      }

      public void fileDeleted(VirtualFileEvent event) {
        removeOldFile(event);
      }

      public void fileMoved(VirtualFileMoveEvent event) {
        removeOldFile(event);
        addNewFile(event);
      }

      public void propertyChanged(VirtualFilePropertyEvent event) {
        VirtualFile file = event.getFile();
        fileChanged(file, event);
      }

      public void contentsChanged(VirtualFileEvent event) {
        VirtualFile file = event.getFile();
        fileChanged(file, null);
      }
    };

    messageBus.connect().subscribe(AppTopics.FILE_DOCUMENT_SYNC, new FileDocumentManagerAdapter() {
      @Override
      public void fileContentLoaded(VirtualFile file, Document document) {
        addNewFile(file);
      }
    });
    EncodingManager.getInstance().addPropertyChangeListener(new PropertyChangeListener() {
      public void propertyChange(final PropertyChangeEvent evt) {
        if (EncodingManager.PROP_NATIVE2ASCII_SWITCH.equals(evt.getPropertyName()) ||
            EncodingManager.PROP_PROPERTIES_FILES_ENCODING.equals(evt.getPropertyName())
          ) {
          encodingChanged();
        }
      }
    });
  }

  private void removeOldFile(final VirtualFileEvent event) {
    VirtualFile file = event.getFile();
    FileType fileType = myFileTypeManager.getFileTypeByFile(file);
    if (fileType == StdFileTypes.PROPERTIES) {
      firePropertiesFileRemoved(file);
    }
    removeFile(file);
  }

  private void removeFile(final VirtualFile file) {
    myPropertiesFiles.remove(file);
  }

  private void addNewFile(final VirtualFileEvent event) {
    addNewFile(event.getFile());
  }

  // returns true if file is of properties file type
  boolean addNewFile(final VirtualFile file) {
    FileType fileType = myFileTypeManager.getFileTypeByFile(file);
    if (fileType == StdFileTypes.PROPERTIES) {
      if (myPropertiesFiles.add(file)) {
        firePropertiesFileAdded(file);
      }
      return true;
    }
    return false;
  }

  public Collection<VirtualFile> getAllPropertiesFiles() {
    return myPropertiesFiles;
  }

  public void initComponent() {
    myVirtualFileManager.addVirtualFileListener(myVirtualFileListener);
  }

  private void fileChanged(final VirtualFile file, final VirtualFilePropertyEvent event) {
    FileType fileType = myFileTypeManager.getFileTypeByFile(file);
    if (fileType == StdFileTypes.PROPERTIES) {
      firePropertiesFileChanged(file, event);
    }
    else {
      removeFile(file);
    }
  }

  public void disposeComponent() {
    VirtualFileManager.getInstance().removeVirtualFileListener(myVirtualFileListener);
  }

  @NotNull
  public String getComponentName() {
    return "Properties files manager";
  }

  private void encodingChanged() {
    ApplicationManager.getApplication().invokeLater(new Runnable(){
      public void run() {
        ApplicationManager.getApplication().runWriteAction(new Runnable(){
          public void run() {
            Collection<VirtualFile> filesToRefresh = new THashSet<VirtualFile>(getAllPropertiesFiles());
            VirtualFile[] virtualFiles = filesToRefresh.toArray(new VirtualFile[filesToRefresh.size()]);
            FileDocumentManager.getInstance().saveAllDocuments();

            //force to re-detect encoding
            for (VirtualFile virtualFile : virtualFiles) {
              virtualFile.setCharset(null);
            }
            FileDocumentManager.getInstance().reloadFiles(virtualFiles);
          }
        });
      }
    }, ModalityState.NON_MODAL);
  }

  public void addPropertiesFileListener(PropertiesFileListener fileListener) {
    myPropertiesFileListeners.add(fileListener);
  }
  public void removePropertiesFileListener(PropertiesFileListener fileListener) {
    myPropertiesFileListeners.remove(fileListener);
  }

  private void firePropertiesFileAdded(VirtualFile propertiesFile) {
    for (PropertiesFileListener listener : myPropertiesFileListeners) {
      listener.fileAdded(propertiesFile);
      listener.fileChanged(propertiesFile, null);
    }
  }
  private void firePropertiesFileRemoved(VirtualFile propertiesFile) {
    for (PropertiesFileListener listener : myPropertiesFileListeners) {
      listener.fileRemoved(propertiesFile);
      listener.fileChanged(propertiesFile, null);
    }
  }
  private void firePropertiesFileChanged(VirtualFile propertiesFile, final VirtualFilePropertyEvent event) {
    for (PropertiesFileListener listener : myPropertiesFileListeners) {
      listener.fileChanged(propertiesFile, event);
    }
  }

  public static interface PropertiesFileListener {
    void fileAdded(VirtualFile propertiesFile);
    void fileRemoved(VirtualFile propertiesFile);
    void fileChanged(VirtualFile propertiesFile, final VirtualFilePropertyEvent event);
  }
}

@@ -2008,32 +2008,42 @@
 addNewFile(event
+.getFile()
 );%0A      %7D%0A%0A    
@@ -2237,32 +2237,42 @@
 addNewFile(event
+.getFile()
 );%0A      %7D%0A%0A    
@@ -3592,32 +3592,24 @@
 le);%0A  %7D%0A%0A  
-private 
 void addNewF
@@ -3622,152 +3622,8 @@
 nal 
-VirtualFileEvent event) %7B%0A    addNewFile(event.getFile());%0A  %7D%0A%0A  // returns true if file is of properties file type%0A  boolean addNewFile(final 
 Virt
@@ -3752,22 +3752,30 @@
 TIES
-) %7B%0A      if (
+ && file.isValid() && 
 myPr
@@ -3797,26 +3797,24 @@
 dd(file)) %7B%0A
-  
       firePr
@@ -3846,54 +3846,9 @@
     
-  %7D%0A      return true;%0A    %7D%0A    return false;
+%7D
 %0A  %7D
@@ -4800,32 +4800,106 @@
 ic void run() %7B%0A
+            if (ApplicationManager.getApplication().isDisposed()) return;%0A
             Coll
@@ -6474,23 +6474,16 @@
  public 
-static 
 interfac

check for isValid