 # Author:
 #   smashwilson
 
-# TODO: docs
-
-Util = require 'util'
 Url = require 'url'
 Redis = require 'redis'
 
+# A markov model backed by a configurably storage engine that can both learn and
+# generate random text.
 class MarkovModel
+
+  # Chain termination marker; chosen because _words will never contain whitespace.
   sentinel = ' '
 
+  # Build a new model with the provided storage backend and order. A markov model's
+  # order is the number of prior states that will be examined to determine the
+  # probabilities of the next state.
   constructor: (@storage, @ply) ->
 
+  # Split a line of text into whitespace-separated, nonempty words.
   _words: (phrase) ->
     (word for word in phrase.split /\s+/ when word.length > 0)
 
+  # Generate a uniformly distributed random number between 0 and max, inclusive.
   _random: (max) ->
     Math.floor(Math.random() * (max + 1))
 
+  # Given an object with possible choices as keys and relative frequencies as values,
+  # choose a key with probability proportional to its frequency.
   _chooseWeighted: (choices) ->
     return sentinel unless choices
 
+    # Sum the frequencies of the available choices and choose a value within that
+    # range.
     total = 0
     total += parseInt(freq) for key, freq of choices
     chosen = @._random(total)
 
+    # Accumulate frequencies as you iterate through the choices. Select the key that
+    # contains the "chunk" including the chosen value.
     acc = 0
     for key, freq of choices
       acc += parseInt(freq)
       return key if chosen <= acc
 
-    throw "Bad choice: #{chosen}"
+    # If we get here, "chosen" was greater than total.
+    throw "Bad choice: #{chosen} from #{total}"
 
+  # Generate each state transition of order @ply among the words of "phrase". For
+  # example, with @ply 2 and a phrase "a b c d", this would generate:
+  #
+  # { from: [null, null] to: 'a' }
+  # { from: [null, 'a'] to: 'b' }
+  # { from: ['a', 'b'], to: 'c' }
+  # { from: ['b', 'c'], to: 'd' }
+  # { from: ['c', 'd'], to: ' ' }
   _transitions: (phrase) ->
     words = @._words(phrase)
     words.unshift null for i in [1..@ply]


module.exports = (robot) ->

  info = Url.parse process.env.REDISTOGO_URL or
    process.env.REDISCLOUD_URL or
    process.env.BOXEN_REDIS_URL or


        @._generate_more(key, chain, max - 1, callback)

# Markov storage implementation that uses redis hash keys to store the model.
class RedisMarkovStorage

  # Prefix used to isolate stored markov transitions from other keys in the database.
  keyprefix = "markov:"

  # Create a storage module that uses the provided Redis connection.
  constructor: (@client) ->

  # Uniformly and unambiguously convert an array of Strings and nulls into a valid
  # Redis key. Uses a length-prefixed encoding.
  #
  # _encode([null, null, "a"]) = "markov:001a"
  # _encode(["a", "bb", "ccc"]) = "markov:1a2b3c"
  _encode: (key) ->
    encoded = for part in key
      if part then "#{part.length}#{part}" else "0"
    keyprefix + encoded.join('')

  # Record a transition within the model. "transition.from" is an array of Strings and
  # nulls marking the prior state and "transition.to" is the observed next state, which
  # may be an end-of-chain sentinel.
  increment: (transition) ->
    @client.hincrby(@._encode(transition.from), transition.to, 1)

  # Retrieve an object containing the possible next hops from a prior state and their
  # relative frequencies. Invokes "callback" with the object.
  get: (prior, callback) ->
    @client.hgetall @._encode(prior), (err, hash) ->
      throw err if err
