"use strict";

var _ = require("./utils");
var Emitter = require("./emitter");
var NeuDC = require("./dc");
var NeuUnit = require("./unit");
var makeOutlet = require("./ugen-makeOutlet");
var selectorParser = require("./selector-parser");

function NeuUGen(synth, key, spec, inputs) {
  Emitter.call(this);

  var parsed = selectorParser.parse(key);

  this.$synth   = synth;
  this.$context = synth.$context;
  this.$key   = parsed.key;
  this.$class = parsed.class;
  this.$id    = parsed.id;

  if (!NeuUGen.registered.hasOwnProperty(parsed.key)) {
    throw new Error("unknown key: " + key);
  }

  var unit = NeuUGen.registered[parsed.key](this, spec, inputs);

  if (!(unit instanceof NeuUnit)) {
    throw new Error("invalid key: " + key);
  }

  this.$unit   = unit;
  this.$outlet = makeOutlet(this.$context, unit, spec);
  this.$offset = 0;

  Object.defineProperties(this, {
    context: {
      value: _.findAudioContext(this.$context),
      enumerable: true
    },
    outlet: {
      value: _.findAudioNode(this.$outlet),
      enumerable: true
    },
  });

  _.each(unit.$methods, function(method, name) {
    _.definePropertyIfNotExists(this, name, {
      value: method
    });
  }, this);
}
_.inherits(NeuUGen, Emitter);

NeuUGen.registered = {};

NeuUGen.register = function(name, func) {
  if (!selectorParser.isValidUGenName(name)) {
    throw new Error("invalid ugen name: " + name);
  }
  if (!_.isFunction(func)) {
    throw new TypeError("ugen must be a function");
  }
  NeuUGen.registered[name] = func;
};

NeuUGen.build = function(synth, key, spec, inputs) {
  if (!_.isString(key)) {
    spec.value = key;
    key = _.typeOf(key);
  }

  return new NeuUGen(synth, key, spec, inputs);
};

NeuUGen.prototype._connect = function(to) {
  _.connect({ from: this.$outlet, to: to });
  if (this.$offset !== 0) {
    if (to instanceof window.AudioParam) {
      to.value = this.$offset;
    } else if (to instanceof window.AudioNode) {
      _.connect({ from: new NeuDC(this.$context, this.$offset), to: to });
    }
  }
};

NeuUGen.prototype.add = function(node) {
  return new NeuUGen(this.$synth, "+", {}, [ this, _.defaults(node, 0) ]);
};

NeuUGen.prototype.mul = function(node) {
  return new NeuUGen(this.$synth, "*", {}, [ this, _.defaults(node, 1) ]);
};

NeuUGen.prototype.madd = function(mul, add) {
  return this.mul(_.defaults(mul, 1)).add(_.defaults(add, 0));
};

module.exports = NeuUGen;

@@ -260,32 +260,33 @@
 nth, key, spec, 
+_
 inputs) %7B%0A  Emit
@@ -595,16 +595,237 @@
 );%0A  %7D%0A%0A
+  var inputs = %5B%5D;%0A  var offset = 0;%0A%0A  for (var i = 0, imax = _inputs.length; i %3C imax; i++) %7B%0A    if (typeof _inputs%5Bi%5D === %22number%22) %7B%0A      offset += _inputs%5Bi%5D;%0A    %7D else %7B%0A      inputs.push(_inputs%5Bi%5D);%0A    %7D%0A  %7D%0A%0A
   var un
@@ -878,16 +878,24 @@
 , inputs
+, offset
 );%0A%0A  if
@@ -992,18 +992,16 @@
 nit 
-  
 = unit;%0A
   th
@@ -996,24 +996,23 @@
 = unit;%0A
+%0A
   
-this.$
+var 
 outlet =
@@ -1050,16 +1050,49 @@
 , spec);
+%0A%0A  this.$outlet = outlet.outlet;
 %0A  this.
@@ -1097,25 +1097,37 @@
 s.$offset = 
-0
+outlet.offset
 ;%0A%0A  Object.

fix NeuUGen: offset support