/*
 * Copyright 2003-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 jetbrains.mps.smodel.tempmodel;

import jetbrains.mps.classloading.ClassLoaderManager;
import jetbrains.mps.module.ReloadableModuleBase;
import jetbrains.mps.project.ModuleId;
import jetbrains.mps.project.facets.JavaModuleFacet;
import jetbrains.mps.project.structure.model.ModelRootDescriptor;
import jetbrains.mps.project.structure.modules.ModuleDescriptor;
import jetbrains.mps.project.structure.modules.ModuleReference;
import jetbrains.mps.smodel.MPSModuleOwner;
import jetbrains.mps.vfs.IFile;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.mps.openapi.module.SModule;
import org.jetbrains.mps.openapi.module.SModuleFacet;
import org.jetbrains.mps.openapi.module.SModuleId;
import org.jetbrains.mps.openapi.module.SModuleReference;
import org.jetbrains.mps.openapi.persistence.Memento;

import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.Set;

/**
 * TODO: rewrite class loading functional : it must not extend ReloadableModuleBase and be maintained by ClassLoaderManager.
 * TODO: it does not belong to any repository
 */
public class TempModule extends ReloadableModuleBase implements SModule, MPSModuleOwner {
  private final static Logger LOG = LogManager.getLogger(TempModule.class);
  private final ModuleDescriptor myDescriptor;
  private final JavaModuleFacet myJavaModuleFacet;

  public TempModule(Set<ModelRootDescriptor> modelRoots, boolean withSourceGen, boolean withJavaFacet) {
    if (withSourceGen && !withJavaFacet) {
      throw new IllegalArgumentException("Don't have GenerationTargetFacet implementation other than JavaModuleFacet handy, either write one or re-consider arguments");
    }
    SModuleId id = ModuleId.regular();
    SModuleReference reference = new ModuleReference("TempModule" + id, id);
    setModuleReference(reference);
    myDescriptor = new ModuleDescriptor();
    myDescriptor.getModelRootDescriptors().addAll(modelRoots);
    dependenciesChanged();

    if (withJavaFacet) {
      IFile sourcesGen = withSourceGen ? createTempDirectory("TempModule_source_gen") : null;
      IFile classesGen = createTempDirectory("TempModule_classes_gen");
      myJavaModuleFacet = new TempModuleJavaFacet(sourcesGen, classesGen);
    } else {
      myJavaModuleFacet = null;
    }
  }

  @Override
  public void reload() {
    if (!willLoad()) return;
    LOG.debug("Reloading temporary module " + this);
    ClassLoaderManager.getInstance().reloadModule(this);
  }

  @Override
  public boolean willLoad() {
    return myJavaModuleFacet != null;
  }

  public boolean isHidden() {
    return true;
  }

  @Override
  public boolean isReadOnly() {
    return false;
  }

  @NotNull
  @Override
  public Iterable<SModuleFacet> getFacets() {
    return myJavaModuleFacet != null ? Collections.singleton(myJavaModuleFacet) : Collections.emptySet();
  }

  public String toString() {
    return getModuleName() + " [temp module]";
  }

  @Override
  public ModuleDescriptor getModuleDescriptor() {
    return myDescriptor;
  }

  private IFile createTempDirectory(String prefix) {
    try {
      final File temp;

      temp = File.createTempFile(prefix, "");

      if (!(temp.delete())) {
        throw new IOException("Could not delete temp file: " + temp.getAbsolutePath());
      }

      if (!(temp.mkdir())) {
        throw new IOException("Could not create temp directory: " + temp.getAbsolutePath());
      }

      return getFileSystem().getFile(temp.getAbsolutePath());
    } catch (IOException e) {
      LOG.error(e.toString(), e);
      return null;
    }
  }

  private class TempModuleJavaFacet implements JavaModuleFacet {
    private final IFile mySourceGen;
    private final IFile myClassesGen;

    @NotNull
    @Override
    public String getFacetType() {
      return FACET_TYPE;
    }

    public TempModuleJavaFacet(IFile sourceGen, IFile classesGen) {
      mySourceGen = sourceGen;
      myClassesGen = classesGen;
    }

    @Override
    public boolean isCompileInMps() {
      return true;
    }

    @Nullable
    @Override
    public IFile getOutputRoot() {
      return mySourceGen;
    }

    @NotNull
    @Override
    public IFile getClassesGen() {
      return myClassesGen;
    }

    @Override
    public Set<String> getLibraryClassPath() {
      return Collections.emptySet();
    }

    @Override
    public Set<String> getClassPath() {
      return Collections.singleton(getClassesGen().getPath());
    }

    @Override
    public Set<String> getAdditionalSourcePaths() {
      return Collections.emptySet();
    }

    @NotNull
    @Override
    public SModule getModule() {
      return TempModule.this;
    }

    @Override
    public void save(Memento memento) {
      throw new UnsupportedOperationException();
    }

    @Override
    public void load(Memento memento) {
      throw new UnsupportedOperationException();
    }
  }
}
