package jetbrains.mps.kotlin.stubs.common;

/*Generated by MPS */

import jetbrains.mps.annotations.GeneratedClass;
import org.jetbrains.mps.openapi.model.SNode;
import jetbrains.mps.smodel.LazyEditableSModelBase;
import java.util.Collection;
import org.jetbrains.mps.openapi.model.SModelReference;
import jetbrains.mps.extapi.persistence.FolderSetDataSource;
import org.jetbrains.annotations.Nullable;
import jetbrains.mps.smodel.SModel;
import jetbrains.mps.vfs.IFile;
import org.jetbrains.annotations.NotNull;
import jetbrains.mps.project.AbstractModule;
import org.jetbrains.mps.openapi.language.SLanguage;
import java.util.Collections;
import java.util.List;
import org.jetbrains.mps.openapi.module.SModuleReference;
import jetbrains.mps.internal.collections.runtime.Sequence;
import jetbrains.mps.internal.collections.runtime.CollectionSequence;
import jetbrains.mps.smodel.ModelLoadResult;
import jetbrains.mps.smodel.loading.ModelLoadingState;
import java.io.IOException;
import org.jetbrains.mps.openapi.persistence.ModelSaveException;

@GeneratedClass(node = "r:3391afe4-c131-4e6c-87cf-990834a43a93(jetbrains.mps.kotlin.stubs.common)/8317865157550790678", model = "r:3391afe4-c131-4e6c-87cf-990834a43a93(jetbrains.mps.kotlin.stubs.common)")
public abstract class KotlinStubModelDescriptor<T extends SNode> extends LazyEditableSModelBase {
  private Collection<SModelReference> myImportsToAdd = null;

  public KotlinStubModelDescriptor(SModelReference modelReference, FolderSetDataSource source) {
    super(modelReference, source);
  }

  @Nullable
  protected abstract Collection<SModelReference> loadDeep(SModel model);

  protected abstract boolean isIncluded(IFile file);

  protected abstract T createRootNode(IFile file);

  @NotNull
  @Override
  public FolderSetDataSource getSource() {
    return (FolderSetDataSource) super.getSource();
  }

  @Override
  public AbstractModule getModule() {
    return (AbstractModule) super.getModule();
  }

  @Override
  public Collection<SLanguage> importedLanguageIds() {
    // Overridden to avoid loading model
    return Collections.emptyList();
  }

  @Override
  public List<SModuleReference> importedDevkits() {
    // Overridden to avoid loading model
    return Collections.emptyList();
  }

  @NotNull
  @Override
  public Collection<SLanguage> getLanguagesEngagedOnGeneration() {
    // Overridden to avoid loading model
    return Collections.emptyList();
  }


  protected void loadInterface(SModel model) {
    Iterable<IFile> topFiles = getTopFiles();
    for (IFile file : Sequence.fromIterable(topFiles)) {
      T rootNode = createRootNode(file);
      if (rootNode != null) {
        model.addRootNode(rootNode);
      }
    }

    for (SLanguage l : importedLanguageIds()) {
      model.addLanguage(l);
    }
  }


  protected Iterable<IFile> getTopFiles() {
    Collection<IFile> paths = getSource().getAffectedFiles();
    return CollectionSequence.fromCollection(paths).where((it) -> it != null).translate((it) -> it.getChildren()).where((it) -> !(it.isDirectory()) && isIncluded(it));
  }


  @Override
  protected ModelLoadResult<SModel> loadSModel(ModelLoadingState state) {
    SModel model = new SModel(getReference(), new KotlinStringBasedNodeIdMap());

    if (state == ModelLoadingState.INTERFACE_LOADED) {
      loadInterface(model);
    } else {
      // Imports are saved so they can be added later on
      myImportsToAdd = loadDeep(model);
    }

    return new ModelLoadResult<>(model, state);
  }

  @Override
  protected void fireModelStateChanged(ModelLoadingState oldState, ModelLoadingState newState) {
    if (oldState == newState) {
      return;
    }

    // Before fully loaded model escapes to the outside, we need to add imports to it if applicable
    if (newState == ModelLoadingState.FULLY_LOADED && myImportsToAdd != null) {
      final SModel sModel = getCurrentModelInternal();
      sModel.enterUpdateMode();
      for (SModelReference ref : CollectionSequence.fromCollection(myImportsToAdd)) {
        sModel.addModelImport(new SModel.ImportElement(ref));
      }
      myImportsToAdd = null;
      sModel.leaveUpdateMode();
    }

    super.fireModelStateChanged(oldState, newState);
  }

  @Override
  public boolean isReadOnly() {
    // Stubs: read only
    return true;
  }

  @Override
  public boolean isChanged() {
    // Stubs: read only
    return false;
  }

  @Override
  protected boolean saveModel() throws IOException, ModelSaveException {
    return false;
  }
}
