package com.googlecode.jslint4java.maven;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.codehaus.plexus.util.FileUtils;
import org.codehaus.plexus.util.StringUtils;

import com.googlecode.jslint4java.Issue;
import com.googlecode.jslint4java.JSLint;

/**
 * Validates JavaScript using jslint4java.
 *
 * @author dom
 * @goal check
 * @phase verify
 */
public class JSLintMojo extends AbstractMojo {

    /**
     * Specifies the the source files to be used for JSLint (relative to
     * {@link #sourceDirectory}). If none are given, defaults to <code>**</code>
     * <code>/*.js</code>.
     *
     * @parameter
     */
    private String[] includes;

    /**
     * Specifies the the source files to be excluded for JSLint (relative to
     * {@link #sourceDirectory}). Maven applies its own defaults.
     *
     * @parameter
     */
    private String[] excludes;

    /**
     * Specifies the location of the source directory to be used for JSLint.
     *
     * @parameter expression="${jslint.sourceDirectory}"
     *            default-value="${basedir}/src/main/webapp"
     * @required
     */
    private File sourceDirectory;

    private final JSLint jsLint;

    public JSLintMojo() throws IOException {
        jsLint = new JSLint();
    }

    public void execute() throws MojoExecutionException, MojoFailureException {
        if (!sourceDirectory.exists()) {
            getLog().warn(sourceDirectory + " does not exist");
            return;
        }
        File[] files;
        try {
            files = getFilesToProcess(includes, excludes);
        } catch (IOException e) {
            throw new MojoExecutionException("Error getting files to process",
                    e);
        }
        int failures = 0;
        for (int i = 0; i < files.length; i++) {
            File file = files[i];
            failures += lintFile(file);
        }
        if (failures > 0) {
            throw new MojoFailureException("JSLint found " + failures
                    + " problems in " + files.length + " files");
        }
    }

    /**
     * Process includes and excludes to work out which files we ae interested
     * in. Originally nicked from CheckstyleReport, now looks nothing like it.
     */
    private File[] getFilesToProcess(String[] includes, String[] excludes)
            throws IOException {
        ArrayList includeList = new ArrayList();
        ArrayList excludeList = new ArrayList();

        if (includes != null && includes.length == 0) {
            includeList.addAll(Arrays.asList(includes));
        }
        // Defaults.
        if (includeList.isEmpty()) {
            includeList.add("**/*.js");
        }
        getLog().debug("includes=" + includeList);

        if (excludes != null && excludes.length > 0) {
            excludeList.addAll(Arrays.asList(excludes));
        }
        getLog().debug("excludes=" + excludeList);

        String[] defaultExcludes = FileUtils.getDefaultExcludes();
        for (int i = 0; i < defaultExcludes.length; i++) {
            excludeList.add(defaultExcludes[i]);
        }

        String includesStr = StringUtils.join(includeList.iterator(), ",");
        String excludesStr = StringUtils.join(excludeList.iterator(), ",");
        List files = FileUtils.getFiles(sourceDirectory, includesStr,
                excludesStr);
        getLog().debug("files=" + files);

        // How I wish for Java 5.
        return (File[]) files.toArray(new File[files.size()]);
    }

    private int lintFile(File file) throws MojoExecutionException {
        getLog().debug("lint " + file);
        InputStream stream = null;
        try {
            stream = new FileInputStream(file);
            BufferedReader reader = new BufferedReader(new InputStreamReader(
                    stream, "UTF-8"));
            List issues = jsLint.lint(file.toString(), reader);
            Iterator it = issues.iterator();
            while (it.hasNext()) {
                Issue issue = (Issue) it.next();
                logIssue(issue);
            }
            return issues.size();
        } catch (FileNotFoundException e) {
            throw new MojoExecutionException("file not found", e);
        } catch (UnsupportedEncodingException e) {
            throw new MojoExecutionException(
                    "unsupported character encoding UTF-8", e);
        } catch (IOException e) {
            throw new MojoExecutionException("aaaragh", e);
        }
    }

    private void logIssue(Issue issue) {
        getLog().info(issue.toString());
        getLog().info(issue.getEvidence());
        getLog().info(spaces(issue.getCharacter() - 1) + "^");
    }

    protected String spaces(int howmany) {
        StringBuffer sb = new StringBuffer(howmany);
        for (int i = 0; i < howmany; i++) {
            sb.append(" ");
        }
        return sb.toString();
    }

}

@@ -325,33 +325,8 @@
 st;%0A
-import java.util.Arrays;%0A
 impo
@@ -1081,33 +1081,53 @@
 private 
-String%5B%5D includes
+final List includes = new ArrayList()
 ;%0A%0A    /
@@ -1317,33 +1317,53 @@
 private 
-String%5B%5D excludes
+final List excludes = new ArrayList()
 ;%0A%0A    /
@@ -1971,20 +1971,25 @@
     
-File%5B%5D files
+List files = null
 ;%0A  
@@ -2244,45 +2244,67 @@
     
-for (int i = 0; i %3C files.length; i++
+Iterator it = files.iterator();%0A        while (it.hasNext()
 ) %7B%0A
@@ -2331,16 +2331,24 @@
 e = 
-files%5Bi%5D
+(File) it.next()
 ;%0A  
@@ -2539,22 +2539,22 @@
 + files.
-length
+size()
  + %22 fil
@@ -2751,339 +2751,162 @@
    *
-/
 %0A    
-private File%5B%5D getFilesToProcess(String%5B%5D includes, String%5B%5D excludes)%0A            throws IOException %7B%0A        ArrayList includeList = new ArrayList();%0A        Array
+ * @return a %7B@link List%7D of %7B@link File%7Ds.%0A     */%0A    private List getFilesToProcess(
 List 
-ex
+in
 clude
-List = new ArrayList();%0A%0A        if (includes != null && includes.length == 0) %7B%0A            includeList.addAll(Arrays.asList(includes));%0A        %7D
+s, List excludes)%0A            throws IOException %7B
 %0A   
@@ -2942,20 +2942,17 @@
 (include
-List
+s
 .isEmpty
@@ -2972,28 +2972,25 @@
      include
-List
+s
 .add(%22**/*.j
@@ -3052,137 +3052,11 @@
 lude
-List);%0A%0A        if (excludes != null && excludes.length %3E 0) %7B%0A            excludeList.addAll(Arrays.asList(excludes));%0A        %7D
+s);
 %0A   
@@ -3096,20 +3096,17 @@
  exclude
-List
+s
 );%0A%0A    
@@ -3246,20 +3246,17 @@
  exclude
-List
+s
 .add(def
@@ -3333,28 +3333,25 @@
 join(include
-List
+s
 .iterator(),
@@ -3410,20 +3410,17 @@
 (exclude
-List
+s
 .iterato
@@ -3626,54 +3626,13 @@
 urn 
-(File%5B%5D) files.toArray(new File%5Bfiles.size()%5D)
+files
 ;%0A  

Purge arrays.