'use strict';

const {
	VMError
} = require('./bridge');

/**
 * Returns the cached coffee script compiler or loads it
 * if it is not found in the cache.
 *
 * @private
 * @param {Object} [options] - Optional compiler options.
 * @return {compileCallback} The coffee script compiler.
 * @throws {VMError} If the coffee-script module can't be found.
 */
function getCoffeeScriptCompiler(options) {
	try {
		// The warning generated by webpack can be disabled by setting:
		// ignoreWarnings[].message = /Can't resolve 'coffee-script'/
		 
		const coffeeScript = require('coffee-script');
		return (code, filename) => {
			return coffeeScript.compile(code, Object.assign({header: false, bare: true}, options));
		};
	} catch (e) {
		throw new VMError('Coffee-Script compiler is not installed.');
	}
}

/**
 * Returns the cached TypeScript compiler or loads it
 * if it is not found in the cache.
 *
 * @private
 * @param {Object} [options] - Optional compiler options.
 * @return {compileCallback} The TypeScript compiler.
 * @throws {VMError} If the TypeScript module can't be found.
 */
function getTypeScriptCompiler(options) {
	try {
		// The warning generated by webpack can be disabled by setting:
		// ignoreWarnings[].message = /Can't resolve 'typescript'/
		 
		const typescript = require('typescript');
		return (code, filename) => {
			return typescript.transpileModule(code, {
				fileName: filename,
				compilerOptions: Object.assign({
					module: typescript.ModuleKind.CommonJS,
				}, options)
			}).outputText;
		};
	} catch (e) {
		throw new VMError('TypeScript compiler is not installed.');
	}
}

/**
 * Remove the shebang from source code.
 *
 * @private
 * @param {string} code - Code from which to remove the shebang.
 * @return {string} code without the shebang.
 */
function removeShebang(code) {
	if (!code.startsWith('#!')) return code;
	return '//' + code.substring(2);
}


/**
 * The JavaScript compiler, just a identity function.
 *
 * @private
 * @type {compileCallback}
 * @param {string} code - The JavaScript code.
 * @param {string} filename - Filename of this script.
 * @return {string} The code.
 */
function jsCompiler(code, filename) {
	return removeShebang(code);
}

/**
 * Look up the compiler for a specific name.
 *
 * @private
 * @param {(string|compileCallback)} compiler - A compile callback or the name of the compiler.
 * @param {Object} [options] - Optional compiler options.
 * @return {compileCallback} The resolved compiler.
 * @throws {VMError} If the compiler is unknown or the coffee script module was needed and couldn't be found.
 */
function lookupCompiler(compiler, options) {
	if ('function' === typeof compiler) return compiler;
	switch (compiler) {
		case 'coffeescript':
		case 'coffee-script':
		case 'cs':
		case 'text/coffeescript':
			return getCoffeeScriptCompiler(options);
		case 'javascript':
		case 'java-script':
		case 'js':
		case 'text/javascript':
			return jsCompiler;
		case 'typescript':
		case 'type-script':
		case 'ts':
		case 'text/typescript':
			return getTypeScriptCompiler(options);
		default:
			throw new VMError(`Unsupported compiler '${compiler}'.`);
	}
}

exports.removeShebang = removeShebang;
exports.lookupCompiler = lookupCompiler;
