/*
 * 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.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();

  protected static final String TEST_MODULE_NAME = "light_idea_test_case";

  public void setUpProject(@NotNull Project project, @NotNull SetupHandler handler) throws Exception {
    WriteAction.run(() -> {
      Module module = createMainModule(project);
      handler.moduleCreated(module);
      VirtualFile sourceRoot = 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;
    try {
      srcRoot = dummyRoot.createChildDirectory(this, srcPath);
      cleanSourceRoot(srcRoot);
    }
    catch (IOException e) {
      throw new RuntimeException(e);
    }

    IndexableFileSet indexableFileSet = new IndexableFileSet() {
      @Override
      public boolean isInSet(@NotNull VirtualFile file) {
        return file.getFileSystem() == srcRoot.getFileSystem() && module.getProject().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(module.getProject(), () -> FileBasedIndex.getInstance().removeIndexableSet(indexableFileSet));

    return srcRoot;
  }

  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);
    }
  }

  @SuppressWarnings("NullableProblems")
  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) { }
  }
}
@@ -3669,16 +3669,239 @@
 false);%0A
+    VirtualFile srcRoot = doCreateSourceRoot(dummyRoot, srcPath);%0A    registerSourceRoot(module.getProject(), srcRoot);%0A    return srcRoot;%0A  %7D%0A%0A  protected VirtualFile doCreateSourceRoot(VirtualFile root, String srcPath) %7B
 %0A    Vir
@@ -3944,22 +3944,17 @@
 cRoot = 
-dummyR
+r
 oot.crea
@@ -4092,24 +4092,125 @@
 (e);%0A    %7D%0A%0A
+    return srcRoot;%0A  %7D%0A%0A  protected void registerSourceRoot(Project project, VirtualFile srcRoot) %7B%0A
     Indexabl
@@ -4402,35 +4402,23 @@
 em() && 
-module.getP
+p
 roject
-()
 .isOpen(
@@ -4918,35 +4918,23 @@
 egister(
-module.getP
+p
 roject
-()
 , () -%3E 
@@ -5000,37 +5000,16 @@
 leSet));
-%0A%0A    return srcRoot;
 %0A  %7D%0A%0A  

allow to override actual source root creation