/*
 * Copyright 2000-2017 JetBrains s.r.o.
 *
 * 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 com.intellij.testFramework;

import com.intellij.openapi.application.WriteAction;
import com.intellij.openapi.module.EmptyModuleType;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.module.ModuleType;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.roots.ContentEntry;
import com.intellij.openapi.roots.ContentIterator;
import com.intellij.openapi.roots.ModifiableRootModel;
import com.intellij.openapi.roots.ModuleRootModificationUtil;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.ThrowableComputable;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.openapi.vfs.VirtualFileVisitor;
import com.intellij.openapi.vfs.ex.temp.TempFileSystem;
import com.intellij.util.indexing.FileBasedIndex;
import com.intellij.util.indexing.IndexableFileSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.model.java.JavaSourceRootType;
import org.jetbrains.jps.model.module.JpsModuleSourceRootType;

import java.io.File;
import java.io.IOException;

public class LightProjectDescriptor {
  public static final LightProjectDescriptor EMPTY_PROJECT_DESCRIPTOR = new LightProjectDescriptor();

  public static final String TEST_MODULE_NAME = "light_idea_test_case";

  public void setUpProject(@NotNull Project project, @NotNull SetupHandler handler) throws Exception {
    Module module = createMainModule(project);
    handler.moduleCreated(module);
    VirtualFile sourceRoot = WriteAction.compute((ThrowableComputable<VirtualFile, Exception>)() -> createSourcesRoot(module));
    if (sourceRoot != null) {
      handler.sourceRootCreated(sourceRoot);
      createContentEntry(module, sourceRoot);
    }
  }

  @NotNull
  public Module createMainModule(@NotNull Project project) {
    return createModule(project, FileUtil.join(FileUtil.getTempDirectory(), TEST_MODULE_NAME + ".iml"));
  }

  protected Module createModule(@NotNull Project project, @NotNull String moduleFilePath) {
    return WriteAction.compute(() -> {
      File imlFile = new File(moduleFilePath);
      if (imlFile.exists()) {
        //temporary workaround for IDEA-147530: otherwise if someone saved module with this name before the created module will get its settings
        FileUtil.delete(imlFile);
      }
      return ModuleManager.getInstance(project).newModule(moduleFilePath, getModuleType().getId());
    });
  }

  @NotNull
  public ModuleType getModuleType() {
    return EmptyModuleType.getInstance();
  }

  @Nullable
  public VirtualFile createSourcesRoot(@NotNull Module module) {
    return createSourceRoot(module, "src");
  }

  protected VirtualFile createSourceRoot(@NotNull Module module, String srcPath) {
    VirtualFile dummyRoot = VirtualFileManager.getInstance().findFileByUrl("temp:///");
    assert dummyRoot != null;
    dummyRoot.refresh(false, false);
    VirtualFile srcRoot = doCreateSourceRoot(dummyRoot, srcPath);
    registerSourceRoot(module.getProject(), srcRoot);
    return srcRoot;
  }

  protected VirtualFile doCreateSourceRoot(VirtualFile root, String srcPath) {
    VirtualFile srcRoot;
    try {
      srcRoot = root.createChildDirectory(this, srcPath);
      cleanSourceRoot(srcRoot);
    }
    catch (IOException e) {
      throw new RuntimeException(e);
    }

    return srcRoot;
  }

  protected void registerSourceRoot(Project project, VirtualFile srcRoot) {
    IndexableFileSet indexableFileSet = new IndexableFileSet() {
      @Override
      public boolean isInSet(@NotNull VirtualFile file) {
        return file.getFileSystem() == srcRoot.getFileSystem() && project.isOpen();
      }

      @Override
      public void iterateIndexableFilesIn(@NotNull VirtualFile file, @NotNull ContentIterator iterator) {
        VfsUtilCore.visitChildrenRecursively(file, new VirtualFileVisitor() {
          @Override
          public boolean visitFile(@NotNull VirtualFile file) {
            iterator.processFile(file);
            return true;
          }
        });
      }
    };
    FileBasedIndex.getInstance().registerIndexableSet(indexableFileSet, null);
    Disposer.register(project, () -> FileBasedIndex.getInstance().removeIndexableSet(indexableFileSet));
  }

  protected void createContentEntry(@NotNull Module module, @NotNull VirtualFile srcRoot) {
    ModuleRootModificationUtil.updateModel(module, model -> {
      Sdk sdk = getSdk();
      if (sdk != null) {
        model.setSdk(sdk);
      }

      ContentEntry contentEntry = model.addContentEntry(srcRoot);
      contentEntry.addSourceFolder(srcRoot, getSourceRootType());

      configureModule(module, model, contentEntry);
    });
  }

  @NotNull
  protected JpsModuleSourceRootType<?> getSourceRootType() {
    return JavaSourceRootType.SOURCE;
  }

  @Nullable
  public Sdk getSdk() {
    return null;
  }

  private void cleanSourceRoot(@NotNull VirtualFile contentRoot) throws IOException {
    TempFileSystem tempFs = (TempFileSystem)contentRoot.getFileSystem();
    for (VirtualFile child : contentRoot.getChildren()) {
      if (!tempFs.exists(child)) {
        tempFs.createChildFile(this, contentRoot, child.getName());
      }
      child.delete(this);
    }
  }

  protected void configureModule(@NotNull Module module, @NotNull ModifiableRootModel model, @NotNull ContentEntry contentEntry) { }

  public interface SetupHandler {
    default void moduleCreated(@NotNull Module module) { }

    default void sourceRootCreated(@NotNull VirtualFile sourceRoot) { }
  }
}
@@ -1230,62 +1230,8 @@
 er;%0A
-import com.intellij.openapi.util.ThrowableComputable;%0A
 impo
@@ -2192,24 +2192,54 @@
 Exception %7B%0A
+    WriteAction.run(() -%3E %7B%0A  
     Module m
@@ -2273,24 +2273,26 @@
 oject);%0A    
+ 
+ 
 handler.modu
@@ -2306,32 +2306,34 @@
 ed(module);%0A    
+  
 VirtualFile sour
@@ -2344,79 +2344,8 @@
 ot =
- WriteAction.compute((ThrowableComputable%3CVirtualFile, Exception%3E)() -%3E
  cre
@@ -2366,23 +2366,24 @@
 (module)
-)
 ;%0A    
+ 
+ 
 if (sour
@@ -2396,24 +2396,26 @@
  != null) %7B%0A
+  
       handle
@@ -2453,16 +2453,18 @@
 ;%0A      
+ 
+ 
 createCo
@@ -2495,25 +2495,35 @@
 eRoot);%0A    
-%7D
+  %7D%0A    %7D);
 %0A  %7D%0A%0A  @Not

Revert "Do not call createContentEntry under WriteAction."