/// <summary>
        /// Makes an asynchronous batch request to the Funtown server.
        /// </summary>
        /// <param name="batchParameters">
        /// List of batch parameters.
        /// </param>
        /// <param name="userState">
        /// The user state.
        /// </param>
        /// <param name="cancellationToken">
        /// The cancellation token.
        /// </param>
        /// <returns>
        /// The json result task.
        /// </returns>
        public virtual Task<object> BatchTaskAsync(FuntownBatchParameter[] batchParameters, object userState, CancellationToken cancellationToken
#if ASYNC_AWAIT
, System.IProgress<FuntownUploadProgressChangedEventArgs> uploadProgress
#endif
)
        {
            return BatchTaskAsync(batchParameters, userState, null, cancellationToken
            #if ASYNC_AWAIT
            , uploadProgress
            #endif
                );
        }
        internal object PrepareBatchRequest(FuntownBatchParameter[] batchParameters, object parameters)
        {
            if (batchParameters == null)
                throw new ArgumentNullException("batchParameters");
            if (batchParameters.Length == 0)
                throw new ArgumentNullException("batchParameters", AtLeastOneBatchParameterRequried);

            IDictionary<string, object> actualBatchParameter = new Dictionary<string, object>();
            IList<object> flatnedBatchParameters = new List<object>();
            actualBatchParameter["batch"] = flatnedBatchParameters;

            IDictionary<string, FuntownMediaObject> mediaObjects;
            IDictionary<string, FuntownMediaStream> mediaStreams;

            foreach (var batchParameter in batchParameters)
            {
                var data = ToDictionary(batchParameter.Data, out mediaObjects, out mediaStreams);

                if (mediaObjects.Count + mediaStreams.Count > 0)
                    throw new ArgumentException("Attachments (FuntownMediaObject/FuntownMediaStream) are only allowed in FuntownBatchParameter.Parameters");

                if (data == null)
                    data = new Dictionary<string, object>();

                if (!data.ContainsKey("method"))
                {
                    switch (batchParameter.HttpMethod)
                    {
                        case HttpMethod.Get:
                            data["method"] = "GET";
                            break;
                        case HttpMethod.Post:
                            data["method"] = "POST";
                            break;
                        case HttpMethod.Delete:
                            data["method"] = "DELETE";
                            break;
                        default:
                            throw new ArgumentOutOfRangeException();
                    }
                }

                IList<string> attachedFiles = new List<string>();

                var pars = ToDictionary(batchParameter.Parameters, out mediaObjects, out mediaStreams) ?? new Dictionary<string, object>();
                bool containsEtag = false;
                string etag = null;
                if (pars.ContainsKey(ETagKey))
                {
                    etag = (string)pars[ETagKey];
                    containsEtag = true;
                    pars.Remove(ETagKey);
                }

                bool hasAttachmentInBatchParameter = false;

                foreach (var attachment in mediaObjects)
                {
                    if (hasAttachmentInBatchParameter)
                        throw new ArgumentException(OnlyOneAttachmentAllowedPerBatchRequest, "batchParameters");
                    if (actualBatchParameter.ContainsKey(attachment.Key))
                        throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Attachment (FuntownMediaObject/FuntownMediaStream) with key '{0}' already exists", attachment.Key));
                    attachedFiles.Add(HttpHelper.UrlEncode(attachment.Key));
                    actualBatchParameter.Add(attachment.Key, attachment.Value);
                    hasAttachmentInBatchParameter = true;
                }

                foreach (var attachment in mediaStreams)
                {
                    if (hasAttachmentInBatchParameter)
                        throw new ArgumentException(OnlyOneAttachmentAllowedPerBatchRequest, "batchParameters");
                    if (actualBatchParameter.ContainsKey(attachment.Key))
                        throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Attachment (FuntownMediaObject/FuntownMediaStream) with key '{0}' already exists", attachment.Key));
                    attachedFiles.Add(HttpHelper.UrlEncode(attachment.Key));
                    actualBatchParameter.Add(attachment.Key, attachment.Value);
                    hasAttachmentInBatchParameter = true;
                }

                if (attachedFiles.Count > 0 && !data.ContainsKey("attached_files"))
                    data["attached_files"] = string.Join(",", attachedFiles.ToArray());

                string path;
                if (!data["method"].ToString().Equals("POST", StringComparison.OrdinalIgnoreCase))
                {
                    if (!data.ContainsKey("relative_url"))
                    {
                        path = ParseUrlQueryString(batchParameter.Path, pars, false);
                        SerializeParameters(pars);

                        var relativeUrl = new StringBuilder();
                        relativeUrl.Append(path).Append("?");
                        foreach (var kvp in pars)
                            relativeUrl.AppendFormat("{0}={1}&", HttpHelper.UrlEncode(kvp.Key), HttpHelper.UrlEncode(BuildHttpQuery(kvp.Value, HttpHelper.UrlEncode)));
                        if (relativeUrl.Length > 0)
                            relativeUrl.Length--;
                        data["relative_url"] = relativeUrl.ToString();
                    }
                }
                else
                {
                    path = ParseUrlQueryString(batchParameter.Path, pars, false);
                    SerializeParameters(pars);

                    if (!data.ContainsKey("relative_url"))
                    {
                        if (path.Length > 0)
                            data["relative_url"] = path;
                    }

                    if (!data.ContainsKey("body"))
                    {
                        var sb = new StringBuilder();
                        foreach (var kvp in pars)
                            sb.AppendFormat("{0}={1}&", HttpHelper.UrlEncode(kvp.Key), HttpHelper.UrlEncode(BuildHttpQuery(kvp.Value, HttpHelper.UrlEncode)));

                        if (sb.Length > 0)
                        {
                            sb.Length--;
                            data["body"] = sb.ToString();
                        }
                    }
                }

                if (containsEtag)
                    data[ETagKey] = etag;

                flatnedBatchParameters.Add(data);
            }

            var parametersAsDictionary = ToDictionary(parameters, out mediaObjects, out mediaStreams);
            if (parametersAsDictionary != null)
            {
                foreach (var parameter in parametersAsDictionary)
                {
                    actualBatchParameter[parameter.Key] = parameter.Value;
                }
            }

            foreach (var mediaObject in mediaObjects)
            {
                actualBatchParameter[mediaObject.Key] = mediaObject.Value;
            }

            foreach (var mediaStream in mediaStreams)
            {
                actualBatchParameter[mediaStream.Key] = mediaStream.Value;
            }

            return actualBatchParameter;
        }
 public virtual void BatchAsync(FuntownBatchParameter[] batchParameters)
 {
     BatchAsync(batchParameters, null);
 }
 public virtual void BatchAsync(FuntownBatchParameter[] batchParameters, object userState)
 {
     BatchAsync(batchParameters, userState, null);
 }
 public virtual void BatchAsync(FuntownBatchParameter[] batchParameters, object userState, object parameters)
 {
     var actualParameter = PrepareBatchRequest(batchParameters, parameters);
     PostAsync(null, actualParameter, userState);
 }
        /// <summary>
        /// Makes an asynchronous batch request to the Funtown server.
        /// </summary>
        /// <param name="batchParameters">
        /// List of batch parameters.
        /// </param>
        /// <param name="userState">
        /// The user state.
        /// </param>
        /// <param name="parameters">
        /// The parameters.
        /// </param>
        /// <param name="cancellationToken">
        /// The cancellation token.
        /// </param>
        /// <returns>
        /// The json result task.
        /// </returns>
        public virtual Task<object> BatchTaskAsync(FuntownBatchParameter[] batchParameters, object userState, object parameters, CancellationToken cancellationToken
#if ASYNC_AWAIT
, System.IProgress<FuntownUploadProgressChangedEventArgs> uploadProgress
#endif
)
        {
            var actualParameter = PrepareBatchRequest(batchParameters, parameters);
            return PostTaskAsync(null, actualParameter, userState, cancellationToken
            #if ASYNC_AWAIT
            , uploadProgress
            #endif
            );
        }
 public virtual Task<object> BatchTaskAsync(FuntownBatchParameter[] batchParameters, object userToken, object parameters, CancellationToken cancellationToken)
 {
     return BatchTaskAsync(batchParameters, userToken, parameters, cancellationToken, null);
 }