var assert = require('chai').assert;

var writers = {
  HTMLWriter: require('./lib/html-writer'),
  JSWriter: require('./lib/js-writer'),
  StatsWriter: require('./lib/stats-writer')
};

var filters = {
  StyleFilter: require('./lib/style-filter'),
  StyleMinimizationFilter: require('./lib/style-minimization-filter'),
  StyleTokenizerFilter: require('./lib/style-tokenizer-filter'),
  NukeIFrameFilter: require('./lib/nuke-iframe-filter'),
  StyleDetokenizerFilter: require('./lib/style-detokenizer-filter')
};

var fabricators = {
  SchemaBasedFabricator: require('./lib/schema-based-fabricator'),
};

var stages = require('./gulp-stages');
var types = require('./gulp-types');
var device = require('./gulp-device');
var experiment = require('./gulp-experiment');


var argInputs = {
  'JSON': stages.JSONReader,
  'file': stages.fileReader,
  'output': stages.fileOutput,
  'save': device.telemetrySave,
  'perf': device.telemetryPerf
}

var byConstruction = [
  {list: writers, constructor: stages.treeBuilderWriter},
  {list: filters, constructor: stages.filter},
  {list: fabricators, constructor: stages.fabricator}
];
var byName = [device, experiment, stages];

function stageSpecificationToStage(stage) {
  var spec = stage.split(':');
  if (spec.length > 1 && spec[0] in argInputs)
    return argInputs[spec[0]](spec.slice(1, spec.length).join(':'));

  for (var i = 0; i < byConstruction.length; i++) {
    if (stage in byConstruction[i].list)
      return byConstruction[i].constructor(byConstruction[i].list[stage]);
  }

  for (var i = 0; i < byName.length; i++) {
    if (stage in byName[i])
      return byName[i][stage]();
  }
}

function processStages(stages, cb, fail) {
  processStagesWithInput(null, stages, cb, fail);
}

/*
 * Constructing a pipeline
 *
 * Sorry for potato quality.
 */
function processStagesWithInput(input, stages, cb, fail) {
  assert.equal(stages[0].input, 'unit');
  var coersion = {};
  for (var i = 0; i < stages.length - 1; i++) {
    coersion = types.coerce(stages[i].output, stages[i + 1].input, coersion);
    assert.isDefined(coersion, "Type checking failed for " + stages[i].output + " -> " + stages[i + 1].input);
  }
  for (var i = stages.length - 1; i >= 0; i--) {
    cb = (function(i, cb) { return function(data) {
      try {
        stages[i].impl(data, cb);
      } catch (e) {
        fail(e);
      }
    } })(i, cb);
  }
  cb(input);
};

module.exports.processStages = processStages;
module.exports.processStagesWithInput = processStagesWithInput;
module.exports.stageSpecificationToStage = stageSpecificationToStage;

@@ -1684,16 +1684,57 @@
 fail) %7B%0A
+  assert.equal(stages%5B0%5D.input, 'unit');%0A
   proces
@@ -1775,16 +1775,16 @@
  fail);%0A
-
 %7D%0A%0A/*%0A *
@@ -1907,49 +1907,8 @@
 ) %7B%0A
-  assert.equal(stages%5B0%5D.input, 'unit');%0A
   va
@@ -2053,16 +2053,19 @@
 on);%0A   
+ //
  assert.
@@ -2109,24 +2109,47 @@
 iled for %22 +
+ stages%5Bi%5D.name + ':' +
  stages%5Bi%5D.o
@@ -2179,23 +2179,246 @@
 %5Bi + 1%5D.
-input);
+name + ':' + stages%5Bi + 1%5D.input);%0A    if (coersion == undefined) %7B%0A      console.warn(%22couldn't type check %22 + stages%5Bi%5D.name + ':' + stages%5Bi%5D.output + %22 -%3E %22 + stages%5Bi + 1%5D.name + ':' + stages%5Bi + 1%5D.input);%0A      break;%0A    %7D
 %0A  %7D%0A  f

Replace type assertions with warnings, for now.