         @resolveDependencies.bind(this, container),
         @applyFactory.bind this
 
-      @asyncWaterfall series, done
+      $.asyncWaterfall series, done
 
+  $ = new Configuration configuration
 
-  return IOC
+  IOC =
+    configuration: $
+    configure: configure
+    Configuration: Configuration
+    CustomError: CustomError
+    ComponentUndefined: ComponentUndefined
+    ComponentDependenciesUndefined: ComponentDependenciesUndefined
+    ComponentCyclic: ComponentCyclic
+    Dictionary: Dictionary
+    Components: Components
+    Promises: Promises
+    Container: Container
+    Component: Component
 
 module.exports = configure()

    isUndefined: (name) ->
      @definitions[name] == undefined

  class IOC.Container
    createPromise: $.createPromise

    constructor: (props = {}) ->
      @components = props.components ?= new IOC.Dictionary
      @componentPromises = props.componentPromises ?= new IOC.Dictionary

    createComponentPromise: (name) ->
      component = @components.get name

      @createPromise (resolve, reject) =>
        component.resolve this, (error, result) ->
          return reject error if error?
          return resolve result

    ensureComponentPromise: (name) ->
      return if @componentPromises.isDefined name
      @componentPromises.set name, @createComponentPromise(name)

    getComponentDependenciesNotDefined: (name) ->
      component = @components.get name
      component.dependencies.filter @components.isUndefined.bind(@components)

    assertComponentDependenciesDefined: (name) ->
      dependenciesNotDefined = @getComponentDependenciesNotDefined name
      return if dependenciesNotDefined.length is 0

      throw new IOC.ComponentDependenciesNotDefined \
        componentName: name
        dependenciesNotDefined: dependenciesNotDefined

    findComponentCycles: (name) ->
      stack = []
      cycles = []

      stackContains = (value) ->
        stack.some (name) -> name is value

      visitComponent = (name) =>
        return cycles.push(stack.concat(name)) if stackContains(name)
        stack.push name
        dependencies = @components.get(name).dependencies
        dependencies.forEach visitComponent
        stack.pop()

      visitComponent name
      return cycles

    assertComponentIsAcyclic: (name) ->
      cycles = @findComponentCycles name
      return if cycles.length is 0
      throw new IOC.ComponentNotAcyclic
        componentName: name
        cycles: cycles

    validateComponent: (name) ->
      return if @componentPromises.isDefined name
      @assertComponentDependenciesDefined name
      @assertComponentIsAcyclic name

    resolveComponent: (name, done) ->
      @validateComponent name
      @ensureComponentPromise name
      @componentPromises.get(name).then done.bind(null, null), done

  class IOC.Component
    asyncMap: $.asyncMap
    asyncWaterfall: $.asyncWaterfall

    constructor: (props = {}) ->
      @dependencies = props.dependencies ? []
      @factory = props.factory

    resolveDependencies: (container, done) ->
      resolveComponent = container.resolveComponent.bind container
      @asyncMap @dependencies, resolveComponent, done

    applyFactory: (args, done) ->
      @factory args..., done

configure = (configuration) ->

  class Configuration
    constructor: (properties = {}) ->
      @[key] = val for own key,val of properties

    require: (name) ->
      require name

    createPromise: (resolver) ->
      @require("when").promise resolver

    asyncMap: (array, mapFn, done) ->
      @require("async").map array, mapFn, done

    asyncWaterfall: (series, done) ->
      @require("async").waterfall series, done

  class CustomError extends Error
    name: undefined

    constructor: (properties = {}) ->
      @[key] = val for own key,val of properties

      @name = @constructor.name
      Error.captureStackTrace @, @constructor

  class ComponentDependenciesUndefined extends CustomError
    componentName: undefined
    undefinedDependencies: undefined

    Object.defineProperty @prototype, "message", get: ->
      "Component '#{@componentName}' " +
      "has undefined dependencies: [#{@undefinedDependencies}]"

  class ComponentCyclic extends CustomError
    componentName: undefined
    cycles: undefined

    inspectCycles: ->
      mapFn = (cycle) -> "* #{cycle.join(" > ")}"
      @cycles.map(mapFn).join("\n")

    Object.defineProperty @prototype, "message", get: ->
      "Component '#{@componentName}' has cycles:\n" +
      @inspectCycles()

  class ComponentUndefined extends CustomError
    componentName: undefined

    Object.defineProperty @prototype, "message", get: ->
      "Component not defined: '#{@componentName}'"

  class Dictionary
    @definitions: undefined

    constructor: (properties = {}) ->
      @[key] = val for own key,val of properties
      @definitions ?= {}

    set: (name, value) ->
      @definitions[name] = value
