# Base class for Modules.
class window.modularity.Module

  # The container variable is required. Provide 'testing' in tests.
  constructor: (container) ->
    container = $(container) if (typeof container is 'string') and container isnt 'testing'
    @container = container

    return console.error 'Error in Module constructor: No container given.' unless @container?
    if container isnt 'testing'
      unless typeof container.jquery is 'string'
        return console.error 'Error in Module constructor:
                              The given container must be a jQuery object.'
      unless container? and container.length > 0
        return console.error "Error in Module constructor:
                              The given container ('#{container.selector}') is empty."
      unless container? and container.length is 1
        return console.error "Error in Module constructor:
                              The given container ('#{container.selector}')
                              has more than one element."


    # Attach mixins.
    if @mixins?
      for mixin_data in @mixins

        # Attach all properties from mixin to the prototype.
        for methodName, method of mixin_data.mixin
          do (methodName, method) => unless @[methodName]
            @[methodName] = => method.apply(@, arguments)

        # Call constructor function from mixin.
        mixin_data.mixin?.constructor?.apply(@, arguments)


  # Runs the given query within the container element.
  $: (query, options = { reportEmptyResults: yes }) ->
    result = @container.find query
    if result.length is 0 and options['reportEmptyResults']
      return console.error "@$: element not found: #{result.selector}"
    result


  # MODULE EVENTS.

  # Calls the given function when this widget fires the given local event.
  on: (event_type, callback) =>
    return unless modularity.assert(typeof event_type == 'string',
                                    "Module.bind_event: parameter 'event_type' is empty")
    unless typeof callback is 'function'
      return console.error "Module.bind_event: parameter 'callback' must be a function,
                            #{callback} (#{typeof callback}) given."
    @container.bind event_type, callback
    @


  # Fires the given local event with the given data payload.
  trigger: (event_type, data) =>
    modularity.assert event_type, 'Module.fire_event: You must provide the event type to fire.'
    unless typeof event_type is 'string'
      return console.error "Module.fire_event: Event type must be a string,
                            #{event_type} (#{typeof event_type}) given."
    @container.trigger event_type, data ?= {}
    @


  # Hides this module.
  hide: ->
    @container.hide()
    @container.trigger 'hide'
    @


  # mixin = constructor of Draggable
  # self = Card
  @mixin: (mixin, p...) ->
    console.error("mixin not found") unless mixin
    @prototype.mixins or= []
    @prototype.mixins.push({mixin: mixin, params: p})


  # Shows this module.
  show: ->
    @container.show()
    @container.trigger 'show'
    @


  # Returns whether this module is currently visible.
  visible: ->
    @container.is ':visible'
