/**
 * @author gblomqui at redhat dot com
 */

var PrototypeExt = {
    Version: '1',
    prototypeVersion: parseFloat(Prototype.Version.split(".")[0] + "." + Prototype.Version.split(".")[1])
}

if((typeof Prototype=='undefined') || PrototypeExt.prototypeVersion < 1.5)
      throw("PrototypeExt requires the Prototype JavaScript framework >= 1.5");

/**
 * <p>
 * The Ajax MultiUpdater is able to update multiple elements from a single
 * Ajax request.  The format of the <TT>containers</TT> input param is
 * specific.
 * </p>
 * <p>
 * The <TT>containers</TT> parameter should be a JSON object that maps
 * each request parameter name to a single container.  For instance,
 * <pre>
 * containers = {
 *   reqParam1 : elementId1,
 *   reqParam2 : elementId2
 * }
 * </pre>
 * </p>
 * <p>
 * The Ajax.MultiUpdater uses a response mapper (see DefaultXmlResponseMapper) in
 * order to map the HTTP response to the elements specified in the containers hash.
 * The response mapper needs to have detailed knowledge of the format of the
 * response.  For instance, the DefaultXmlResponseMapper assumes a very specific
 * format of the xml in the response body.
 * </p>
 * <p>
 * The caller can pass in any response mapper (even a custom response mapper) in
 * the "responseMapper" option parameter.  This is similar to specifying an
 * "insertion" option.  See the documentation for the Ajax.Request to learn how
 * to pass options.  If no response mapper is provided, the default behavior is to
 * use the DefaultXmlResponseMapper.  See the DefaultXmlResponseMapper for details
 * regarding the output format.
 * </p>
 */
Ajax.MultiUpdater = Class.create();

Object.extend(Object.extend(Ajax.MultiUpdater.prototype, Ajax.Updater.prototype), {
  initialize: function(containers, url, options) {
    // only a single element id was provided as the container, so only update one thing
    if (typeof containers == 'string') {
        throw("Use Ajax.Updater when updating a single element");
    } else if (containers.success) {
        throw("Use Ajax.Updater when updating success and/or failure elements");
    } else {
        //  assume that containers is the properly formatted JSON object
        //  that we're looking for (for now...need to error check more here)
        this.containers = $H(containers);
    }

    this.transport = Ajax.getTransport();
    this.setOptions(options);
    //  default to the DefaultXmlResponseMapper
    if (!this.options.responseMapper) {
        this.options.responseMapper = new Ajax.MultiUpdater.DefaultXmlResponseMapper();
    }

    var onComplete = this.options.onComplete || Prototype.emptyFunction;
    this.options.onComplete = (function(transport, json) {
      var response = new Ajax.MultiUpdater.ResponseData(transport.responseXML, transport.responseText, json);
      this.updateContent(response);
      onComplete(transport, param);
    }).bind(this);

    this.request(url);
  },

  updateContent: function(ajaxResponse) {
    this.options.responseMapper.mapResponse(ajaxResponse, this.containers, this.options.insertion);
  }
});


/**
 * Encapsulates the data from an Ajax response.
 * There are three possible pieces to an Ajax
 * response:
 * <li>xml: an xml document containing the formatted response data</li>
 * <li>text: unformatted plain text containing</li>
 * <li>json: structured javascript response from the X-JSON response header</li>
 */
Ajax.MultiUpdater.ResponseData = Class.create();
Ajax.MultiUpdater.ResponseData.prototype = {
    initialize: function(xml, text, json) {
        this.xml = xml;
        this.text = text;
        this.json = json;
    },

    getXml: function() {
        return this.xml;
    },

    getText: function() {
        return this.text;
    },

    getJson: function() {
        return this.json;
    }
}

/**
 * Provides an interface for response mappers.  There is no real type
 * hierarchy that is followed by subclassing BaseResponseMapper.
 * It simply provides the prototype method of "mapResponse" that
 * should be overridden by subclasses.
 */
Ajax.MultiUpdater.BaseResponseMapper = function() {};
Ajax.MultiUpdater.BaseResponseMapper.prototype = {
  initialize: function() {

  },

  mapResponse: function(response, containers, insertion) {
    // base implementation doesn't do anything
    throw("Cannot call 'mapResponse' on BaseResponseMapper");
  }
}

/**
 * Assumes a format of:
 * <root>
 *   <response id="reqParam1">
 *      HTML DATA
 *   </response>
 *   <response id="reqParam2">
 *      HTML DATA
 *   </response>
 * </root>
 *
 * The HTML contents of <response id="reqParam1"> will be used to populate the
 * container mapped to reqParam1 in the containers hash.  The containers hash
 * is an attribute passed to the Ajax.MultiUpdater.
 */
Ajax.MultiUpdater.DefaultXmlResponseMapper = Class.create();
Ajax.MultiUpdater.DefaultXmlResponseMapper.prototype = Object.extend(new Ajax.MultiUpdater.BaseResponseMapper(), {
    responseElementToString: function(responseElement) {
        var div = document.createElement("div");
        for (var i = 0, length = responseElement.childNodes.length; i < length; ++i) {
            if (responseElement.childNodes[i].nodeType != 1) continue;
            div.appendChild(responseElement.childNodes[i].cloneNode(true));
        }
        return div.innerHTML;
    },

    mapResponse: function(response, containers, insertion) {
        var xmlRoot = response.getXml().documentElement;
        var xmlResponses = xmlRoot.getElementsByTagName("response");
        for (var i = 0, length = xmlResponses.length; i < length; ++i) {
            var xmlResponse = xmlResponses[i];
            var id = xmlResponse.getAttribute("id");
            var receiver = containers[id];
            if (receiver = $(receiver)) {
                if (insertion)
                    new insertion(receiver, this.responseElementToString(xmlResponse));
                else
                    receiver.update(this.responseElementToString(xmlResponse));
            }
        }
    }
});


/**
 * There is currently no default implementation for the JsonResponseMapper.
 * Presumably, this mapper would take a set of properties that match the
 * request parameters and use the values to populate the containers.
 */
Ajax.MultiUpdater.DefaultJsonResponseMapper = Class.create();
Ajax.MultiUpdater.DefaultJsonResponseMapper.prototype = Object.extend(new Ajax.MultiUpdater.BaseResponseMapper(), {
    mapResponse: function(response, containers, insertion) {

    }
});