/**
 * Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies
 * 
 * Please see distribution for license.
 */
package com.opengamma.maven.scripts;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.maven.artifact.DependencyResolutionRequiredException;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.project.MavenProject;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.opengamma.maven.MojoUtils;
import com.opengamma.util.ClasspathUtils;
import com.opengamma.util.annotation.ClassNameAnnotationScanner;
import com.opengamma.util.generate.scripts.Scriptable;
import com.opengamma.util.generate.scripts.ScriptsGenerator;

import freemarker.cache.FileTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;

/**
 * Maven plugin for generating scripts to run tools annotated with {@link Scriptable}.
 * <p>
 * Variables available in the Freemarker template are:
 * <ul>
 * <li> className - the fully-qualified Java class name
 * </ul>
 * 
 * @goal scripts-tools
 * @requiresDependencyResolution compile
 * @threadSafe
 */
public class ScriptableScriptGeneratorMojo extends AbstractMojo {

  /**
   * @parameter alias="outputDir" default-value="${project.build.directory}/scripts-tools"
   * @required
   */
  private File _outputDir;
  /**
   * @parameter alias="template"
   */
  private String _template;
  /**
   * @parameter alias="baseClassTemplateMap"
   */
  private Map<String, String> _baseClassTemplateMap;
  /**
   * @parameter alias="resourceArtifact"
   */
  private String _resourceArtifact;
  /**
   * @parameter alias="additionalScripts"
   */
  private String[] _additionalScripts;
  /**
   * @parameter default-value="${project.basedir}"
   * @required
   */
  private File _baseDir;
  
  /**
   * @component
   */
  private MavenProject _project;
  
  //-------------------------------------------------------------------------
  @SuppressWarnings("unchecked")
  @Override
  public void execute() throws MojoExecutionException, MojoFailureException {
    boolean templateSet = !StringUtils.isBlank(_template);
    boolean templateMapSet = _baseClassTemplateMap != null && _baseClassTemplateMap.isEmpty();
    if ((templateSet && templateMapSet) || (!templateSet && !templateMapSet)) {
      throw new MojoExecutionException("Exactly one of 'template' or 'baseClassTemplateMap' must be set");
    }
    if (!_outputDir.exists()) {
      try {
        getLog().debug("Creating output directory " + _outputDir);
        if (!_outputDir.mkdirs()) {
          throw new MojoExecutionException("Unable to create output directory " + _outputDir.getAbsolutePath());
        }
      } catch (Exception e) {
        throw new MojoExecutionException("Error creating output directory " + _outputDir.getAbsolutePath());
      }
    }
    
    List<String> classpathElementList;
    try {
      classpathElementList = _project.getCompileClasspathElements();
    } catch (DependencyResolutionRequiredException e) {
      throw new MojoExecutionException("Error obtaining dependencies", e);
    }
    URL[] classpathUrls = ClasspathUtils.getClasspathURLs(classpathElementList);
    Set<String> annotationClasses = ClassNameAnnotationScanner.scan(classpathUrls, Scriptable.class.getName());
    getLog().info("Generating " + annotationClasses.size() + " scripts");
    
    ClassLoader classLoader = new URLClassLoader(classpathUrls, this.getClass().getClassLoader());
    Map<Class<?>, Template> templateMap = resolveTemplateMap(classLoader);
    
    for (String className : annotationClasses) {
      Map<String, Object> templateData = new HashMap<String, Object>();
      templateData.put("className", className);
      Template template = getTemplateForClass(className, classLoader, templateMap);
      if (template == null) {
        getLog().warn("No template for scriptable class " + className);
        continue;
      }
      ScriptsGenerator.generate(className, _outputDir, template, templateData);
    }
    
    if (_additionalScripts != null) {
      getLog().info("Copying " + _additionalScripts.length + " additional script(s)");
      for (String script : _additionalScripts) {
        File scriptFile = new File(script);
        if (scriptFile.exists()) {
          if (!scriptFile.isFile()) {
            throw new MojoExecutionException("Can only copy files, but additional script '" + scriptFile + "' is not a file");
          }
          try {
            FileUtils.copyFileToDirectory(scriptFile, _outputDir);
            File copiedFile = new File(_outputDir, scriptFile.getName());
            copiedFile.setReadable(true, false);
            copiedFile.setExecutable(true, false);
          } catch (IOException e) {
            throw new MojoExecutionException("Unable to copy additional script '" + scriptFile + "'", e);
          }
        }
      }
    }
  }

  //-------------------------------------------------------------------------
  private Map<Class<?>, Template> resolveTemplateMap(ClassLoader classLoader) throws MojoExecutionException {
    if (_baseClassTemplateMap == null || _baseClassTemplateMap.isEmpty()) {
      return ImmutableMap.<Class<?>, Template>of(Object.class, getTemplate(_template));
    }
    Map<Class<?>, Template> templateMap = new HashMap<Class<?>, Template>();
    for (Map.Entry<String, String> unresolvedEntry : _baseClassTemplateMap.entrySet()) {
      String className = unresolvedEntry.getKey();
      Class<?> clazz;
      try {
        clazz = classLoader.loadClass(className);
      } catch (ClassNotFoundException e) {
        throw new IllegalArgumentException("Unable to resolve class " + className);
      }
      Template template = getTemplate(unresolvedEntry.getValue());
      templateMap.put(clazz, template);
    }
    return templateMap;
  }
  
  private Template getTemplateForClass(String className, ClassLoader classLoader, Map<Class<?>, Template> templateMap) throws MojoExecutionException {
    if (templateMap.size() == 1 && Object.class.equals(Iterables.getOnlyElement(templateMap.keySet()))) {
      return Iterables.getOnlyElement(templateMap.values());
    }
    Class<?> clazz;
    try {
      clazz = classLoader.loadClass(className);
    } catch (ClassNotFoundException e) {
      throw new MojoExecutionException("Unable to resolve class " + className);
    }
    for (Map.Entry<Class<?>, Template> templateMapEntry : templateMap.entrySet()) {
      if (templateMapEntry.getKey().isAssignableFrom(clazz)) {
        return templateMapEntry.getValue();
      }
    }
    return null;
  }

  private Template getTemplate(String templateName) throws MojoExecutionException {
    try {
      if (_resourceArtifact == null) {
        return getTemplateFromFile(_baseDir, templateName);
      } else {
        ClassLoader resourceLoader = getResourceLoader(_resourceArtifact, _project);
        InputStream resourceStream = resourceLoader.getResourceAsStream(templateName);
        if (resourceStream == null) {
          throw new MojoExecutionException("Resource '" + templateName + "' not found");
        }
        String templateStr;
        try {
          templateStr = IOUtils.toString(resourceStream);
        } finally {
          resourceStream.close();
        }
        return new Template(templateName, new StringReader(templateStr), new Configuration());
      }
    } catch (IOException e) {
      throw new MojoExecutionException("Error loading Freemarker template from '" + templateName + "'", e);
    }
  }

  private static ClassLoader getResourceLoader(String resourceArtifact, MavenProject project) throws MojoExecutionException, MalformedURLException {
    if (StringUtils.isBlank(resourceArtifact)) {
      return ScriptableScriptGeneratorMojo.class.getClassLoader();
    }
    File artifactFile = MojoUtils.getArtifactFile(resourceArtifact, project);
    return new URLClassLoader(new URL[] { artifactFile.toURI().toURL() });
  }

  private static Template getTemplateFromFile(File baseDir, String templateFile) throws IOException {
    Template template;
    Configuration cfg = new Configuration();
    cfg.setTemplateLoader(new FileTemplateLoader(baseDir));
    template = cfg.getTemplate(templateFile);
    return template;
  }

}

@@ -580,24 +580,68 @@
 tringUtils;%0A
+import org.apache.commons.lang.SystemUtils;%0A
 import org.a
@@ -4577,16 +4577,43 @@
 lateData
+, SystemUtils.IS_OS_WINDOWS
 );%0A    %7D

Fix compile error from recent change to script generator