# The main `Bot` class that all robots are created from, this contains the
# main functions for assembling a bot.
{EventEmitter} = require 'events'
request = require 'request'
fs = require 'fs'

# Get the current version from `package.json`.
exports.version = JSON.parse(fs.readFileSync(__dirname + "/../package.json")).version

# Factory method for creating a new `Bot` instance. Accepts a name
# argument, which is the name given to the bot.
#
#     var bots = require('bots');
#
#     var coolbot = bots.createBot('coolbot 1.0.0');
#
exports.createBot = (name) ->
  new Bot name

# The `Bot` class, inherits from `EventEmitter` so it can notify plugins of
# certain lifecycle events.
exports.Bot = class Bot extends EventEmitter

  # Creates a new `Bot` with the given name. Sets up the bot ready to be
  # configured.
  constructor: (@name) ->
    @handlers = []
    @interfaces = []
    @descriptions = {}

  setup: (nickname, callback) ->
    if typeof(nickname) is 'function'
      callback = nickname
    else
      @nickname = nickname

    callback @desc, @hear

  # Adds a description of a piece of the robots functionality, this is used
  # for the robots builtin `help` phrase.
  #
  # * **phrase** - The String representing the phase the bot recognises.
  # * **functionality** - The String describing the phrase (optional).
  #
  # #### Example
  #
  #     bot.desc('image me THING', 'Get a random image of THING');
  #
  desc: (phrase, functionality) =>
    @descriptions[phrase] = functionality

  # Add a `pattern` to the robots repotoire. This is matched against incoming
  # messages and if they match then the message is passed to `callback`.
  #
  # * **pattern** - The RegEx to match against the message body.
  # * **callback** - The Function to be invoked when the pattern is matched.
  #
  # #### Example
  #
  #     bot.hear(/ping/, function(message) {
  #       message.say('PONG');
  #     });
  #
  hear: (pattern, callback) =>
    @handlers.push [pattern, callback]

  # Add an interface to the robot. This is allows the 'bot to communicate
  # with the outside world. See the `Cli` and `XMPP` interfaces for examples,
  # the interface should inherit from EventEmitter, and emit a `message`
  # event when there is a new message on the interface.
  #
  #     var bots = require('bots');
  #     var coolbot = bots.createBot('coolbot 1.0.0');
  #     coolbot.use(bots.cli());
  #
  use: (interface) ->
    @interfaces.push interface
    interface.on 'message', @dispatch

  # Dispatches an incoming message to any handlers that match the message
  # body. This can be used to fake message to the bot, useful for testing
  # the bot. Takes a `message` object with `body` and `say` properties.
  #
  #     coolbot.dispatch({
  #       message: 'ping',
  #       say: function(text, callback) {
  #         console.log(text);
  #         callback();
  #       }
  #     });
  #
  dispatch: (message) =>
    for pair in @handlers
      if @nickname
        continue unless message.body.match(new RegExp("^#{@nickname}"))
      [ pattern, handler ] = pair
      handler.call(@, message) if message.match = message.body.match(pattern)

  # Start the bot up, calls listen on the registered interfaces. This registers
  # the **help** phrase that the bot will always repond to. Emits a `start`
  # event when setup is complete.
  #
  #     coolbot.on('start', function() {
  #       console.log("coolbot started");
  #     });
  #     coolbot.start();
  #
  start: ->
    interface.listen() for interface in @interfaces
    @hear /help/, @help
    @emit 'start'

  stop: (finished) ->
    console.log "\nStopping #{@name}"
    closing = @interfaces.length
    for interface in @interfaces
      interface.close ->
        finished() if --closing is 0

  # Reset the bot's `handlers` and `descriptions`, note this does not stop
  # the registered interfaces from listening.
  reset: (callback) ->
    @handlers = []
    @descriptions = []
    callback?()

  # Helper method for making a `GET` request, proxies to the `request`
  # method.
  get: (uri, body, callback) ->
    @request('GET', uri, body, callback)

  # Helper method for making a `POST` request, proxies to the `request`
  # method.
  post: (uri, body, callback) ->
    @request('POST', uri, body, callback)

  # Handler for the default help action, gathers all of the registered
  # descriptions and sends a message describing each action.
  help: (message) ->
    if Object.keys(@descriptions).length is 0
      return message.say "I do not have any actions yet."
    message.say "I listen for the following…", =>
      for phrase, functionality of @descriptions
        if functionality
          output =  phrase + ": " + functionality
        else
          output = phrase
        message.say output

  # Helper to make http requests, tries to automatically handle JSON input and
  # output.
  request: (method, uri, body, callback) ->
    options = { method: method, uri: uri }

    options.headers = { 'User-Agent': @name }

    if typeof(body) is 'function' and not callback
      callback = body
      body = null

    if typeof body is 'string'
      options.body = body
    else
      options.json = body

    request options, (err, response, body) ->
      try
        body = JSON.parse body
      catch e
        # Ignore and pass through the raw body.

      callback? body, response

# Command line interface.
exports.cli = ->
  Cli = require './interfaces/cli'
  new Cli

# Campfire interface.
exports.campfire = (args...) ->
  Campfire = require './interfaces/campfire'
  new Campfire args...

# XMPP interface.
exports.xmpp = (args...) ->
  Xmpp = require './interfaces/xmpp'
  new Xmpp args...

exports.generate = require './generator'
