package jetbrains.mps.kotlin.stubs.common;

/*Generated by MPS */

import jetbrains.mps.annotations.GeneratedClass;
import java.util.List;
import jetbrains.mps.vfs.IFile;
import java.util.Collection;
import jetbrains.mps.extapi.persistence.SourceRoot;
import jetbrains.mps.internal.collections.runtime.ListSequence;
import java.util.ArrayList;
import jetbrains.mps.internal.collections.runtime.CollectionSequence;
import java.util.Set;
import jetbrains.mps.internal.collections.runtime.SetSequence;
import org.jetbrains.mps.openapi.model.SModel;
import jetbrains.mps.extapi.persistence.FileBasedModelRoot;
import jetbrains.mps.java.stub.PackageScopeControl;
import jetbrains.mps.extapi.persistence.SourceRootKinds;
import java.util.HashSet;
import jetbrains.mps.vfs.path.Path;
import java.util.Map;
import org.jetbrains.mps.openapi.model.SModelId;
import jetbrains.mps.internal.collections.runtime.MapSequence;
import java.util.HashMap;
import org.jetbrains.mps.openapi.model.SModelReference;
import jetbrains.mps.extapi.model.SModelBase;
import jetbrains.mps.extapi.persistence.FolderSetDataSource;
import org.jetbrains.mps.openapi.module.SModule;
import org.jetbrains.mps.openapi.persistence.ModelRoot;
import jetbrains.mps.baseLanguage.closures.runtime._FunctionTypes;
import jetbrains.mps.internal.collections.runtime.Sequence;

/**
 * Copy of java implementation for jar stubs.
 * 
 * @see jetbrains.mps.persistence.java.library.JavaClassStubsModelRoot 
 */
@GeneratedClass(node = "r:3391afe4-c131-4e6c-87cf-990834a43a93(jetbrains.mps.kotlin.stubs.common)/2728382324535686898", model = "r:3391afe4-c131-4e6c-87cf-990834a43a93(jetbrains.mps.kotlin.stubs.common)")
public class JarHelper {
  public static List<IFile> rootsToFiles(Collection<SourceRoot> roots) {
    List<IFile> files = ListSequence.fromList(new ArrayList<IFile>(CollectionSequence.fromCollection(roots).count()));
    for (SourceRoot r : roots) {
      ListSequence.fromList(files).addElement(r.getAbsolutePath());
    }
    return files;
  }

  public static void collectJarFiles(final IFile file, Collection<IFile> excluded, Set<IFile> archiveFiles) {
    if (CollectionSequence.fromCollection(excluded).contains(file)) {
      return;
    }
    if (file.getPath().endsWith(".jar") || file.getPath().endsWith(".zip")) {
      SetSequence.fromSet(archiveFiles).addElement(file);
      return;
    }
    if (!(file.isDirectory())) {
      return;
    }
    for (IFile child : file.getChildren()) {
      collectJarFiles(child, excluded, archiveFiles);
    }
  }

  /**
   * Load models from JAR files according to the file based model root specification, the model descriptor producer and the
   * package scope.
   */
  public static Iterable<SModel> loadModelsFromFiles(final FileBasedModelRoot modelRoot, final ModelDescriptorProducer descriptorProducer, final PackageScopeControl packageScope) {
    // TODO merge with java by setting this public
    List<IFile> files = JarHelper.rootsToFiles(modelRoot.getSourceRoots(SourceRootKinds.SOURCES));
    List<IFile> excluded = JarHelper.rootsToFiles(modelRoot.getSourceRoots(SourceRootKinds.EXCLUDED));

    Set<IFile> jarsToLoad = SetSequence.fromSet(new HashSet<IFile>());

    final Set<IFile> cpRootsToLoad = SetSequence.fromSet(new HashSet<IFile>());
    for (IFile file : files) {
      JarHelper.collectJarFiles(file, excluded, jarsToLoad);

      // we suppose here that each path can be either a jar-file or a classes directory or a jar directory,
      // but does not contain both jar files and class files
      if (SetSequence.fromSet(jarsToLoad).isNotEmpty()) {
        continue;
      }

      SetSequence.fromSet(cpRootsToLoad).addElement(file);
    }

    // FIXME though IFile("whatever.jar") could be already from JAR FS (e.g. CommonPaths creates IFiles using JAR FS for jar files right away), I found no way to figure it out
    // therefore have to resort to this stupid way to step into jar
    SetSequence.fromSet(jarsToLoad).select((it) -> modelRoot.getFileSystem().getFile(it.getPath() + Path.ARCHIVE_SEPARATOR)).visitAll((it) -> SetSequence.fromSet(cpRootsToLoad).addElement(it));

    final Map<SModelId, SModel> result = MapSequence.fromMap(new HashMap<SModelId, SModel>());
    SetSequence.fromSet(cpRootsToLoad).visitAll((it) -> descriptorProducer.getModelDescriptors(result, it, "", modelRoot.getModule(), packageScope, modelRoot));

    return MapSequence.fromMap(result).values();
  }

  public interface ModelDescriptorProducer {
    boolean isFileIncluded(IFile file);
    SModelReference createPackageModelReference(String packageName);
    SModelBase createDescriptor(SModelReference reference, FolderSetDataSource dataSource);


    default void getModelDescriptors(final Map<SModelId, SModel> result, IFile file, String prefix, SModule module, PackageScopeControl psc, ModelRoot mr) {
      List<IFile> children = file.getChildren();
      for (IFile subdir : ListSequence.fromList(children).where((it) -> it.isDirectory())) {
        String pack = (prefix.length() == 0 ? subdir.getName() : prefix + '.' + subdir.getName());
        if (psc != null && !(psc.isIncluded(pack))) {
          if (psc.isAnyChildIncluded(pack)) {
            getModelDescriptors(result, subdir, pack, module, psc, mr);
          }
          continue;
        }
        List<IFile> subchildren = subdir.getChildren();
        Iterable<IFile> rootsClasses = ListSequence.fromList(subchildren).where(new _FunctionTypes._return_P1_E0<Boolean, IFile>() {
          public Boolean invoke(IFile file) {
            return isFileIncluded(file);
          }
        });
        if (Sequence.fromIterable(rootsClasses).isNotEmpty()) {
          SModelReference modelReference = createPackageModelReference(pack);
          SModelBase smd;
          // FIXME: hack, see comment in Java impl
          SModel modelDescriptor = module.getModel(modelReference.getModelId());
          if (modelDescriptor != null) {
            assert modelDescriptor instanceof SModelBase;
            smd = (SModelBase) modelDescriptor;
            MapSequence.fromMap(result).put(modelReference.getModelId(), modelDescriptor);
          } else
          if (MapSequence.fromMap(result).get(modelReference.getModelId()) != null) {
            modelDescriptor = MapSequence.fromMap(result).get(modelReference.getModelId());
            assert modelDescriptor instanceof SModelBase;
            smd = (SModelBase) modelDescriptor;
          } else {
            FolderSetDataSource ds = new FolderSetDataSource();
            smd = createDescriptor(modelReference, ds);
            smd.setModelRoot(mr);
            MapSequence.fromMap(result).put(modelReference.getModelId(), smd);
          }

          FolderSetDataSource source = (FolderSetDataSource) smd.getSource();
          source.addPath(subdir);
        }

        getModelDescriptors(result, subdir, pack, module, psc, mr);
      }
    }

  }


}
