/// <summary>
        /// Handles all calls to the APIs and determines which is needed.
        /// </summary>
        /// <param name="args">The list of arguments that will determine the API call needed and give it the information required.</param>
        /// <returns>Key-Value paris of useful data retrived by the API.</returns>
        public IDictionary <string, string> ApiHandler(ApiArgs args)
        {
            IDictionary <string, string> output = new Dictionary <string, string>();

            output.Add("output", "Error getting response.");
            using (HttpClient client = new HttpClient())
            {
                using (HttpResponseMessage response = new HttpResponseMessage())
                {
                    //Set up headers for API call.
                    client.DefaultRequestHeaders.Clear();
                    client.DefaultRequestHeaders.Add("UserName", TEMP_USERNAME);
                    client.DefaultRequestHeaders.Add("Password", TEMP_PASSWORD);
                    client.DefaultRequestHeaders.Host = "sandbox.api.gnsvc.com";
                    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                    client.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("gzip"));
                    client.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("defate"));
                    client.DefaultRequestHeaders.Add("AdvancedErrorCodes", "True");

                    //Holds temp attributes to be added to output
                    IDictionary <string, string> attributes;
                    switch (args.TYPE)
                    {
                    case ApiArgs.TYPES.Zip:
                        TeesByZipArgs teesByZip = (TeesByZipArgs)args;
                        attributes = GetFacilityByZip(client, response, teesByZip);
                        attributes.ToList().ForEach(x => output[x.Key] = x.Value);
                        break;

                    case ApiArgs.TYPES.City:
                        TeesByCityArgs teesByCity = (TeesByCityArgs)args;
                        attributes = GetFacilityByCity(client, response, teesByCity);
                        attributes.ToList().ForEach(x => output[x.Key] = x.Value);
                        break;

                    case ApiArgs.TYPES.Invoice:
                        RateInvoiceArgs invoiceArgs = (RateInvoiceArgs)args;
                        output = GetRateInvoice(client, response, invoiceArgs);
                        break;

                    case ApiArgs.TYPES.Login:
                        LoginArgs loginArgs = (LoginArgs)args;
                        output["output"]        = "Loggging in...";
                        output["customerToken"] = GetCustToken(client, response, new User(loginArgs.Username, loginArgs.Password));
                        break;
                    }
                }
            }

            if (output["output"] == "" || output == null || output["output"] == "Error getting response.")
            {
                output["output"] = "Sorry, no tee times were found with that information...";
            }

            return(output);
        }
        /// <summary>
        /// Gets facilities based around a given zipcode.
        /// </summary>
        /// <param name="client">Object for making HTTP requests.</param>
        /// <param name="result">Object holding the response of the HTTP request.</param>
        /// <param name="zip">the arguments for the zipcode based API request. Ex. time, proximity, etc...</param>
        /// <returns>Key-Value paris of the returned TeeTimes by Facility.</returns>
        private static IDictionary <string, string> GetFacilityByZip(HttpClient client, HttpResponseMessage result, TeesByZipArgs zip)
        {
            //Call the API with the args.
            result = client.GetAsync(ENDPOINT + $"/channel/{CHANNEL_ID}/facilities?q=postal-code&postal-code={zip.ZipCode}&proximity={zip.Proximity}&minplayercount={zip.NumOfPlayers}&fields=ID,name").Result;
            //Parse response into C# objects.
            FacilitiesResponse facilities = new FacilitiesResponse(JsonConvert.DeserializeObject <List <Facility> >(result.Content.ReadAsStringAsync().Result));

            //Send facilites to be searched for tee times. Return K/V pair array of results.
            return(getTeeTimesByFacilities(client, result, facilities, zip.Time, zip.NumOfPlayers));
        }
        /// <summary>
        /// Handles events from Lex
        /// </summary>
        /// <param name="lexEvent">Information about the lex event.</param>
        /// <param name="context">Lambda execution variables.</param>
        /// <returns>A response to the lex event</returns>
        public LexResponse FunctionHandler(LexEvent lexEvent, ILambdaContext context)
        {
            //Time slot entered by user.
            string time;
            //Number of players indicated by the user.
            string players;
            //The text of the response message.
            string textOut;
            //Key-Value pairs of options, response text, and other things returned by the API.
            IDictionary <string, string> attributes;
            //What (if any) information to populate slots with in the next intent.
            IDictionary <string, string> slots = new Dictionary <string, string>();
            //The name of the next intent.
            string nextIntent;
            //The name of the next slot to elicit.
            string slotToElicit;

            //Detrimine the intent that triggered the logic.
            switch (lexEvent.CurrentIntent.Name)
            {
            case "login":
                string username;
                string password;
                string customerToken;

                if (!lexEvent.SessionAttributes.TryGetValue("username", out username))
                {
                    return(Close(lexEvent.SessionAttributes, "Fulfilled", new LexResponse.LexMessage {
                        ContentType = "PlainText", Content = "Could not get retrieve username."
                    }));
                }
                if (!lexEvent.SessionAttributes.TryGetValue("password", out password))
                {
                    return(Close(lexEvent.SessionAttributes, "Fulfilled", new LexResponse.LexMessage {
                        ContentType = "PlainText", Content = "Could not get retrieve password."
                    }));
                }

                lexEvent.SessionAttributes.Remove("password");

                LoginArgs loginArgs = new LoginArgs(username, password);
                if (ApiConnectionBroker.Instance().ApiHandler(loginArgs).TryGetValue("customerToken", out customerToken))
                {
                    lexEvent.SessionAttributes["customerToken"] = customerToken;
                    return(Close(lexEvent.SessionAttributes, "Fulfilled", new LexResponse.LexMessage {
                        ContentType = "PlainText", Content = "Login Successful."
                    }));
                }

                return(Close(lexEvent.SessionAttributes, "Fulfilled", new LexResponse.LexMessage {
                    ContentType = "PlainText", Content = "Could not retrieve customer token."
                }));

            //An example case.
            case "ditto":
                //Example slot population.
                slots.Add("actor", "Bob Hoskins");
                return(elicitSlot(lexEvent.SessionAttributes, "Test", slots, "time", new LexResponse.LexMessage {
                    ContentType = "PlainText", Content = "This is the new intent!! Enter a time..."
                }));

            //User wants tee times via a given zip code.
            case "getTeeTimeByZip":
                string zip;
                nextIntent   = "chooseOption";
                slotToElicit = "option";

                //Gather user inputs.
                if (!lexEvent.CurrentIntent.Slots.TryGetValue("zip", out zip))
                {
                    zip = "No zip...";
                }
                if (!lexEvent.CurrentIntent.Slots.TryGetValue("time", out time))
                {
                    time = "No time...";
                }
                if (!lexEvent.CurrentIntent.Slots.TryGetValue("players", out players))
                {
                    players = "1";
                }

                //Colate arguments for the API.
                TeesByZipArgs teesByZip = new TeesByZipArgs(zip, "15", time, players);
                //Contact API and get resultes.
                attributes = ApiConnectionBroker.Instance().ApiHandler(teesByZip);
                //Seperate out the output text.
                textOut = attributes["output"];
                attributes.Remove("output");
                //add options to lexEvent Session Attribute vars that we will perserve.
                attributes.ToList().ForEach(x => lexEvent.SessionAttributes[x.Key] = x.Value);

                //Elicit the next intent to choose between options.
                return(elicitSlot(lexEvent.SessionAttributes, nextIntent, slots, slotToElicit, new LexResponse.LexMessage {
                    ContentType = "PlainText", Content = textOut
                }));

            //User wants tee times via given city, state(optional), and country(optional).
            case "getTeeTimeByCity":
                string city;
                string state;
                string country;
                nextIntent   = "chooseOption";
                slotToElicit = "option";

                //Gather user inputs.
                if (!lexEvent.CurrentIntent.Slots.TryGetValue("city", out city))
                {
                    city = "No city...";
                }
                if (!lexEvent.CurrentIntent.Slots.TryGetValue("time", out time))
                {
                    time = "No time...";
                }
                if (!lexEvent.CurrentIntent.Slots.TryGetValue("state", out state))
                {
                    state = "No state...";
                }
                if (!lexEvent.CurrentIntent.Slots.TryGetValue("country", out country))
                {
                    country = "No country...";
                }
                if (!lexEvent.CurrentIntent.Slots.TryGetValue("players", out players))
                {
                    players = "1";
                }

                players = validatePlayers(players);

                //Colate arguments for the API.
                TeesByCityArgs teesByCity = new TeesByCityArgs(city, state, country, time, players);
                //Contact API and get resultes.
                attributes = ApiConnectionBroker.Instance().ApiHandler(teesByCity);
                //Seperate out the output text.
                textOut = attributes["output"];
                attributes.Remove("output");
                //add options to lexEvent Session Attribute vars that we will perserve.
                attributes.ToList().ForEach(x => lexEvent.SessionAttributes[x.Key] = x.Value);

                return(elicitSlot(lexEvent.SessionAttributes, nextIntent, slots, slotToElicit, new LexResponse.LexMessage {
                    ContentType = "PlainText", Content = textOut
                }));

            //The user has been presented with options and has chosen one.
            case "chooseOption":
                //the index of the option chosen.
                string option;

                //Ensure an option was chosen.
                if (lexEvent.CurrentIntent.Slots.TryGetValue("option", out option))
                {
                    //Ensure the option selected exists.
                    if (lexEvent.SessionAttributes["rateOption" + option] != "")
                    {
                        TeeTimeRateSelection selection = JsonConvert.DeserializeObject <TeeTimeRateSelection>(lexEvent.SessionAttributes["rateOption" + option]);
                        RateInvoiceArgs      args      = new RateInvoiceArgs(selection.FacilityID, selection.RateID, lexEvent.SessionAttributes["players"]);
                        attributes = ApiConnectionBroker.Instance().ApiHandler(args);
                        //Seperate out the output text.
                        textOut = attributes["output"];
                        attributes.Remove("output");
                        //textOut = "Rate found: " + selection.RateID + " at facility: " + selection.FacilityID;
                    }
                    else
                    {
                        textOut = "Rate not found.";
                    }
                }
                else
                {
                    textOut = "Rate number not found.";
                }
                return(Close(lexEvent.SessionAttributes, "Fulfilled", new LexResponse.LexMessage {
                    ContentType = "PlainText", Content = textOut
                }));

            //The triggering event is unknown to this logic.
            default:
                return(Close(lexEvent.SessionAttributes, "Fulfilled", new LexResponse.LexMessage {
                    ContentType = "PlainText", Content = "Chat Error: State Invalid."
                }));
            }
        }