import * as path from "path";
import {readFileSync, writeFileSync} from "fs";
import {module as tslibModule, name as tslib} from "tslib/package.json";
import {createService} from "./service";



export default function tsc(tsconfig) {
	tsconfig = tsconfig || {};
	tsconfig.compilerOptions = tsconfig.compilerOptions || {};

	const loadTslib = tslibLoader();
	const service = createService(tsconfig);

	let bundleInput;
	let bundleDecls = [];

	return {
		name: "tsc",

		options(opts) {
			tsconfig.sourcemap = tsconfig.sourcemap || opts.sourcemap;
			bundleInput = opts.input;
			if(!path.isAbsolute(bundleInput)) {
				const cwd = service.host.getCurrentDirectory();
				bundleInput = path.join(cwd, bundleInput);
			}
		},

		resolveId(importee, importer) {
			if(importee === tslib) {
				return `\0${tslib}`;
			} else if(!importer) {
				return null;
			}
			importer = importer.replace("\\", "/");

			const {resolvedFileName} = service.host.resolveModuleName(importee, importer) || {};
			return resolvedFileName && !resolvedFileName.endsWith(".d.ts")
				? resolvedFileName
				: null;
		},

		load(id) {
			if(id === `\0${tslib}`) {
				return loadTslib();
			}
			return null;
		},

		transform(code, id) {
			if(!service.filter(id)) {
				return null;
			}
			service.host.addFile(id, code);

			const {outputFiles, errors, warnings} = service.emit(id);
			if(warnings && warnings.length) {
				warnings.forEach(this.warn);
			}
			if(errors && errors.length) {
				errors.forEach(err => console.error(`ERROR: ${err}`));
				this.error(`typescript compilation failed`);
			}

			const js = outputFiles.find(f => f.name.endsWith(".js") || f.name.endsWith(".jsx"));
			const map = outputFiles.find(f => f.name.endsWith(".map"));
			const dts = outputFiles.find(f => f.name.endsWith(".d.ts"));
			if(dts) {
				bundleDecls.push(dts);
			}

			return !js ? null : {
				code: js.text,
				map: map ? JSON.parse(map.text) : {mappings: ""},
			};
		},

		onwrite(opts) {
			const dir = tsconfig.compilerOptions.declarationDir || path.dirname(opts.file);
			bundleDecls.forEach(decl => {
				const file = path.basename(decl.name);
				writeFileSync(path.join(dir, file), decl.text);
			});
		},
	};
}

function tslibLoader() {
	let src;
	return function() {
		if(!src) {
			src = readFileSync(require.resolve(`${tslib}/${tslibModule}`), "utf8");
		}
		return src;
	}
}

@@ -58,16 +58,37 @@
 FileSync
+, mkdirSync, statSync
 %7D from %22
@@ -1991,16 +1991,63 @@
 opts) %7B%0A
+%09%09%09if(!bundleDecls.length) %7B%0A%09%09%09%09return;%0A%09%09%09%7D%0A%0A
 %09%09%09const
@@ -2121,16 +2121,34 @@
 .file);%0A
+%09%09%09mkdirAll(dir);%0A
 %09%09%09bundl
@@ -2434,16 +2434,16 @@
 %22);%0A%09%09%7D%0A
-
 %09%09return
@@ -2453,8 +2453,247 @@
 c;%0A%09%7D%0A%7D%0A
+%0Afunction mkdirAll(path) %7B%0A%09try %7B%0A%09%09mkdirSync(path);%0A%09%7D catch(e) %7B%0A%09%09if(e.code === %22ENOENT%22) %7B%0A%09%09%09mkdirAll(path.dirname(path));%0A%09%09%09return mkdirSync(path);%0A%09%09%7D%0A%09%09const stat = statSync(path);%0A%09%09if(!stat.isDirectory()) %7B%0A%09%09%09throw e;%0A%09%09%7D%0A%09%7D%0A%7D%0A

create declaration directory if necessary