/*
 * Copyright (C) 2015, United States Government, as represented by the 
 * Administrator of the National Aeronautics and Space Administration.
 * All rights reserved.
 * 
 * The PSYCO: A Predicate-based Symbolic Compositional Reasoning environment 
 * platform is 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 gov.nasa.jpf.util;

import gov.nasa.jpf.Config;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.StandardJavaFileManager;
import javax.tools.StandardLocation;
import javax.tools.ToolProvider;

import org.stringtemplate.v4.ST;

/**
 * Compiles source generated from a string template
 */
public class TemplateBasedCompiler {
  
  private final File tmpSrcDir;
  
  private final List<File> sourceFiles = new ArrayList<>();
  
  
  public TemplateBasedCompiler() throws IOException {
    this.tmpSrcDir = File.createTempFile("jpf-jdart", "src");
    tmpSrcDir.delete();
    tmpSrcDir.mkdir();
    
  }
  
  public TemplateBasedCompiler(File outdir) {
    this.tmpSrcDir = outdir;
    tmpSrcDir.mkdirs();    
  } 
  

  public void addDynamicSource(String packageName, String className, Map<String,Object> attributes, InputStream tplIs) throws IOException {
    StringBuilder sb = new StringBuilder();
    try(BufferedReader r = new BufferedReader(new InputStreamReader(tplIs))) {
      String line;
      while((line = r.readLine()) != null) {
        sb.append(line).append('\n');
      }
    }
    
    ST tpl = new ST(sb.toString());
    
    for(Map.Entry<String, Object> e : attributes.entrySet()) {
      tpl.add(e.getKey(), e.getValue());
    }
    tpl.add("package", packageName);
    tpl.add("class", className);
    
    String packagePath = packageName.replace('.', File.separatorChar);
    
    File outputDir = new File(tmpSrcDir, packagePath);
    outputDir.mkdirs();
    
    File outputFile = new File(outputDir, className + ".java");
    
    try(FileWriter fw = new FileWriter(outputFile)) {
      fw.write(tpl.render());
    }
    
    sourceFiles.add(outputFile);
  }
  
  public File compile(Config config) throws IOException {
    JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
    StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
    fileManager.setLocation(StandardLocation.SOURCE_PATH, Collections.singleton(tmpSrcDir));
    
    File tmpClassDir = File.createTempFile("jpf-testing", "classes");
    tmpClassDir.delete();
    tmpClassDir.mkdir();
    fileManager.setLocation(StandardLocation.CLASS_OUTPUT, Collections.singleton(tmpClassDir));
    
    File[] cp = config.getPathArray("classpath");
    fileManager.setLocation(StandardLocation.CLASS_PATH, Arrays.asList(cp));
    
    CompilationTask task = compiler.getTask(null, fileManager, null, null, null, fileManager.getJavaFileObjectsFromFiles(sourceFiles));
    if(!task.call())
      throw new RuntimeException("Compilation failed");
    
    return tmpClassDir;
  }
  
  
  
}