internal async Task<Tuple<bool, string>> MakeRequest(ConstructorIORequest APIRequest)
 {
     return await MakeRequest(APIRequest, (response) =>
     {
         return ((int)response.StatusCode) == 204;
     });
 }
        private ConstructorIORequest CreateTrackingRequest(APIRequestType APIPath, string Method, HashArgs ExtraArgs)
        {
            var trackingRequest = new ConstructorIORequest(APIPath, Method);

            if (ExtraArgs != null)
                Util.Merge(ExtraArgs, trackingRequest.RequestBody);

            return trackingRequest;
        }
        /// <summary>
        /// Adds an Item to your autocomplete.
        /// </summary>
        /// <param name="Item">The item to add</param>
        /// <returns>true if successful</returns>
        public async Task<bool> AddAsync(ListItem Item)
        {
            string requestMethod = "POST";
            var addRequest = new ConstructorIORequest(APIRequestType.V1_Item, requestMethod);

            Item.GetAsHash().ToList().ForEach((kvp) => addRequest.RequestBody.Add(kvp.Key, kvp.Value));

            var addResponse = await Requestor.MakeRequest(addRequest);
            return addResponse.Item1;
        }
        /// <summary>
        /// Adds multiple items in one batch upload.
        /// </summary>
        /// <param name="Items">The items to add.</param>
        /// <param name="AutocompleteSection">The section the items should be added to.</param>
        /// <returns>true if successful</returns>
        public async Task<bool> AddBatchAsync(IEnumerable<ListItem> Items, string AutocompleteSection)
        {
            string requestMethod = "POST";
            var addBatchRequest = new ConstructorIORequest(APIRequestType.V1_BatchItems, requestMethod);

            addBatchRequest.RequestBody["items"] = Items.ToArray().Select(li => li.GetAsHash());
            addBatchRequest.RequestBody["autocomplete_section"] = AutocompleteSection;

            var addBatchResponse = await Requestor.MakeRequest(addBatchRequest);
            return addBatchResponse.Item1;
        }
        /// <summary>
        /// Verify that the API key is valid.
        /// </summary>
        /// <returns>true if successful</returns>
        public async Task<bool> VerifyAsync()
        {
            var verifyRequest = new ConstructorIORequest(APIRequestType.V1_Verify, "GET");

            var verifyResponse = await Requestor.MakeRequest(verifyRequest);

            if (verifyResponse.Item2.IndexOf("successful authentication") != -1)
            {
                //TODO: beter way to do this?
                return true;
            }
            return false;
        }
        /// <summary>
        /// Removed multiple items from your autocomplete.
        /// </summary>
        /// <param name="ItemsToRemove">The items to remove.</param>
        /// <param name="AutocompleteSection">The autocomplete section to remove from.</param>
        /// <returns>true if successful</returns>
        public async Task<bool> RemoveBatchAsync(IEnumerable<ListItem> ItemsToRemove, string AutocompleteSection)
        {
            var removeBatchRequest = new ConstructorIORequest(APIRequestType.V1_BatchItems, "DELETE");

            removeBatchRequest.RequestBody["items"] = ItemsToRemove.ToArray().Select(item => item.GetAsRemoveHash());
            removeBatchRequest.RequestBody["autocomplete_section"] = AutocompleteSection;

            var removeBatchResponse = await Requestor.MakeRequest(removeBatchRequest);
            return removeBatchResponse.Item1;
        }
        /// <summary>
        /// Removes an item from your autocomplete.
        /// </summary>
        /// <param name="ItemToRemove">The item to remove.</param>
        /// <returns>true if successful</returns>
        public async Task<bool> RemoveAsync(ListItem ItemToRemove)
        {
            var removeRequest = new ConstructorIORequest(APIRequestType.V1_Item, "DELETE");

            Util.Merge(ItemToRemove.GetAsRemoveHash(), removeRequest.RequestBody);

            var removeResponse = await Requestor.MakeRequest(removeRequest);
            return removeResponse.Item1;
        }
        /// <summary>
        /// Modifies an existing item
        /// </summary>
        /// <param name="ItemToUpdate">The item to udpate</param>
        /// <returns>true if successful</returns>
        public async Task<bool> ModifyAsync(ListItem ItemToUpdate)
        {
            var modifyRequest = new ConstructorIORequest(APIRequestType.V1_Item, "PUT");

            Util.Merge(ItemToUpdate.GetAsModifyHash(), modifyRequest.RequestBody);

            var modifyResponse = await Requestor.MakeRequest(modifyRequest);
            return modifyResponse.Item1;
        }
        internal async Task<Tuple<bool, string>> MakeRequest(ConstructorIORequest APIRequest, Func<HttpWebResponse, bool> ResponseCheck)
        {
            Uri requestURI = APIRequest.GetURI(_autocompleteKey, _host);

            bool validResponse = false;
            bool errorRaised = false;
            string responseText = "";

            string jsonBody = null;
            HttpWebResponse serverResponse = null;

            try
            {
                if (APIRequest.RequestBody != null)
                {
                    JObject jobj = JObject.FromObject(APIRequest.RequestBody);
                    jsonBody = jobj.ToString();
                }
            }
            catch (Exception ex)
            {
                throw new Exception("Error generating JSON body. See inner exception for details.", ex);
            }
            try
            {
                HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(requestURI);

                string creds = Convert.ToBase64String(Encoding.ASCII.GetBytes(_apiKey + ":"));
                request.Headers[HttpRequestHeader.Authorization] = String.Format("Basic {0}", creds);
                request.Method = APIRequest.Method;

                if (request.Method != "GET")
                {
                    request.ContentType = "application/json";

                    using (Stream requestStream = await request.GetRequestStreamAsync())
                    {
                        using (StreamWriter writer = new StreamWriter(requestStream))
                        {
                            writer.Write(jsonBody);
                        }
                        await requestStream.FlushAsync();
                    }
                }

                serverResponse = (HttpWebResponse)await request.GetResponseAsync();
                validResponse = ResponseCheck(serverResponse);
            }
            catch (WebException webException)
            {
                errorRaised = true;

                if (webException.Response as HttpWebResponse != null)
                {
                    serverResponse = webException.Response as HttpWebResponse;
                }
                else
                {
                    throw new Exception("Error Downlaoding. See inner exception for details", webException);
                }
            }
            catch (Exception e)
            {
                throw new Exception("Error Downlaoding. See inner exception for details", e);
            }

            try
            {
                if (serverResponse != null)
                    using (Stream dataStream = serverResponse.GetResponseStream())
                    using (StreamReader reader = new StreamReader(dataStream))
                        _lastBody = responseText = await reader.ReadToEndAsync();
            }
            catch (Exception ex)
            {
                throw new Exception("Error reading server response. See inner exception for details", ex);
            }

            if (responseText != null)
            {
                if (errorRaised)
                {
                    //Best way to check for valid jsontext is try ot 
                    try
                    {
                        var jsonResponse = JObject.Parse(responseText);
                        if (jsonResponse["message"] != null)
                        {
                            throw new ConstructorIOException(jsonResponse["message"].ToString());
                        }
                    }
                    catch (JsonReaderException ex)
                    {
                        throw new Exception("Error occured during request. Response:" + _lastBody);
                    }
                }
            }

            return Tuple.Create(validResponse, responseText);
        }