package org.jsdoc;

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import org.mozilla.javascript.commonjs.module.provider.UrlModuleSourceProvider;
import org.mozilla.javascript.json.JsonParser;
import org.mozilla.javascript.json.JsonParser.ParseException;
import org.mozilla.javascript.commonjs.module.provider.ModuleSource;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.NativeObject;
import org.mozilla.javascript.tools.SourceReader;


/**
 * An extension of Rhino's UrlModuleSourceProvider that supports Node.js/CommonJS packages.
 * @author Jeff Williams
 */
public class JsDocModuleProvider extends UrlModuleSourceProvider {
	private static final String JS_EXTENSION = ".js";
	private static final String PACKAGE_FILE = "package.json";
	private static final String MODULE_INDEX = "index" + JS_EXTENSION;

	public JsDocModuleProvider(Iterable<URI> privilegedUris, Iterable<URI> fallbackUris) {
		super(privilegedUris, fallbackUris);
	}

	@Override
	protected ModuleSource loadFromUri(URI uri, URI base, Object validator)
		throws IOException, URISyntaxException {
		String uriString = uri.toString();
		if (!uriString.endsWith(JS_EXTENSION)) {
			uriString += JS_EXTENSION;
		}

		File jsFile = new File(new URI(uriString));
		File packageFile = new File(new URI(uri.toString() + File.separator + PACKAGE_FILE));
		File indexFile = new File(new URI(uri.toString() + File.separator + MODULE_INDEX));

		try {
			URI moduleUri = getModuleUri(jsFile, packageFile, indexFile);
			return loadFromActualUri(moduleUri, base, validator);
		} catch (Exception e) {
			return null;
		}
	}

	private URI getModuleUri(File jsFile, File packageFile, File indexFile)
		throws SecurityException, IOException, ParseException {

		// Check for the following, in this order:
		// 1. The file jsFile.
		// 2. The "main" property of the JSON file packageFile.
		// 3. The file indexFile.
		if (jsFile.isFile()) {
			return jsFile.toURI();
		} 

		if (packageFile.isFile()) {
			URI packageMain = getPackageMain(packageFile);
			if (packageMain != null) {
				return packageMain;
			}
		}

		if (indexFile.isFile()) {
			return indexFile.toURI();
		}

		// couldn't find the module URI
		return null;
	}

	private URI getPackageMain(File packageFile) throws IOException, ParseException {
		NativeObject packageJson = parsePackageFile(packageFile);
		String mainFile = (String) packageJson.get("main");
		if (mainFile != null) {
			return packageFile.toURI().resolve(mainFile);
		} else {
			return null;
		}
	} 

	private NativeObject parsePackageFile(File packageFile) throws IOException, ParseException {
		String packageJson = SourceReader.readFileOrUrl(packageFile.toString(), true, "UTF-8").
			toString();

		Context cx = Context.enter();
		JsonParser parser = new JsonParser(cx, cx.initStandardObjects());
		NativeObject json = (NativeObject) parser.parseValue(packageJson);
		return json;
	}
}

@@ -1169,119 +1169,51 @@
 %7B%0A%09%09
-String uriString = uri.toString();%0A%09%09if (!uriString.endsWith(JS_EXTENSION)) %7B%0A%09%09%09uriString += JS_EXTENSION;%0A%09%09%7D
+URI uriWithExtension = addJsExtension(uri);
 %0A%0A%09%09
@@ -1239,26 +1239,24 @@
 ile(
-new URI(uriString)
+uriWithExtension
 );%0A%09
@@ -2432,24 +2432,64 @@
  != null) %7B%0A
+%09%09%09mainFile = addJsExtension(mainFile);%0A
 %09%09%09return pa
@@ -2558,17 +2558,283 @@
 ;%0A%09%09%7D%0A%09%7D
- 
+%0A%0A%09private URI addJsExtension(URI uri) throws URISyntaxException %7B%0A%09%09String str = uri.toString();%0A%09%09return new URI(addJsExtension(str));%0A%09%7D%0A%0A%09private String addJsExtension(String str) %7B%0A%09%09if (!str.endsWith(JS_EXTENSION)) %7B%0A%09%09%09str += JS_EXTENSION;%0A%09%09%7D%0A%09%09return str;%0A%09%7D
 %0A%0A%09priva

if necessary, append ".js" to the "main" property in package.json