var extractNodeView = require('./view-matrix-from-node')
var perspective = require('gl-mat4/perspective')
var KindredNode = require('./kindred-node')
var eyeVector = require('eye-vector')
var inherits = require('inherits')
var Fit = require('canvas-fit')

var scratchProj = new Float32Array(16)
var scratchEye = new Float32Array(3)
var scratchDrawProps = {
  background: null,
  proj: null,
  view: null,
  eye: null,
  fog: null,
  gl: null
}

module.exports = KindredScene

inherits(KindredScene, KindredNode)
function KindredScene (props) {
  if (!(this instanceof KindredScene)) {
    return new KindredScene(props)
  }

  KindredNode.call(this, props)
  this._projection = null

  var data = this.data
  data.fog = data.fog || false
  if (!('background' in data)) data.background = [0, 0, 0, 1]
  if (data.fog && data.fog.length === 3) data.fog[3] = 1
  if (data.background && data.background.length === 3) data.background[3] = 1
  data.frame = data.frame || 0
}

KindredScene.prototype.loop = function (canvas, runFrame) {
  if (typeof canvas === 'function') {
    runFrame = canvas
    canvas = null
  }

  if (!canvas) {
    var ratio = this.data.pixelRatio || 1
    canvas = document.createElement('canvas')
    document.body.appendChild(canvas)
    window.addEventListener('resize', Fit(canvas, null, ratio), false)
  }

  var gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl')
  var looping = true

  window.requestAnimationFrame(loop)
  function loop () {
    if (!looping) return

    var width = canvas.width
    var height = canvas.height

    gl.enable(gl.DEPTH_TEST)
    gl.enable(gl.CULL_FACE)

    runFrame(gl, width, height)
    window.requestAnimationFrame(loop)
  }

  return function cancel () {
    looping = false
  }
}

KindredScene.prototype.perspective = function (fov, width, height, near, far) {
  if (!this._projection) this._projection = new Float32Array(16)
  return perspective(this._projection, fov, width / height, near, far)
}

KindredScene.prototype.step = function (props) {
  this.data.frame++
  this._eachPreStep(props)
  this._eachStep(props)
  this._eachPostStep(props)
  this.tick()
}

KindredScene.prototype.getFrameProps = function (camera, width, height) {
  scratchDrawProps.proj = this._projection || _defaultProjection(width, height)
  scratchDrawProps.view = camera.data ? extractNodeView(camera) : camera
  scratchDrawProps.background = this.data.background
  scratchDrawProps.fog = this.data.fog
  scratchDrawProps.eye = eyeVector(scratchDrawProps.view, scratchEye)
  scratchDrawProps.frame = this.data.frame
  scratchDrawProps.width = width
  scratchDrawProps.height = height
  return scratchDrawProps
}

KindredScene.prototype._eachStep = componentTreeTrigger('step')
KindredScene.prototype._eachPreStep = componentTreeTrigger('preStep')
KindredScene.prototype._eachPostStep = componentTreeTrigger('postStep')

function componentTreeTrigger (name) {
  var args

  return function (_args) {
    args = _args
    forEach(this)
    this.each(forEach)
  }

  function forEach (node) {
    var list = node._componentList
    for (var i = 0; i < list.length; i++) {
      var component = list[i]
      if (component[name]) component[name](args)
    }
  }
}

function _defaultProjection (width, height) {
  return perspective(scratchProj, Math.PI / 4, width / height, 0.1, 500)
}

@@ -675,16 +675,49 @@
 n = null
+%0A  this._eachSorter = this.list()
 %0A%0A  var 
@@ -2083,16 +2083,50 @@
 .frame++
+%0A%0A  var nodes = this._eachSorter()
 %0A  this.
@@ -2134,24 +2134,31 @@
 eachPreStep(
+nodes, 
 props)%0A  thi
@@ -2165,24 +2165,31 @@
 s._eachStep(
+nodes, 
 props)%0A  thi
@@ -2204,16 +2204,23 @@
 ostStep(
+nodes, 
 props)%0A 
@@ -3006,80 +3006,116 @@
 er (
-name) %7B%0A  var args%0A%0A  return function (_args) %7B%0A    args = _
+triggerFunctionName) %7B%0A  return function triggerAllNodes (nodes, args) %7B%0A    triggerNode(this, 
 args
+)
 %0A    for
 Each
@@ -3114,71 +3114,125 @@
  for
-Each(this)%0A    this.each(forEach)%0A  %7D%0A%0A  function forEach (node
+ (var i = 0; i %3C nodes.length; i++) %7B%0A      triggerNode(nodes%5Bi%5D, args)%0A    %7D%0A  %7D%0A%0A  function triggerNode (node, args
 ) %7B%0A
@@ -3364,26 +3364,66 @@
 ent%5B
-name%5D) component%5Bn
+triggerFunctionName%5D) %7B%0A        component%5BtriggerFunctionN
 ame%5D
@@ -3425,24 +3425,32 @@
 Name%5D(args)%0A
+      %7D%0A
     %7D%0A  %7D%0A%7D%0A

use array instead of recursion on nodes