/*
 * Copyright 2000-2012 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.compiler.ant;

import com.intellij.compiler.ant.taskdefs.*;
import com.intellij.openapi.fileTypes.FileTypeManager;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ContentEntry;
import com.intellij.openapi.roots.ModuleFileIndex;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileVisitor;
import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.List;

/**
 * @author Eugene Zhuravlev
 * @since Nov 22, 2004
 */
public class ModuleChunkSourcePath extends CompositeGenerator{
  private final VirtualFile[] mySourceRoots;
  private final VirtualFile[] myTestSourceRoots;

  public ModuleChunkSourcePath(final Project project, ModuleChunk chunk, final GenerationOptions genOptions) {
    final Path sourcePath = new Path(BuildProperties.getSourcepathProperty(chunk.getName()));
    final Path testSourcePath = new Path(BuildProperties.getTestSourcepathProperty(chunk.getName()));
    final PatternSet excludedFromCompilation = new PatternSet(BuildProperties.getExcludedFromCompilationProperty(chunk.getName()));
    final String moduleChunkBasedirProperty = BuildProperties.getModuleChunkBasedirProperty(chunk);
    final Module[] modules = chunk.getModules();

    if (CompilerExcludes.isAvailable(project)) {
      excludedFromCompilation.add(new PatternSetRef(BuildProperties.PROPERTY_COMPILER_EXCLUDES));
    }

    final List<VirtualFile> sourceRootFiles = new ArrayList<>();
    final List<VirtualFile> testSourceRootFiles = new ArrayList<>();

    for (final Module module : modules) {
      final String moduleName = module.getName();
      final ModuleRootManager rootManager = ModuleRootManager.getInstance(module);
      final ModuleFileIndex moduleFileIndex = rootManager.getFileIndex();


      final PatternSet excludedFromModule = new PatternSet(BuildProperties.getExcludedFromModuleProperty(moduleName));
      excludedFromModule.add(new PatternSetRef(BuildProperties.PROPERTY_IGNORED_FILES));

      final ContentEntry[] contentEntries = rootManager.getContentEntries();
      for (final ContentEntry contentEntry : contentEntries) {
        final VirtualFile file = contentEntry.getFile();
        if (file == null) {
          continue; // filter invalid entries
        }
        if (!file.isInLocalFileSystem()) {
          continue; // skip content roots inside jar and zip archives
        }
        final VirtualFile dirSetRoot = getDirSetRoot(contentEntry);

        final String dirSetRootRelativeToBasedir = GenerationUtils
          .toRelativePath(dirSetRoot, chunk.getBaseDir(), moduleChunkBasedirProperty, genOptions);
        final DirSet sourcesDirSet = new DirSet(dirSetRootRelativeToBasedir);
        final DirSet testSourcesDirSet = new DirSet(dirSetRootRelativeToBasedir);

        final VirtualFile[] sourceRoots = contentEntry.getSourceFolderFiles();
        for (final VirtualFile root : sourceRoots) {
          if (!moduleFileIndex.isInContent(root)) {
            continue; // skip library sources
          }

          addExcludePatterns(module, root, root, excludedFromModule, true);

          final Include include = new Include(VfsUtilCore.getRelativePath(root, dirSetRoot, '/'));
          if (moduleFileIndex.isInTestSourceContent(root)) {
            testSourcesDirSet.add(include);
            testSourceRootFiles.add(root);
          }
          else {
            sourcesDirSet.add(include);
            sourceRootFiles.add(root);
          }
        }
        if (sourcesDirSet.getGeneratorCount() > 0) {
          sourcePath.add(sourcesDirSet);
        }
        if (testSourcesDirSet.getGeneratorCount() > 0) {
          testSourcePath.add(testSourcesDirSet);
        }
      }

      if (excludedFromModule.getGeneratorCount() > 0) {
        add(excludedFromModule);
        excludedFromCompilation.add(new PatternSetRef(BuildProperties.getExcludedFromModuleProperty(moduleName)));
      }
    }

    mySourceRoots = VfsUtilCore.toVirtualFileArray(sourceRootFiles);
    myTestSourceRoots = VfsUtilCore.toVirtualFileArray(testSourceRootFiles);

    if (excludedFromCompilation.getGeneratorCount() > 0) {
      add(excludedFromCompilation, 1);
    }
    if (sourcePath.getGeneratorCount() > 0) {
      add(sourcePath, 1);
    }
    if (testSourcePath.getGeneratorCount() != 0) {
      add(testSourcePath, 1);
    }
  }

  public VirtualFile[] getSourceRoots() {
    return mySourceRoots;
  }

  public VirtualFile[] getTestSourceRoots() {
    return myTestSourceRoots;
  }

  private VirtualFile getDirSetRoot(final ContentEntry contentEntry) {
    final VirtualFile contentRoot = contentEntry.getFile();
    final VirtualFile[] sourceFolderFiles = contentEntry.getSourceFolderFiles();
    for (VirtualFile sourceFolderFile : sourceFolderFiles) {
      if (contentRoot.equals(sourceFolderFile)) {
        return contentRoot.getParent();
      }
    }
    return contentRoot;
  }

  private static void addExcludePatterns(Module module,
                                         final VirtualFile root,
                                         VirtualFile dir,
                                         final CompositeGenerator generator,
                                         final boolean parentIncluded) {
    final FileTypeManager fileTypeManager = FileTypeManager.getInstance();
    final ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(module);

    VfsUtilCore.visitChildrenRecursively(dir, new VirtualFileVisitor() {
      @Override
      public boolean visitFile(@NotNull VirtualFile dir) {
        if (!dir.isDirectory() || fileTypeManager.isFileIgnored(dir)) {
          // ignored files are handled by global 'ignored' pattern set
          return false;
        }

        final boolean isIncluded = moduleRootManager.getFileIndex().isInContent(dir);
        if (isIncluded != parentIncluded) {
          final String relativePath = VfsUtilCore.getRelativePath(dir, root, '/');
          if (isIncluded) {
            generator.add(new Include(relativePath + "/**"));
          }
          else {
            if (!isExcludedByDefault(dir.getName())) {
              generator.add(new Exclude(relativePath + "/**"));
            }
          }
        }

        return true;
      }
    });
  }

  private static boolean isExcludedByDefault(String name) {
    //noinspection HardCodedStringLiteral
    return "CVS".equals(name) || "SCCS".equals(name) || ".DS_Store".equals(name);
  }
}
