/// <summary>
        /// This class extends the class ImageInfo to pertain to image information for local images
        /// </summary>
        // Downloads cached response stored locally.
        // Program does not currently save Knowledge API responses locally
        // If you want to add this functionality, save the Knowledge API response in the same folder as your image as "filepath.json"
        public override async Task GenerateSearchResponse()
        {
            // Try to look for a json response file in the same location with same filename but .json extension
            // if exits attempt to read it as a KAPI JSON response file. If not, fallback.
            var name            = this.Url;
            var nameForJsonFile = name.Replace(".jpg", ".json");                               //only support JPG at the moment

            if (string.Equals(name, nameForJsonFile) == false && File.Exists(nameForJsonFile)) //If there exists a JSON file with the same name as a jpeg, then continue
            {
                try
                {
                    KapiResponse sr = new KapiResponse();
                    sr.Status = "Cached Response";
                    sr.Source = SearchResponseSource.Cache;

                    string jsonResponse = File.ReadAllText(nameForJsonFile);
                    this.JsonString = jsonResponse;

                    bool parsed = sr.TryParse(jsonResponse);
                    if (parsed == false)
                    {
                        this.SearchResponse = sr;
                        return;
                    }

                    sr.Status = "Cached Response";
                    sr.Source = SearchResponseSource.Cache;

                    // we may get a KAPI response of type error. So only process the success (ImageKnowledge) type responses
                    if (sr.ResponseType == KapiResponseType.ImageKnowledge)
                    {
                        sr.ExtractSearchResults();

                        var tagNames = from tag in sr.Tags
                                       let t                 = tag.DisplayName
                                                       let s = tag.Score.HasValue ? $"({tag.Score.Value.ToString("F1")})" : String.Empty
                                                               where string.IsNullOrEmpty(tag.DisplayName) == false
                                                               select $"{t}{s}";

                        this.Tags = tagNames.ToArray();
                    }

                    this.SearchResponse = sr;
                    return;
                }
                catch (Exception) { }
            }

            // In the case that cached response is found, we still populate the SearchResponse
            await Task.Run(() =>
            {
                this.SearchResponse = new SearchResponse();
                this.SearchResponse.SearchEndpointUrl = KapiSearch.KapiEndpointUrl;
                this.SearchResponse.Status            = "No Cached Response Available";
                this.SearchResponse.Source            = SearchResponseSource.None;
            });

            return;
        }
        protected virtual async Task <KapiResponse> Request(string requestUri, MultipartFormDataContent mfdc) //This method sends the request to the server and recieves a response
        {
            bool       perInstanceClient = true;
            HttpClient client            = null;

            if (this.sharedHttpClient != null) // Checks that the client is valid
            {
                client            = this.sharedHttpClient;
                perInstanceClient = false;
            }
            else
            {
                client = new HttpClient();
            }
            try
            {
                using (var request = new HttpRequestMessage(HttpMethod.Post, requestUri))
                {
                    request.Content = mfdc;
                    if (!string.IsNullOrEmpty(accessKey) && accessKey.Length == 32)  // Checks for valid length access key
                    {
                        request.Headers.Add("Ocp-Apim-Subscription-Key", accessKey);
                    }

                    using (var response = await client.SendAsync(request)) // Makes a call to the API and recieves the response
                    {
                        string responseJsonString = string.Empty;
                        responseJsonString = response.Content.ReadAsStringAsync().Result;

                        KapiResponse retVal = new KapiResponse(responseJsonString); // Creates a KapiResponse object from the returned json string
                        retVal.SearchEndpointUrl = requestUri;
                        retVal.Source            = SearchResponseSource.LiveQuery;
                        retVal.Status            = $"{response.StatusCode.ToString()}";

                        if (response.Headers.Contains("BingAPIs-TraceID"))
                        {
                            retVal.EventId = response.Headers.GetValues("BingAPIs-TraceID").FirstOrDefault();
                        }

                        if (response.IsSuccessStatusCode)
                        {
                            retVal.ExtractSearchResults();
                        }

                        return(retVal);
                    }
                }
            }
            finally
            {
                // dispose
                if (perInstanceClient)
                {
                    client.Dispose();
                }
            }
        }
        protected KapiResponse AddJson(KapiRequest kapiRequest, KapiResponse kapiRespose) // This method converts the response into a JSON object
        {
            var root = new JObject();

            root.Add("object",
                     // .NET object --> JSON string --> JObject graph (so that it can be rendered)
                     Newtonsoft.Json.Linq.JObject.Parse(
                         Newtonsoft.Json.JsonConvert.SerializeObject(kapiRequest)
                         ));
            kapiRespose.KapiRequestJson = root;
            return(kapiRespose);
        }
        // Downloads cached response from blob.
        // Program does not currently save Knowledge API responses to the blob
        // If you want to add this functionality, save the Knowledge API response to your blob as "uri.json"
        private async Task DownloadAndGenerateSearchResponse()
        {
            if (this.SearchResponse != null) // we have already generated this response no need to try again
            {
                return;
            }
            try
            {
                var sr = new KapiResponse();
                sr.SearchEndpointUrl = KapiSearch.KapiEndpointUrl; // queryUrl for Kapi is constant.
                sr.Status            = "No Cached Response Available";
                sr.Source            = SearchResponseSource.None;

                var blobUrl      = new Uri(this.Url);
                var jsonBlobName = blobUrl.Segments[blobUrl.Segments.Length - 1].Replace(".jpg", ".json");

                var blob =
                    await this.Container.GetBlobReferenceFromServerAsync(jsonBlobName);

                // if no blob exists
                if (blob == null || await blob.ExistsAsync() == false)
                {
                    this.SearchResponse = sr;
                    return;
                }

                MemoryStream memStream = new MemoryStream();
                await blob.DownloadRangeToStreamAsync(memStream, null, null);

                byte[] bytes = memStream.ToArray();
                if (bytes.Length == 0)
                {
                    this.SearchResponse = sr;
                    return;
                }

                string jsonResponse = Encoding.UTF8.GetString(bytes);
                this.JsonString = jsonResponse;
                bool parsed = sr.TryParse(jsonResponse);
                if (parsed == false)
                {
                    this.SearchResponse = sr;
                    return;
                }

                sr.Status = "Cached Response";
                sr.Source = SearchResponseSource.Cache;

                // we may get a KAPI response of type error. So only process the success (ImageKnowledge) type responses
                if (sr.ResponseType == KapiResponseType.ImageKnowledge)
                {
                    sr.ExtractSearchResults();

                    var tagNames = from tag in sr.Tags
                                   let t                 = tag.DisplayName
                                                   let s = tag.Score.HasValue ? $"({tag.Score.Value.ToString("F1")})" : String.Empty
                                                           where string.IsNullOrEmpty(tag.DisplayName) == false
                                                           select $"{t}{s}";

                    this.Tags = tagNames.ToArray();

                    sr.MiscInfo = new Dictionary <string, Newtonsoft.Json.Linq.JToken>();
                    sr.MiscInfo.Add("Response", sr.KapiResponseJson);
                }

                this.SearchResponse = sr;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(ex.ToString());
            }
        }