/// <summary>
        /// Helper method to find the service document of the OData service this context refers to
        /// </summary>
        /// <param name="maximumPayloadSize">maximum payload size in byte</param>
        /// <param name="uri">uri hint, which is used to derive various possible uris in hope one of them is the service base endpoint</param>
        /// <param name="acceptHeaderValue">value of header of Accept that will be used in request getting the response</param>
        /// <param name="IgnoreInputUri">if true, the given uri is known other than service base</param>
        /// <param name="serviceBaseUri">out parameter: the service base uri if one shall be found</param>
        /// <param name="responseServiceDocument">out parameter: the response object including payload, headers and status code</param>
        /// <param name="reqHeaders">Http headers to be sent out to server</param>
        /// <returns>true if a valid service document is found; otherwise false</returns>
        private static bool TryGetServiceDocument(int maximumPayloadSize,
                                                  Uri uri,
                                                  string acceptHeaderValue,
                                                  bool IgnoreInputUri,
                                                  out Uri serviceBaseUri,
                                                  out Response responseServiceDocument,
                                                  IEnumerable <KeyValuePair <string, string> > reqHeaders)
        {
            if (!IgnoreInputUri)
            {
                try
                {
                    var response = WebHelper.Get(uri, acceptHeaderValue, maximumPayloadSize, reqHeaders);
                    if (response.IsServiceDocument())
                    {
                        serviceBaseUri          = uri;
                        responseServiceDocument = response;
                        return(true);
                    }
                }
                catch (OversizedPayloadException)
                {
                    // does nothing
                }
            }

            var segments = uri.Segments;

            if (segments != null && segments.Length > 0)
            {
                Uri uriParent;
                if (Uri.TryCreate(uri.GetLeftPart(UriPartial.Authority) + string.Join("", segments.Take(segments.Length - 1).ToArray()), UriKind.Absolute, out uriParent))
                {
                    if (uri != uriParent)
                    {
                        return(ServiceContextFactory.TryGetServiceDocument(maximumPayloadSize, uriParent, acceptHeaderValue, false, out serviceBaseUri, out responseServiceDocument, reqHeaders));
                    }
                }
            }

            serviceBaseUri          = null;
            responseServiceDocument = null;
            return(false);
        }
        /// <summary>
        /// Factory method to set up the OData Interop context based on uri, desired format and job id
        /// </summary>
        /// <param name="destination">uri string pointing to a OData service endpoint</param>
        /// <param name="format">format preference, could be either "atom" or "json" - default and falling back to atom</param>
        /// <param name="jobId">unique identifier to tag this context</param>
        /// <param name="maximumPayloadSize">the maximum number of bytes rule engine is willing to retrieve from the Uri provided</param>
        /// <param name="reqHeaders">Http headers sent as part of request</param>
        /// <returns>context object representing the interop request session</returns>
        public static ServiceContext Create(string destination, string format, Guid jobId, int maximumPayloadSize, IEnumerable <KeyValuePair <string, string> > reqHeaders, string category = "core")
        {
            Uri    inputUri;
            Uri    serviceBaseUri          = null;
            string serviceDocument         = null;
            string metadataDocument        = null;
            string entityType              = null;
            string jsonFullMetadataPayload = null;
            var    serviceStatus           = ServiceStatus.GetInstance();

            if (string.IsNullOrEmpty(format))
            {
                throw new ArgumentException(Resource.ArgumentNotNullOrEmpty, "format");
            }

            try
            {
                if (destination.EndsWith(@"/"))
                {
                    destination = destination.TrimEnd('/');
                }

                inputUri = new Uri(destination);
            }
            catch (UriFormatException)
            {
                if (!destination.StartsWith(@"http://") && !destination.StartsWith(@"https://"))
                {
                    inputUri = new Uri(SupportedScheme.SchemeHttp + "://" + destination);
                }
                else
                {
                    inputUri = new Uri(Uri.EscapeUriString(destination));
                }
            }

            if (!SupportedScheme.Instance.Contains(inputUri.Scheme))
            {
                throw new ArgumentException(
                          string.Format(CultureInfo.CurrentCulture, Resource.formatSchemeNotSupported, inputUri.Scheme),
                          "destination");
            }

            string            acceptHeader  = ServiceContextFactory.MapFormatToAcceptValue(format);
            ODataMetadataType odataMetadata = ServiceContextFactory.MapFormatToMetadataType(format);
            Response          response      = WebHelper.Get(inputUri, acceptHeader, maximumPayloadSize, reqHeaders);

            if (response.StatusCode == HttpStatusCode.NotFound)
            {
                return(null);
            }

            var payloadFormat = response.ResponsePayload.GetFormatFromPayload();
            var payloadType   = ContextHelper.GetPayloadType(response.ResponsePayload, payloadFormat, response.ResponseHeaders);

            switch (payloadType)
            {
            case PayloadType.ServiceDoc:
                serviceBaseUri  = inputUri;
                serviceDocument = response.ResponsePayload;
                break;

            case PayloadType.Metadata:
                if (inputUri.AbsoluteUri.EndsWith(Constants.OptionMetadata, StringComparison.Ordinal))
                {
                    if (payloadFormat == PayloadFormat.JsonLight)
                    {
                        serviceBaseUri  = new Uri(serviceStatus.RootURL);
                        serviceDocument = serviceStatus.ServiceDocument;
                    }
                    else
                    {
                        serviceBaseUri = new Uri(inputUri.AbsoluteUri.Substring(0, inputUri.AbsoluteUri.Length - Constants.OptionMetadata.Length));
                        var respSvcDoc = WebHelper.Get(serviceBaseUri, acceptHeader, maximumPayloadSize, reqHeaders);
                        serviceDocument = respSvcDoc.ResponsePayload;
                    }
                }

                break;

            default:
                if (payloadType.IsValidPayload())
                {
                    string   fullPath = inputUri.GetLeftPart(UriPartial.Path).TrimEnd('/');
                    var      uriPath  = new Uri(fullPath);
                    Uri      baseUri;
                    Response serviceDoc;
                    if (ServiceContextFactory.TryGetServiceDocument(maximumPayloadSize, uriPath, acceptHeader, uriPath.AbsoluteUri == fullPath, out baseUri, out serviceDoc, reqHeaders))
                    {
                        serviceBaseUri  = baseUri;
                        serviceDocument = serviceDoc.ResponsePayload;
                    }
                }

                break;
            }

            if (payloadType == PayloadType.Metadata)
            {
                metadataDocument = response.ResponsePayload;
            }
            else
            {
                if (serviceBaseUri != null)
                {
                    if (payloadFormat == PayloadFormat.JsonLight && payloadType == PayloadType.ServiceDoc)
                    {
                        metadataDocument = serviceStatus.MetadataDocument;
                    }
                    else
                    {
                        try
                        {
                            string metadataURL      = serviceBaseUri.AbsoluteUri.EndsWith(@"/") ? Constants.OptionMetadata.TrimStart('/') : Constants.OptionMetadata;
                            var    responseMetadata = WebHelper.Get(new Uri(serviceBaseUri.AbsoluteUri + metadataURL), null, maximumPayloadSize, reqHeaders);
                            if (responseMetadata != null)
                            {
                                metadataDocument = responseMetadata.ResponsePayload;
                            }
                        }
                        catch (OversizedPayloadException)
                        {
                            // do nothing
                        }
                    }
                }
            }

            if (payloadFormat == PayloadFormat.JsonLight)
            {
                try
                {
                    // Specify full metadata accept header
                    string acceptHeaderOfFullMetadata = string.Empty;
                    if (acceptHeader.Equals(Constants.V3AcceptHeaderJsonFullMetadata) ||
                        acceptHeader.Equals(Constants.V3AcceptHeaderJsonMinimalMetadata) ||
                        acceptHeader.Equals(Constants.V3AcceptHeaderJsonNoMetadata))
                    {
                        acceptHeaderOfFullMetadata = Constants.V3AcceptHeaderJsonFullMetadata;
                    }
                    else
                    {
                        acceptHeaderOfFullMetadata = Constants.V4AcceptHeaderJsonFullMetadata;
                    }

                    // Send full metadata request and get full metadata response.
                    var responseFullMetadata = WebHelper.Get(inputUri, acceptHeaderOfFullMetadata, maximumPayloadSize, reqHeaders);
                    if (responseFullMetadata != null)
                    {
                        jsonFullMetadataPayload = responseFullMetadata.ResponsePayload;
                        entityType = jsonFullMetadataPayload.GetFullEntityType(payloadType, payloadFormat, metadataDocument);
                    }
                }
                catch (OversizedPayloadException)
                {
                    // do nothing
                }
            }
            else
            {
                entityType = response.ResponsePayload.GetFullEntityType(payloadType, payloadFormat, metadataDocument);
            }

            return(new ServiceContext(inputUri,
                                      jobId,
                                      response.StatusCode,
                                      response.ResponseHeaders,
                                      response.ResponsePayload,
                                      entityType,
                                      serviceBaseUri,
                                      serviceDocument,
                                      metadataDocument,
                                      false,
                                      reqHeaders,
                                      odataMetadata,
                                      jsonFullMetadataPayload,
                                      category));
        }