         @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()

configure = ($dependencies) ->
  IOC = {}
  IOC.configure = configure

  constructDependencies = (props = {}) ->
    $ = Object.create props

    $.createPromise ?= (resolver) ->
      require("when").promise resolver

    $.asyncMap ?= (array, mapFn, done) ->
      require("async").map array, mapFn, done

    $.asyncWaterfall ?= (series, done) ->
      require("async").waterfall series, done

    return $

  $ = constructDependencies $dependencies

  class IOC.ComponentDependenciesNotDefined extends Error
    constructor: (props = {}) ->
      @name = @constructor.name
      @componentName = props.componentName
      @dependenciesNotDefined = props.dependenciesNotDefined
      super @getMessage()
      Error.captureStackTrace @, @constructor

    getMessage: ->
      "Component '#{@componentName}' " +
      "has undefined dependencies: [#{@dependenciesNotDefined}]"

  class IOC.ComponentNotAcyclic extends Error
    constructor: (props = {}) ->
      @name = @constructor.name
      @componentName = props.componentName
      @cycles = props.cycles
      @message = @getMessage()
      Error.captureStackTrace @, @constructor

    getMessage: ->
      message = "Component '#{@componentName}' has cycles:\n"
      @cycles.forEach (cycle) ->
        message += "*  #{cycle.join(' > ')}"
      message

  class IOC.Dictionary
    constructor: (props = {}) ->
      @definitions = props.definitions ?= {}

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

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

  class Components extends Dictionary

    getUndefinedDependencies: (name) ->
      @get(name).dependencies.filter @isUndefined.bind(@)

    getCycles: (name, stack = []) ->
      return stack.concat(name) if stack.indexOf(name) isnt -1
      return [] unless @isDefined(name)

      reduceDependencyCycles = (memo, dependency) =>
        cycles = @getCycles dependency, stack
        memo.push cycles if cycles.length isnt 0
        memo

      stack = stack.concat name
      dependencies = @get(name).dependencies
      dependencies.reduce reduceDependencyCycles, []

    assertDefined: (name) ->
      return if @isDefined(name)
      throw new ComponentUndefined
        componentName: name

    assertAcyclic: (name) ->
      cycles = @getCycles name
      return if cycles.length is 0
      throw new ComponentCyclic
        componentName: name
        cycles: cycles

    assertDependenciesDefined: (name) ->
      undefinedDependencies = @getUndefinedDependencies name
      return if undefinedDependencies.length is 0
      throw new ComponentDependenciesUndefined
        componentName: name
        undefinedDependencies: undefinedDependencies

    validate: (name) ->
      @assertDefined name
      @assertAcyclic name
      @assertDependenciesDefined name

  class Promises extends Dictionary

  class Container
    components: undefined
    promises: undefined

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

      @components ?= new Components
      @promises ?= new Promises

    createComponentPromise: (name) ->
      $.createPromise (resolve, reject) =>
        @components.get(name).resolve this, (error, result) ->
          return reject error if error?
          return resolve result

    ensureComponentPromiseSet: (name) ->
      return if @promises.isDefined name
      @promises.set name, @createComponentPromise(name)
      @promises.get name

    resolveComponent: (name, done) ->
      try
        @components.validate(name) unless @promises.isDefined(name)
        @ensureComponentPromiseSet(name).then done.bind(null, null), done
      catch error then done error

  class Component
    dependencies: undefined
    factory: undefined

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

      @dependencies ?= []
      @factory ?= null

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

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