/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/
var fs = require("fs");
var path = require("path");
var async = require("async");
var loaderUtils = require("loader-utils");

// Matches only the last occurrence of sourceMappingURL
var baseRegex = "\\s*[@#]\\s*sourceMappingURL\\s*=\\s*([^\\s]*)(?![\\S\\s]*sourceMappingURL)",
	// Matches /* ... */ comments
	regex1 = new RegExp("/\\*"+baseRegex+"\\s*\\*/"),
	// Matches // .... comments
	regex2 = new RegExp("//"+baseRegex+"($|\n|\r\n?)"),
	// Matches DataUrls
	regexDataUrl = /data:[^;\n]+(?:;charset=[^;\n]+)?;base64,([a-zA-Z0-9+/]+={0,2})/;

module.exports = function(input, inputMap) {
	this.cacheable && this.cacheable();
	var resolve = this.resolve;
	var addDependency = this.addDependency;
	var emitWarning = this.emitWarning || function() {};
	var match = input.match(regex1) || input.match(regex2);
	if(match) {
		var url = match[1];
		var dataUrlMatch = regexDataUrl.exec(url);
		var callback = this.async();
		if(dataUrlMatch) {
			var mapBase64 = dataUrlMatch[1];
			var mapStr = (new Buffer(mapBase64, "base64")).toString();
			var map;
			try {
				map = JSON.parse(mapStr)
			} catch (e) {
				emitWarning("Cannot parse inline SourceMap '" + mapBase64.substr(0, 50) + "': " + e);
				return untouched();
			}
			processMap(map, this.context, callback);
		} else {
			resolve(this.context, loaderUtils.urlToRequest(url, true), function(err, result) {
				if(err) {
					emitWarning("Cannot find SourceMap '" + url + "': " + err);
					return untouched();
				}
				addDependency(result);
				fs.readFile(result, "utf-8", function(err, content) {
					if(err) {
						emitWarning("Cannot open SourceMap '" + result + "': " + err);
						return untouched();
					}
					var map;
					try {
						map = JSON.parse(content);
					} catch (e) {
						emitWarning("Cannot parse SourceMap '" + url + "': " + e);
						return untouched();
					}
					processMap(map, path.dirname(result), callback);
				});
			}.bind(this));
			return;
		}
	} else {
		var callback = this.callback;
		return untouched();
	}
	function untouched() {
		callback(null, input, inputMap);
	}
	function processMap(map, context, callback) {
		if(!map.sourcesContent || map.sourcesContent.length < map.sources.length) {
			var sourcePrefix = map.sourceRoot ? map.sourceRoot + "/" : "";
			map.sources = map.sources.map(function(s) { return sourcePrefix + s; });
			delete map.sourceRoot;
			var missingSources = map.sourcesContent ? map.sources.slice(map.sourcesContent.length) : map.sources;
			async.map(missingSources, function(source, callback) {
				resolve(context, loaderUtils.urlToRequest(source, true), function(err, result) {
					if(err) {
						emitWarning("Cannot find source file '" + source + "': " + err);
						return callback(null, null);
					}
					addDependency(result);
					fs.readFile(result, "utf-8", function(err, content) {
						if(err) {
							emitWarning("Cannot open source file '" + result + "': " + err);
							return callback(null, null);
						}
						callback(null, {
							source: result,
							content: content
						});
					});
				});
			}, function(err, info) {
				map.sourcesContent = map.sourcesContent || [];
				info.forEach(function(res) {
					if(res) {
						map.sources[map.sourcesContent.length] = res.source;
						map.sourcesContent.push(res.content);
					} else {
						map.sourcesContent.push(null);
					}
				});
				processMap(map, context, callback);
			});
			return;
		}
		callback(null, input.replace(match[0], ''), map);
	}
}