/**
 * Routes.js provides a single function that allows
 *           mapping from subpage transitions
 *           onto Appcelerator Messages.
 *
 *
 *
 * A typical usage looks like the following:
 *
 * routes({
 *   '#home' :                  'l:show.home',
 *   '#kittens' :               'l:show.kittens',
 *   '#puppies' :               'l:show.puppies',
 *   '#kitten/:id' :            'l:show.animal[type=cat,id=#{id}]',
 *   '#puppy/:id' :             'l:show.animal[type=dog,id=#{id}]',
 *   '#kitten/:id/friends' :    'l:show.friends[type=cat,id=#{id}]',
 *   '#puppy/:id/friends' :     'l:show.friends[type=dog,id=#{id}]',
 *   '#yaps/:fromid/:toid' :    'l:show.messages[dogid=#{fromid},catid=#{toid}]',
 *   '#hisses/:fromid/:toid' :  'l:show.messages[catid=#{fromid},dogid=#{toid}]',
 * });
 *
 * Using this routes definition,
 * the app author can create links to subpages,
 * and respond to these link clicks based on the messages on the right-hand-side.
 *
 * For example, this link:
 *   <a href="#kitten/312/friends">Whisker's Friends</a>
 * will send the message:
 *   l:show.friends[type=cat,id=312]
 *
 * Like other Appcelerator history change listeners,
 * the messages connected by routes will be fired when the user
 *  1) clicks a link
 *  2) navigates forward and backward
 *  3) follows a link from another page which includes the hash
 *
 * Syntax is borrowed from Ruby on Rails routing:
 *   http://manuals.rubyonrails.com/read/chapter/65
 * and from Appcelerator string interpolation/templates:
 *   http://doc.appcelerator.org/reference/interpolation/index.html
 *
 * Version 0.1, by Mark Luffel
 * Released under GPL version 3
 */
function routes(mapping) {
  
  var routing = [];
  var patterns = {}; // for error reporting
  for(var pattern in mapping) {
    
    var rightHandSide = mapping[pattern];
    var rhsType = typeof rightHandSide;
    
    if(rhsType == 'string') {
      var callback = routes.makeMessageSendCallback(rightHandSide);
    } else if(rhsType == 'function') {
      var callback = rightHandSide;
    } else {
      $E('Right-hand-side of route must be a string or callback function, not "'+rhsType+'"');
      continue;
    }
    
    var pattern = pattern.replace(/^#/,'');
    
    var patternParts = ['^'];
    var patternVars = [];
    var match;
    var varOrLit = /(:([a-zA-Z0-9_]+))|([^:]+)/g;
    while ((match = varOrLit.exec(pattern)) != null) {
      if(match[2]) {
        patternVars.push(match[2]);
        patternParts.push('(.*?)');
      } else if(match[3]) {
        patternParts.push(RegExp.escape(match[3]));
      }
    }
    patternParts.push('$');
    var regexedPattern = patternParts.join('');
    
    if(patterns[regexedPattern]) {
      $E('Two conflicting routing patterns "'+pattern+'" and "'+patterns[regexedPattern]+'"');
    }
    patterns[regexedPattern] = pattern;
    
    routing.push({
      pattern: new RegExp(regexedPattern),
      vars: patternVars,
      callback: callback
    });
  }
  
  Appcelerator.History.onChange(function(newstate,data) {
    if(newstate == null) {
      newstate = '#';
    }
    
    for(var i = 0; i < routing.length; i++) {
      var route = routing[i];
      var match = newstate.match(route.pattern);
      
      if(match) {
        var vars = route.vars;
        if(match.length < vars.length+1) {
          $E('internal routing error: too few arguments matched in route'); return;
        }
        
        var templateScope = {};
        for(var j = 0; j < vars.length; j++) {
          templateScope[vars[j]] = match[j+1];
        }
        
        route.callback(templateScope);
        return; // only the first match
      }
    }
  });
}

routes.makeMessageSendCallback = function(messageTemplateText) {
  var template = Appcelerator.Compiler.compileTemplate(messageTemplateText);
  return function(variables) {
    var msg = template(variables);
    $MQX(msg);
  };
}

/**
 * An extended form for $MQ, that accepts parameters in the standard on expression format
 * Ex:
 *  $MQX('l:foo.request[cats=345,pigs=345]')
 *
 * This is a convenience method for code that wants to provide an
 * mini-language like the onexpressions, but without the full syntax.
 */
function $MQX(messageAndPayload) {
  var extendedMessage = /([^[]+)(\[(.*?)\])?/;
  var parts = extendedMessage.exec(messageAndPayload);
  var type = parts[1];
  var payload = parts[3];
  
  var data = Appcelerator.Compiler.getParameters(payload,true);
  Appcelerator.Util.ServiceBroker.queue({
    type: type,
    data: data,
    version: '1.0'
  });
};

