lm.utils = function() {
  var wait = function(time, f) {
    setTimeout(f, time);
  };

  var assert = function(bool, message) {
    if(!bool) {
        if(message) {
            throw message;
        } else {
            throw 'Assertion failed';
        }
    }
  };

  return {
    wait : wait,
    assert : assert,
    flush : function(f) { wait(1, f); }
  };
}();

lm.data = function() {
  var add_items_to_set = function(set, items) {
    for(var i=0;i<items.length;i++) {
      set[items[i]] = true;
    }
  };
  var remove_item_from_set = function(set, item) {
    set[item] = false;
  };
  var get_items_in_set = function(set, sort_function) {
    var keys = [];
    for(var key in set) {
      if(set[key]) {
        keys = keys.concat([key]);
      }
    }
    if(sort_function) {
      return keys.sort(sort_function);
    } else {
      return keys;
    }
  };

  return {
    add_items_to_set : add_items_to_set,
    get_items_in_set : get_items_in_set,
    remove_item_from_set : remove_item_from_set
  };
}();

lm.parser = function() {
  var change_rules = [];
  var colon_splitter_regex = new RegExp(/^(.+?)(:(.*))?$/);
  var suffix_id_regex = new RegExp(/^.+_(\d+)$/);

  var split_on_first_colon = function(s) {
    var a1 = s.replace(colon_splitter_regex, '$1');
    var a2 = s.replace(colon_splitter_regex, '$3');
    return [a1, a2];
  };

  var suffix_id = function(s) {
    var id = s.replace(suffix_id_regex, '$1');
    if(id.length > 0) {
        return id;
    } else {
        throw('String does not have id suffix: ' + s);
    }
  };
  
  return {
    split_on_first_colon : split_on_first_colon,
    suffix_id : suffix_id
  };
}();


lm.ajax = function() {
  var generate_replace_and_show = function(target) {
    var replace_and_show = function(response) {
      target.html(response);
      target.show();
    };
    return replace_and_show;
  };

  var kwargs_of_action = function(button) {
    inputs = button.children('.kwargs').children('input');
    var kwargs = {};
    for(var i=0;i<inputs.length;i++) {
      var name = inputs.eq(i).attr('name');
      var val = inputs.eq(i).val();
      kwargs[name] = val;
    }
    return kwargs;
  };

  var target_from_kwargs = function(button, kwargs) {
    var target = null;
    if(!kwargs['target-type']) {
        kwargs['target-type'] == 'absolute';
    }
    if(kwargs['target-type'] == 'absolute') {
        target = jQuery(kwargs['target']);
    } else if(kwargs['target-type'] == 'parents') {
        target = button.parents(kwargs['target']);
    } else if(kwargs['target-type'] == 'child-of') {
        var closest = button.closest(kwargs['closest']);
        target = closest.find(kwargs['target']);
    } else if(kwargs['target-type'] == 'closest') {
        target = button.closest(kwargs['target']);
    } else if(kwargs['target-type'] == 'sibling') {
        target = button.siblings(kwargs['target']);
    } else {
        throw 'No known target type: ' + kwargs['target-type'];
    }
    if(target.length == 0) {
        throw 'No target found for selector: ' + kwargs['target-type'] + kwargs['target'];
    } else if(target.length > 1) {
        throw 'Too many targets found for selector: ' + kwargs['target-type'] + kwargs['target'];
    } else {
        return target;
    }
  };

  var act_on_opener_closer = function(button, kwargs) {
    var target = target_from_kwargs(button, kwargs);
    if(target.css('display') != 'none') {
        button.children('.when-closed').show();
        button.children('.when-open').hide();
        target.hide();
    } else {
        var url = kwargs['url'];
        button.children('.when-closed').hide();
        button.children('.when-open').show();
        if(target.html() != "" || !url) {
            target.show();
        } else {
            target.html('<img src="/images/spinner_mac.gif" />');
            target.show();
            lm.utils.flush(function() {
                jQuery.ajax({url: url,
                    success: generate_replace_and_show(target) });
            });
        }
    }
    return false;
  };

  var act_on_form_submitter = function(button, kwargs) {
    var target = target_from_kwargs(button, kwargs);
    if(kwargs['form']) {
        var form = jQuery(kwargs['form']);
    } else {
        var form = button.children('form');
    }
    lm.utils.assert(form.length == 1, 'No form found in button');
    var method = form.attr('method');
    var url = form.attr('action');
    lm.utils.assert(url !== '', 'Form submit URL is not blank');
    var data = form.formSerialize();
    lm.utils.assert(data !== '', 'Form should send some data');
    button.html('<img src="/images/spinner_mac.gif" />');
    jQuery.ajax({url: url,
                 method: method,
                 data: data,
                 success: generate_replace_and_show(target) });
    return false;
  };

  var action = function(e) {
    var button = jQuery(this);
    var kwargs = kwargs_of_action(button);
    /* #TODO: jperla: better error handling */
    if(kwargs['type'] == 'opener-closer') {
        return act_on_opener_closer(button, kwargs);
    } else if(kwargs['type'] == 'form-submitter') {
        return act_on_form_submitter(button, kwargs);
    } else {
        throw 'No known kwargs type';
    }
  };

  var always_replace_target_with_url = function(e) {
    var button = jQuery(this);
    var target_selector = button.children("input[name='target']").eq(0).val();
    var target = jQuery(target_selector);
    lm.utils.assert(target.length > 0, 'Did not find a target with selector: ' + target_selector);
    var url = button.children("input[name='url']").eq(0).val();
    target.hide();
    lm.utils.flush(function() {
        jQuery.ajax({url: url,
            success: generate_replace_and_show(target) });
    });
    return false;
  };

  return {
    always_replace_target_with_url: always_replace_target_with_url,
    action: action
  };
}();


jQuery(function() {
  jQuery('.always-replace-target-with-url').live('click', lm.ajax.always_replace_target_with_url);
  jQuery('.actionable').live('click', lm.ajax.action);
});

