'use strict';

var util = require('util');
var assert = require('assert');
var EventEmitter = require('events').EventEmitter;

var namespaces = process.namespaces = Object.create(null);

function Context(namespace) {
  assert.ok(namespace, "Context must be associated with namespace!");
  EventEmitter.call(this);
  this.namespace = namespace;
  this.bag = Object.create(null);
}
util.inherits(Context, EventEmitter);

Context.prototype.enter = function () { this.namespace.active = this; };
Context.prototype.exit = function () { delete this.namespace.active; };
// TODO: Context.prototype.bind = function () {};
// TODO: Context.prototype.add = function () {};
Context.prototype.end = function () { this.emit('end'); };
Context.prototype.set = function (key, value) { this.bag[key] = value; };
Context.prototype.get = function (key) { return this.bag[key]; };
Context.prototype.run = function (callback) {
  this.enter();
  callback.call(this);
  this.exit();
  this.end();
};

function Namespace (name) {
  assert.ok(name, "Namespace must be given a name!");
  this.name = name;
  namespaces[name] = this;
}

// "class" method
Namespace.get = function (name) { return namespaces[name]; };

Namespace.prototype.createContext = function () {
  return new Context(this);
};

Namespace.prototype.set = function (name, value) {
  if (!this.active) {
    return;
    // alternately: this.emit('error', "No active context on namespace " + this.name + " to set " + name "."); return;
    // alternately: throw new Error("No active context on namespace " + this.name + " to set " + name ".");
  }

 this.active.set(name, value);
};

Namespace.prototype.get = function (name) {
  if (!this.active) {
    return;
    // alternately: this.emit('error', "No active context on namespace " + this.name + " to get " + name "."); return;
    // alternately: throw new Error("No active context on namespace " + this.name + " to get " + name ".");
  }

  return this.active.get(name);
};

module.exports = {
  createNamespace : function (name) { return new Namespace(name); },
  getNamespace : function (name) { return Namespace.get(name); }
};

@@ -471,21 +471,20 @@
 ace.
-active = 
+_enter(
 this
+)
 ; %7D;
@@ -527,14 +527,14 @@
 ) %7B 
-delete
+return
  thi
@@ -545,22 +545,27 @@
 mespace.
-active
+_exit(this)
 ; %7D;%0A// 
@@ -855,24 +855,95 @@
 ag%5Bkey%5D; %7D;%0A
+Context.prototype.hasKey = function (key) %7B return key in this.bag; %7D;%0A
 Context.prot
@@ -1136,28 +1136,8 @@
 ;%0A  
-this.name = name;%0A  
 name
@@ -1155,16 +1155,323 @@
 %5D = this
+;%0A%0A  this.name = name;%0A  this.nest = %5B%5D;%0A  // every namespace has a default / %22global%22 context%0A  this.nest.push(this.createContext());%0A  Object.defineProperty(this, %22active%22, %7B%0A    enumerable   : true,%0A    configurable : false,%0A    get          : function () %7B return this.nest%5Bthis.nest.length - 1%5D; %7D%0A  %7D)
 ;%0A%7D%0A%0A// 
@@ -1597,18 +1597,16 @@
 ion () %7B
-%0A 
  return 
@@ -1623,21 +1623,20 @@
 t(this);
-%0A
+ 
 %7D;%0A
-%0A
 Namespac
@@ -1663,20 +1663,19 @@
 nction (
-name
+key
 , value)
@@ -1672,32 +1672,25 @@
 ey, value) %7B
-%0A  if (!
+ 
 this.active)
@@ -1692,286 +1692,269 @@
 tive
-) %7B%0A    return;%0A    
+.set(key, value); %7D;%0A%0A
 // 
+f
 al
-ternately: this.emit('error', %22No active context on namespace %22 + this.name + %22 to set %22 + name %22.%22); return;%0A    // alternately: throw new Error(%22No active context on namespace %22 + this.name + %22 to set %22 + name %22.%22);%0A  %7D%0A%0A this.active.set(name, value);
+l through to enclosing context if value isn't found in this context%0ANamespace.prototype.get = function (key) %7B%0A  for (var i = this.nest.length; i %3E 0; i--) %7B%0A    if (this.nest%5Bi - 1%5D.hasKey(key)) return this.nest%5Bi - 1%5D.get(key);%0A  %7D
 %0A%7D;%0A
@@ -1966,35 +1966,38 @@
 space.prototype.
-get
+_enter
  = function (nam
@@ -1997,215 +1997,309 @@
 on (
-name) %7B%0A  if (!this.active) %7B%0A    return;%0A    // alternately: this.emit('error', %22No active context on namespace %22 + this.name + %22 to get %22 + name %22.%22); return;%0A    // alternately: throw new Error(%22No 
+context) %7B%0A  assert.ok(context, %22context must be provided for entering%22);%0A  this.nest.push(context);%0A%7D;%0A%0A// TODO: generalize nesting via configuration to handle domains%0ANamespace.prototype._exit = function (context) %7B%0A  assert.ok(context, %22context must be provided for exiting%22);%0A  if (this.
 active
+ ===
  con
@@ -2307,97 +2307,118 @@
 ext 
-on namespace %22 + this.name + %22 to get %22 + name %22.%22);%0A  %7D%0A%0A  return this.active.get(name);
+&&%0A      // don't delete the default context%0A      context !== this.nest%5B0%5D) %7B%0A    return this.nest.pop();%0A  %7D
 %0A%7D;%0A

Handle domain nesting.