

/*global opensocial */

/**
 * @fileoverview Object used to request social information from the container.
 * This includes data for friends, profiles, app data, and activities.
 *
 * All apps that require access to people information should send a dataRequest
 * in order to receieve a dataResponse
 */


/**
 * @class
 * <p>
 * Used to request social information from the container.
 * This includes data for friends, profiles, app data, and activities.
 * All apps that require access to people information
 * should send a DataRequest.
 * </p>
 *
 * <p>
 * Here's an example of creating, initializing, sending, and handling
 * the results of a data request:
 * </p>
 *
 * <pre>function requestMe() {
  var req = opensocial.newDataRequest();
  req.add(req.newFetchPersonRequest(
            opensocial.DataRequest.PersonId.VIEWER),
          "viewer");
  req.send(handleRequestMe);
};

function handleRequestMe(data) {
  var viewer = data.get("viewer");
  if (viewer.hadError()) {
    <em>//Handle error using viewer.getError()...</em>
    return;
  }

  <em>//No error. Do something with viewer.getData()...</em>
}
</pre>
 * <p>
 * <b>See also:</b>
 * <a href="opensocial.html#newDataRequest"><code>
 * opensocial.newDataRequest()</code></a>
 * </p>
 *
 * @name opensocial.DataRequest
 */

/**
 * Do not create a DataRequest directly, instead use
 * opensocial.newDataRequest();
 *
 * @private
 * @constructor
 */
opensocial.DataRequest = function() {
  this.requestObjects_ = [];
};


/**
 * {Array.<{key: string, request:Object}>}
 *    requestObjects An array of
 *    data requests that the container should fetch data for
 * @private
 */
opensocial.DataRequest.prototype.requestObjects_ = null;


/**
 * Get the requested objects
 *
 * @return {Array.<{key:string, request:Object}>}
 *    requestObjects An array of data requests that the container should fetch
 *    data for
 * @private
 */
opensocial.DataRequest.prototype.getRequestObjects = function() {
  return this.requestObjects_;
};


/**
 * Adds an item to fetch (get) or update (set) data from the server.
 * A single DataRequest object can have multiple items.
 * As a rule, each item is executed in the order it was added,
 * starting with the item that was added first.
 * However, items that can't collide might be executed in parallel.
 *
 * @param {Object} request Specifies which data to fetch or update.
 * @param {string} opt_key A key to map the generated response data to.
 */
opensocial.DataRequest.prototype.add = function(request, opt_key) {
  return this.requestObjects_.push({'key': opt_key, 'request': request});
};


/**
 * Sends a data request to the server in order to get a data response.
 * Although the server may optimize these requests,
 * they will always be executed
 * as though they were serial.
 *
 * @param {function(opensocial.ResponseItem)=} opt_callback The function to call with the
 *   <a href="opensocial.DataResponse.html">data response</a>
 *    generated by the server.
 */
opensocial.DataRequest.prototype.send = function(opt_callback) {
  var callback = opt_callback || function() {};
  opensocial.Container.get().requestData(this, callback);
};


/**
 * @static
 * @class
 * The sort orders available for ordering person objects.
 *
 * @name opensocial.DataRequest.SortOrder
 */
opensocial.DataRequest.SortOrder = {
  /**
   * When used will sort people by the container's definition of top friends.
   * @member opensocial.DataRequest.SortOrder
   */
  TOP_FRIENDS: 'topFriends',
  /**
   * When used will sort people alphabetically by the name field.
   *
   * @member opensocial.DataRequest.SortOrder
   */
  NAME: 'name'
};


/**
 * @static
 * @class
 * The filters available for limiting person requests.
 *
 * @name opensocial.DataRequest.FilterType
 */
opensocial.DataRequest.FilterType = {
  /**
   * Retrieves all friends.
   *
   * @member opensocial.DataRequest.FilterType
   */
  ALL: 'all',
  /**
   * Retrieves all friends with any data for this application.
   *
   * @member opensocial.DataRequest.FilterType
   */
  HAS_APP: 'hasApp',
  /**
   * Retrieves only the user's top friends.
   *
   * @member opensocial.DataRequest.FilterType
   */
  TOP_FRIENDS: 'topFriends',
  /**
   * Will filter the people requested by checking if they are friends with
   * the given <a href="opensocial.IdSpec.html">idSpec</a>. Expects a
   *    filterOptions parameter to be passed with the following fields defined:
   *  - idSpec The <a href="opensocial.IdSpec.html">idSpec</a> that each person
   *        must be friends with.
  */
  IS_FRIENDS_WITH: 'isFriendsWith'
};


/**
 * @static
 * @class
 * @name opensocial.DataRequest.PeopleRequestFields
 * @enum {string}
 */
opensocial.DataRequest.PeopleRequestFields = {
  /**
   * An array of
   * <a href="opensocial.Person.Field.html">
   * <code>opensocial.Person.Field</code></a>
   * specifying what profile data to fetch
   * for each of the person objects. The server will always include
   * ID, NAME, and THUMBNAIL_URL.
   *
   * @member opensocial.DataRequest.PeopleRequestFields
   */
  PROFILE_DETAILS: 'profileDetail',

  /**
   * A sort order for the people objects; defaults to TOP_FRIENDS.
   * Possible values are defined by
   * <a href="opensocial.DataRequest.SortOrder.html">SortOrder</a>.
   *
   * @member opensocial.DataRequest.PeopleRequestFields
   */
  SORT_ORDER: 'sortOrder',

  /**
   * How to filter the people objects; defaults to ALL.
   * Possible values are defined by
   * <a href="opensocial.DataRequest.FilterType.html">FilterType</a>.
   *
   * @member opensocial.DataRequest.PeopleRequestFields
   */
  FILTER: 'filter',

  /**
   * Additional options to be passed into the filter,
   * specified as a Map&lt;String, Object>.
   *
   * @member opensocial.DataRequest.PeopleRequestFields
   */
  FILTER_OPTIONS: 'filterOptions',

  /**
   * When paginating, the index of the first item to fetch.
   * Specified as a <code>Number</code>.
   *
   * @member opensocial.DataRequest.PeopleRequestFields
   */
  FIRST: 'first',

  /**
   * The maximum number of items to fetch; defaults to 20. If set to a larger
   * number, a container may honor the request, or may limit the number to a
   * container-specified limit of at least 20.
   * Specified as a <code>Number</code>.
   *
   * @member opensocial.DataRequest.PeopleRequestFields
   */
  MAX: 'max',

  /**
   * A string or array of strings, specifying the app data keys to fetch for
   * each of the Person objects. This field may be used interchangeably with
   * the string 'appData'.  Pass the string '*' to fetch all app data keys.
   * @member opensocial.DataRequest.PeopleRequestFields
   */
  APP_DATA: 'appData',

  /**
   * How to escape app data returned from the server;
   * defaults to HTML_ESCAPE. Possible values are defined by
   * <a href="opensocial.EscapeType.html">EscapeType</a>.
   * This field may be used interchangeably with the string 'escapeType'.
   * @member opensocial.DataRequest.PeopleRequestFields
   */
  ESCAPE_TYPE: 'escapeType'
};


/**
 * If the named param does not exist sets it to the default value.
 *
 * @param {Object.<string,Object>} params Parameter map.
 * @param {string} name of the param to check.
 * @param {*} defaultValue The value to set if the param does not exist.
 * @private
 */
opensocial.DataRequest.prototype.addDefaultParam = function(params, name,
    defaultValue) {
  params[name] = params[name] || defaultValue;
};


/**
 * Adds the default profile fields to the desired array.
 *
 * @param {Object} params Parameter map.
 * @private
 */
opensocial.DataRequest.prototype.addDefaultProfileFields = function(params) {
  var fields = opensocial.DataRequest.PeopleRequestFields;
  var profileFields = params[fields.PROFILE_DETAILS] || [];
  params[fields.PROFILE_DETAILS] = profileFields.concat(
      [opensocial.Person.Field.ID, opensocial.Person.Field.NAME,
       opensocial.Person.Field.THUMBNAIL_URL]);
};


/**
 * Returns the keys object as an array.
 *
 * @param {Object} keys
 * @private
 */
opensocial.DataRequest.prototype.asArray = function(keys) {
  if (opensocial.Container.isArray(keys)) {
    return keys;
  } else {
    return [keys];
  }
};


/**
 * Creates a new album and returns the ID of the album created.
 *
 * @param {opensocial.IdSpec} idSpec The ID of the used to specify which people/groups
 *   to create an album for.
 * @param {opensocial.Album} album The album to create.
 * @return {Object} A request object.
 */
opensocial.DataRequest.prototype.newCreateAlbumRequest = function(idSpec, album) {
  return opensocial.Container.get().newCreateAlbumRequest(idSpec, album);
};

/**
 * Creates a new media item in the album and returns the ID of the album created.
 *
 * @param {opensocial.IdSpec} idSpec The ID of the used to specify which people/groups
 *   to create an album for.
 * @param {string} albumId The ID of album to add the media item to.
 * @param {openSocial.MediaItem} mediaItem The MediaItem to add to the album.
 * @return {Object} A request object.
 */
opensocial.DataRequest.prototype.newCreateMediaItemRequest = function(idSpec, albumId,
    mediaItem) {
  return opensocial.Container.get().newCreateMediaItemRequest(idSpec, albumId, mediaItem);
};

/**
 * Deletes the album specified.
 *
 * @param {opensocial.IdSpec} idSpec The ID of the used to specify which people/groups
 *   to create an album for.
 * @param {string} albumId The ID of the album to create.
 * @return {Object} A request object.
 */
opensocial.DataRequest.prototype.newDeleteAlbumRequest = function(idSpec, albumId) {
  return opensocial.Container.get().newDeleteAlbumRequest(idSpec, albumId);
};

/**
 * Creates an item to request a profile for the specified person ID.
 * When processed, returns a
 * <a href="opensocial.Person.html"><code>Person</code></a> object.
 *
 * @param {string} id The ID of the person to fetch; can be the standard
 *    <a href="opensocial.IdSpec.PersonId.html">person ID</a>
 *    of VIEWER or OWNER.
 * @param {Object.<opensocial.DataRequest.PeopleRequestFields, Object>=} opt_params
 *    Additional
 *    <a href="opensocial.DataRequest.PeopleRequestFields.html">parameters</a>
 *    to pass to the request; this request supports PROFILE_DETAILS.
 * @return {Object} A request object.
 */
opensocial.DataRequest.prototype.newFetchPersonRequest = function(id,
    opt_params) {
  opt_params = opt_params || {};
  this.addDefaultProfileFields(opt_params);

  return opensocial.Container.get().newFetchPersonRequest(id, opt_params);
};


/**
 * Creates an item to request friends from the server.
 * When processed, returns a <a href="opensocial.Collection.html">Collection</a>
 * &lt;<a href="opensocial.Person.html">Person</a>&gt; object.
 *
 * @param {opensocial.IdSpec} idSpec An IdSpec used to specify
 *    which people to fetch. See also <a href="opensocial.IdSpec.html">IdSpec</a>.
 * @param {Object.<opensocial.DataRequest.PeopleRequestFields, Object>=} opt_params
 *    Additional
 *    <a href="opensocial.DataRequest.PeopleRequestFields.html">params</a>
 *    to pass to the request.
 * @return {Object} A request object.
 */
opensocial.DataRequest.prototype.newFetchPeopleRequest = function(idSpec,
    opt_params) {
  opt_params = opt_params || {};
  var fields = opensocial.DataRequest.PeopleRequestFields;

  this.addDefaultProfileFields(opt_params);

  this.addDefaultParam(opt_params, fields.SORT_ORDER,
      opensocial.DataRequest.SortOrder.TOP_FRIENDS);

  this.addDefaultParam(opt_params, fields.FILTER,
      opensocial.DataRequest.FilterType.ALL);

  this.addDefaultParam(opt_params, fields.FIRST, 0);

  this.addDefaultParam(opt_params, fields.MAX, 20);

  return opensocial.Container.get().newFetchPeopleRequest(idSpec, opt_params);
};


/**
 * @static
 * @class
 * @name opensocial.DataRequest.AlbumRequestFields
 * @enum {string}
 */
opensocial.DataRequest.AlbumRequestFields = {
  /**
   * When paginating, the index of the first item to fetch.
   * Specified as a <code>Number</code>.
   *
   * @member opensocial.DataRequest.AlbumRequestFields
   */
  FIRST: 'first',

  /**
   * The maximum number of items to fetch; defaults to 20.
   * Specified as a <code>Number</code>.
   *
   * @member opensocial.DataRequest.AlbumRequestFields
   */
  MAX: 'max'
};

/**
 * @static
 * @class
 * @name opensocial.DataRequest.MediaItemRequestFields
 * @enum {string}
 */
opensocial.DataRequest.MediaItemRequestFields = {
  /**
   * When paginating, the index of the first item to fetch.
   * Specified as a <code>Number</code>.
   *
   * @member opensocial.DataRequest.MediaItemRequestFields
   */
  FIRST : 'first',

  /**
   * The maximum number of items to fetch; defaults to 20.
   * Specified as a <code>Number</code>.
   *
   * @member opensocial.DataRequest.MediaItemRequestFields
   */
  MAX : 'max'
};

/**
 * @static
 * @class
 * @name opensocial.DataRequest.DataRequestFields
 * @enum {string}
 */
opensocial.DataRequest.DataRequestFields = {
  /**
   * How to escape person data returned from the server; defaults to HTML_ESCAPE.
   * Possible values are defined by
   * <a href="opensocial.EscapeType.html">EscapeType</a>.
   * Use of this function is deprecated in favor of using the
   * <a href="#opensocial.DataRequest.PeopleRequestFields.ESCAPE_TYPE">
   * ESCAPE_TYPE</a> request field.
   *
   * @deprecated
   * @member opensocial.DataRequest.DataRequestFields
   */
  ESCAPE_TYPE: 'escapeType'
};


/**
 * Creates an item to request app data for the given people.
 * When processed, returns a Map&lt;
 * <a href="opensocial.DataRequest.PersonId.html">PersonId</a>,
 * Map&lt;String, String&gt;&gt; object.
 * All of the data values returned will be valid json.
 * Use of this function is deprecated in favor of using the
 * <a href="#opensocial.DataRequest.PeopleRequestFields.APP_DATA">APP_DATA</a>
 * request field.
 *
 * @deprecated
 * @param {opensocial.IdSpec} idSpec An IdSpec used to specify which people to
 *     fetch. See also <a href="opensocial.IdSpec.html">IdSpec</a>.
 * @param {Array.<string> | string} keys The keys you want data for; this
 *     can be an array of key names, a single key name, or "*" to mean
 *     "all keys".
 * @param {Object.<opensocial.DataRequest.DataRequestFields, Object>=} opt_params
 *    Additional
 *    <a href="opensocial.DataRequest.DataRequestFields.html">params</a>
 *    to pass to the request.
 * @return {Object} A request object.
 */
opensocial.DataRequest.prototype.newFetchPersonAppDataRequest = function(idSpec,
    keys, opt_params) {
  return opensocial.Container.get().newFetchPersonAppDataRequest(idSpec,
      this.asArray(keys), opt_params);
};

/**
 * Updates the fields for an album specified in the params.
 * The following fields cannot be set: MEDIA_ITEM_COUNT, OWNER_ID, ID.
 *
 * @param {opensocial.IdSpec} idSpec An IdSpec used to specify which people/groups
 *    to own the album.
 * @param {string} albumId The ID of album to update.
 * @param {Object.<opensocial.Album.Field, Object>=} opt_params The Album Fields to update.
 * @return {Object} A request object.
 */
opensocial.DataRequest.prototype.newUpdateAlbumRequest = function(idSpec, albumId,
    opt_params) {
  return opensocial.Container.get().newUpdateAlbumRequest(idSpec, albumId, opt_params);
};

/**
 * Updates the fields for a media item specified in the params.
 * The following fields cannot be set: ID, CREATED, ALBUM_ID, FILE_SIZE, NUM_COMMENTS.
 *
 * @param {opensocial.IdSpec} idSpec An IdSpec used to specify which people/groups
 *    own the album/media item.
 * @param {string} albumId The ID of the album containing the media item to update.
 * @param {string} mediaItemId ID of media item to update.
 * @param {Object.<opensocial.MediaItem.Field, Object>=} opt_params The Album Fields to update.
 * @return {Object} A request object.
 */
opensocial.DataRequest.prototype.newUpdateMediaItemRequest = function(idSpec, albumId,
    mediaItemId, opt_params) {
  return opensocial.Container.get().newUpdateMediaItemRequest(idSpec, albumId,
      mediaItemId, opt_params);
};

/**
 * Creates an item to request an update of an app field for the current VIEWER
 * When processed, does not return any data.
 * App Data is stored as a series of key value pairs of strings, scoped per
 * person, per application. Containers supporting this request SHOULD provide
 * at least 10KB of space per user per application for this data.
 * @param {string} key The name of the key. This may only contain alphanumeric
 *     (A-Za-z0-9) characters, underscore(_), dot(.) or dash(-).
 * @param {string} value The value, must be valid json.
 * @return {Object} A request object.
 */
opensocial.DataRequest.prototype.newUpdatePersonAppDataRequest = function(
    key, value) {
  return opensocial.Container.get().newUpdatePersonAppDataRequest(key,
      value);
};


/**
 * Deletes the given keys from the datastore for the current VIEWER.
 * When processed, does not return any data.
 *
 * @param {Array.<string> | string} keys The keys you want to delete from
 *     the datastore; this can be an array of key names, a single key name,
 *     or "*" to mean "all keys".
 * @return {Object} A request object.
 */
opensocial.DataRequest.prototype.newRemovePersonAppDataRequest = function(keys) {
  return opensocial.Container.get().newRemovePersonAppDataRequest(keys);
};


/**
 * @static
 * @class
 * Used by
 * <a href="opensocial.DataRequest.html#newFetchActivitiesRequest">
 * <code>DataRequest.newFetchActivitiesRequest()</code></a>.
 * @name opensocial.DataRequest.ActivityRequestFields
 * @private
 * @enum {string}
 */
opensocial.DataRequest.ActivityRequestFields = {
  /**
   * If provided will filter all activities by this app Id.
   * @private - at the moment you can only request activities for your own app
   */
  APP_ID: 'appId',
  /**
   * When paginating, the index of the first item to fetch.
   * Specified as a <code>Number</code>.
   *
   * @member opensocial.DataRequest.ActivityRequestFields
   */
  FIRST: 'first',

  /**
   * The maximum number of items to fetch; defaults to 20. If set to a larger
   * number, a container may honor the request, or may limit the number to a
   * container-specified limit of at least 20.
   * Specified as a <code>Number</code>.
   *
   * @member opensocial.DataRequest.ActivityRequestFields
   */
  MAX: 'max'
};


/**
 * Creates an item to request an activity stream from the server.
 *
 * <p>
 * When processed, returns a Collection&lt;Activity&gt;.
 * </p>
 *
 * @param {opensocial.IdSpec} idSpec An IdSpec used to specify which people to
 *     fetch. See also <a href="opensocial.IdSpec.html">IdSpec</a>.
 * @param {Object.<opensocial.DataRequest.ActivityRequestFields, Object>=} opt_params
 *    Additional parameters
 *    to pass to the request; not currently used.
 * @return {Object} A request object.
 */
opensocial.DataRequest.prototype.newFetchActivitiesRequest = function(idSpec,
    opt_params) {
  opt_params = opt_params || {};

  var fields = opensocial.DataRequest.ActivityRequestFields;

  this.addDefaultParam(opt_params, fields.FIRST, 0);
  this.addDefaultParam(opt_params, fields.MAX, 20);

  return opensocial.Container.get().newFetchActivitiesRequest(idSpec,
      opt_params);
};

/**
 * Creates an item to request albums from the container.
 *
 * @param {opensocial.IdSpec} idSpec An IdSpec used to specify which albums to fetch.
 * @param {Object.<Object, Object>=} opt_params Additional parameters to pass to the request.
 * @return {Object} A request object.
 */
opensocial.DataRequest.prototype.newFetchAlbumsRequest = function(idSpec, opt_params) {
  opt_params = opt_params || {};
  
  var fields = opensocial.DataRequest.AlbumRequestFields;

  this.addDefaultParam(opt_params, fields.FIRST, 0);
  this.addDefaultParam(opt_params, fields.MAX, 20);
  
  return opensocial.Container.get().newFetchAlbumsRequest(idSpec, opt_params);
};

/**
 * Creates an item to request media items from the container.
 * 
 * @param {opensocial.IdSpec}
 *          idSpec An IdSpec used to specify which media items to fetch.
 * @param {string}
 *          albumId The id of the album to fetch media items from.
 * @param {Object.
 *          <Object, Object>=} opt_params Additional parameters to pass to the request.
 * @return {Object} A request object
 */
opensocial.DataRequest.prototype.newFetchMediaItemsRequest = function(idSpec, albumId, opt_params) {
  opt_params = opt_params || {};
  
  var fields = opensocial.DataRequest.MediaItemRequestFields;

  this.addDefaultParam(opt_params, fields.FIRST, 0);
  this.addDefaultParam(opt_params, fields.MAX, 20);
  
  return opensocial.Container.get().newFetchMediaItemsRequest(idSpec, albumId, opt_params);
};

/**
 * Creates an item to request messages from the container.
 */
opensocial.DataRequest.prototype.newFetchMessageCollectionsRequest = function(idSpec, opt_params) {
  opt_params = opt_params || {};
  return opensocial.Container.get().newFetchMessageCollectionsRequest(idSpec, opt_params);
};

opensocial.DataRequest.prototype.newFetchMessagesRequest = function(idSpec, msgCollId, opt_params) {
  opt_params = opt_params || {};
  return opensocial.Container.get().newFetchMessagesRequest(idSpec, msgCollId, opt_params);
};
