var traceur = require('traceur');

var ParseTreeTransformer = traceur.get('codegeneration/ParseTreeTransformer.js').ParseTreeTransformer;
var ModuleSpecifier = traceur.get('syntax/trees/ParseTrees.js').ModuleSpecifier;
var createStringLiteralToken = traceur.get('codegeneration/ParseTreeFactory.js').createStringLiteralToken;

function TraceurImportNormalizeTransformer(map) {
  this.map = map;
  return ParseTreeTransformer.apply(this, arguments);
}
TraceurImportNormalizeTransformer.prototype = Object.create(ParseTreeTransformer.prototype);
TraceurImportNormalizeTransformer.prototype.transformModuleSpecifier = function(tree) {
  var depName = this.map(tree.token.processedValue) || tree.token.processedValue;

  return new ModuleSpecifier(tree.location, createStringLiteralToken(depName));
};

exports.TraceurImportNormalizeTransformer = TraceurImportNormalizeTransformer;


function remap(source, map, fileName) {
  var compiler = new traceur.Compiler();

  var tree = compiler.parse(source, fileName);

  tree = new TraceurImportNormalizeTransformer(map).transformAny(tree);

  return Promise.resolve({
    source: compiler.write(tree)
  });
}
exports.remap = remap;

exports.attach = function(loader) {
  // NB for better performance, we should just parse and
  // cache the AST and store on the metadata, returning the deps only
  var loaderTranslate = loader.translate;
  loader.translate = function(load) {
    return loaderTranslate.call(this, load);
  };
};

var versionCheck = true;

exports.compile = function(load, opts, loader) {
  var normalize = opts.normalize;
  var options;

  // load.metadata.originalSource set by esm layer to allow plugin -> esm
  var source = load.metadata.originalSource || load.originalSource;

  return Promise.resolve(global[loader.transpiler == 'typescript' ? 'ts' : loader.transpiler] || loader.import(loader.transpiler))
  .then(function(transpiler) {
    if (transpiler.__useDefault)
      transpiler = transpiler['default'];

    if (transpiler.Compiler) {
      options = loader.traceurOptions || {};
      options.modules = 'instantiate';
      options.script = false;
      options.sourceRoot = true;
      options.moduleName = !opts.anonymous;

      if (opts.sourceMaps)
        options.sourceMaps = 'memory';
      if (opts.lowResSourceMaps)
        options.lowResolutionSourceMap = true;

      if (load.metadata.sourceMap)
        options.inputSourceMap = load.metadata.sourceMap;

      var compiler = new transpiler.Compiler(options);

      var tree = compiler.parse(source, load.address);

      var transformer = new TraceurImportNormalizeTransformer(function(dep) {
        return normalize ? load.depMap[dep] : dep;
      });

      tree = transformer.transformAny(tree);

      tree = compiler.transform(tree, load.name);

      var outputSource = compiler.write(tree, load.address);

      if (outputSource.match(/\$traceurRuntime/))
        load.metadata.usesTraceurRuntimeGlobal = true;

      return Promise.resolve({
        source: outputSource,
        sourceMap: compiler.getSourceMap()
      });
    }
    else if (transpiler.createLanguageService) {
      var options = loader.typescriptOptions || {};
      if (options.target === undefined)
        options.target = transpiler.ScriptTarget.ES5;
      options.module = transpiler.ModuleKind.System;

      source = transpiler.transpile(source, options, load.address, undefined, !opts.anonymous && load.name);
      
      return Promise.resolve({
        source: source,
        // TODO pipe TypeScript sourcemaps
        // sourceMap: {...}
      });
    }
    else {
      if (versionCheck) {
        var babelVersion = transpiler.version;
        if (babelVersion.split('.')[0] > 5)
          console.log('Warning - using Babel ' + babelVersion + '. This version of SystemJS builder is designed to run against Babel 5.');
        versionCheck = false;
      }
        
      options = loader.babelOptions || {};
      options.modules = 'system';
      if (opts.sourceMaps)
        options.sourceMap = true;
      if (load.metadata.sourceMap)
        options.inputSourceMap = load.metadata.sourceMap;
      options.filename = load.address;
      options.filenameRelative = load.name;
      options.sourceFileName = load.address;
      options.keepModuleIdExtensions = true;
      options.code = true;
      options.ast = false;
      options.moduleIds = !opts.anonymous;
      options.externalHelpers = true;

      if (transpiler.version.match(/^4/))
        options.returnUsedHelpers = true;
      else if (transpiler.version.match(/^5\.[01234]\./))
        options.metadataUsedHelpers = true;

      if (normalize)
        options.resolveModuleSource = function(dep) {
          return load.depMap[dep] || dep;
        };

      var output = transpiler.transform(source, options);

      var usedHelpers = output.usedHelpers || output.metadata && output.metadata.usedHelpers;

      if ((!options.optional || options.optional.indexOf('runtime') == -1) && usedHelpers.length)
        load.metadata.usesBabelHelpersGlobal = true;

      // pending Babel v5, we need to manually map the helpers
      if (options.optional && options.optional.indexOf('runtime') != -1)
        load.deps.forEach(function(dep) {
          if (dep.match(/^babel-runtime/))
            output.code = output.code.replace(dep, load.depMap[dep]);
        });

      return Promise.resolve({
        source: output.code,
        sourceMap: output.map
      });
    }
  });
};

@@ -3324,23 +3324,24 @@
 em;%0A
+      
 %0A      
-source =
+var
  tra
@@ -3350,90 +3350,272 @@
 pile
-r.transpile(source, options, load.address, undefined, !opts.anonymous && load.name
+Options = %7B %0A        compilerOptions: options, %0A        renamedDependencies: load.depMap, %0A        fileName: load.address, %0A        moduleName: !opts.anonymous && load.name %0A      %7D;%0A      %0A      var transpiled = transpiler.transpileModule(source, transpileOptions
 );%0A 
@@ -3671,70 +3671,61 @@
 ce: 
-source,%0A        // TODO pipe TypeScript
+transpiled.outputText,%0A       
  source
-maps%0A        // 
+Map: transpiled.
 sour
@@ -3733,15 +3733,12 @@
 eMap
-: %7B...%7D
+Text
 %0A   

make TypeScript transpile accept dependency map and return source maps