/* A lazy binding take on computed */
// doesn't start watching observables until itself is watched, and then releases if unwatched
// avoids memory/watcher leakage

var resolve = require('./resolve')

module.exports = computed

function computed (observables, lambda) {
  var values = []
  var releases = []
  var computedValue = null
  var live = false
  var lazy = false
  var listeners = []

  var result = function (listener) {
    if (!listener) {
      return getValue()
    }

    if (typeof listener !== 'function') {
      throw new Error('Listeners must be functions.')
    }

    listeners.push(listener)
    listen()

    return function remove () {
      for (var i = 0, len = listeners.length; i < len; i++) {
        if (listeners[i] === listener) {
          listeners.splice(i, 1)
          break
        }
      }
      if (!listeners.length) {
        unlisten()
      }
    }
  }

  return result

  // scoped

  function listen () {
    if (!live) {
      for (var i = 0, len = observables.length; i < len; i++) {
        if (typeof observables[i] === 'function') {
          releases.push(observables[i](onUpdate))
        }
      }
      live = true
      lazy = true
    }
  }

  function unlisten () {
    if (live) {
      live = false
      while (releases.length) {
        releases.pop()()
      }
    }
  }

  function update () {
    var changed = false
    for (var i = 0, len = observables.length; i < len; i++) {
      var newValue = resolve(observables[i])
      if (newValue !== values[i] || typeof newValue === 'object') {
        changed = true
        values[i] = newValue
      }
    }

    if (changed) {
      var newComputedValue = lambda.apply(null, values)
      if (newComputedValue !== computedValue || typeof newComputedValue === 'object') {
        computedValue = newComputedValue
        return true
      }
    }
    return false
  }

  function onUpdate () {
    if (update()) {
      for (var i = 0, len = listeners.length; i < len; i++) {
        listeners[i](computedValue)
      }
    }
  }

  function getValue () {
    if (!live || lazy) {
      lazy = false
      update()
    }
    return computedValue
  }
}

@@ -33,16 +33,18 @@
 ed */%0A//
+ -
  doesn't
@@ -129,16 +129,18 @@
 tched%0A//
+ -
  avoids 
@@ -166,113 +166,436 @@
 age%0A
-%0Avar resolve = require('./resolve')%0A%0Amodule.exports = computed%0A%0Afunction computed (observables, lambda) %7B
+// - attaches to inner observables if these are returned from value%0A// - doesn't broadcast if value is same as last value (and is %60value type%60 or observable, can't make assuptions about reference types)%0A%0Avar resolve = require('./resolve')%0Avar isObservable = require('./is-observable')%0A%0Amodule.exports = computed%0A%0Afunction computed (observables, lambda) %7B%0A  if (!Array.isArray(observables)) %7B%0A    observables = %5Bobservables%5D%0A  %7D%0A
 %0A  v
@@ -656,16 +656,63 @@
  = null%0A
+%0A  var inner = null%0A  var releaseInner = null%0A%0A
   var li
@@ -737,24 +737,50 @@
 azy = false%0A
+  var initialized = false%0A
   var listen
@@ -1545,24 +1545,95 @@
   %7D%0A      %7D%0A
+      if (inner) %7B%0A        releaseInner = inner(onInnerUpdate)%0A      %7D%0A
       live =
@@ -1723,24 +1723,111 @@
 ive = false%0A
+%0A      if (releaseInner) %7B%0A        releaseInner()%0A        releaseInner = null%0A      %7D%0A%0A
       while 
@@ -2194,19 +2194,60 @@
 (changed
-) %7B
+ %7C%7C !initialized) %7B%0A      initialized = true
 %0A      v
@@ -2339,24 +2339,25 @@
 tedValue %7C%7C 
+(
 typeof newCo
@@ -2384,103 +2384,788 @@
 ect'
-) %7B%0A        computedValue = newComputedValue%0A        return true%0A      %7D%0A    %7D%0A    return false
+ && !isObservable(newComputedValue))) %7B%0A        if (releaseInner) %7B%0A          releaseInner()%0A          inner = releaseInner = null%0A        %7D%0A%0A        if (isObservable(newComputedValue)) %7B%0A          // handle returning observable from computed%0A          computedValue = newComputedValue()%0A          inner = newComputedValue%0A          if (live) %7B%0A            releaseInner = inner(onInnerUpdate)%0A          %7D%0A        %7D else %7B%0A          computedValue = newComputedValue%0A        %7D%0A        return true%0A      %7D%0A    %7D%0A    return false%0A  %7D%0A%0A  function onInnerUpdate (value) %7B%0A    if (value !== computedValue %7C%7C typeof newComputedValue === 'object') %7B%0A      computedValue = value%0A      for (var i = 0, len = listeners.length; i %3C len; i++) %7B%0A        listeners%5Bi%5D(computedValue)%0A      %7D%0A    %7D
 %0A  %7D

resolve and broadcast inner value if observable