/// <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>
        /// Returns the complete information of citties that could match the user's input.
        /// </summary>
        /// <param name="client">Object for making HTTP requests.</param>
        /// <param name="result">Object holding the response of the HTTP request.</param>
        /// <param name="city">The city information provided by the user.</param>
        /// <returns>List of cities that could match the given city name.</returns>
        private static List <City> getCityInfo(HttpClient client, HttpResponseMessage result, TeesByCityArgs city)
        {
            List <City> possibleMatchs = new List <City>();

            //Get all country codes

            //If country code provided, use that.
            string[] countryCode;
            if (city.Country != null && city.Country.Length <= 2)
            {
                countryCode = new string[] { city.Country }
            }
            ;
            else
            {
                try
                {
                    string fields = "CountryCode";
                    if (city.Country != null && city.Country.Length > 2)
                    {
                        fields += ",CountryName";
                    }
                    result = client.GetAsync(ENDPOINT + "/channel/" + CHANNEL_ID + "/countries?fields=" + fields).Result;
                } catch (Exception e)
                {
                    Console.WriteLine(e.Message);
                }
                var countries = JArray.Parse(result.Content.ReadAsStringAsync().Result);
                countryCode = new string[countries.Count];

                for (int i = 0; i < countryCode.Length; i++)
                {
                    //If user entered country is found, use only that and break out of loop.
                    if (city.Country != null && (countries[i]["CountryName"].Value <string>() == city.Country || countries[i]["CountryCode"].Value <string>() == city.Country))
                    {
                        countryCode = new string[] { countries[i]["CountryCode"].Value <string>() };
                        break;
                    }

                    string value = countries[i]["CountryCode"].Value <string>();
                    if (value == "US")
                    {
                        countryCode[i] = countryCode[0];
                        countryCode[0] = value;
                    }
                    else if (value == "CA")
                    {
                        countryCode[i] = countryCode[1];
                        countryCode[1] = value;
                    }
                    else
                    {
                        countryCode[i] = value;
                    }
                }
            }

            //Search all states
            foreach (string country in countryCode)
            {
                //If state code provided, use that.
                string[] stateCode;
                if (city.State != null && city.State.Length <= 2)
                {
                    stateCode = new string[] { city.State }
                }
                ;
                else
                {
                    try
                    {
                        string fields = "StateProvinceCode";
                        if (city.State != null)
                        {
                            fields += ",StateProvinceName";
                        }
                        result = client.GetAsync(ENDPOINT + "/channel/" + CHANNEL_ID + "/countries/" + country + "/state-provinces?fields=" + fields).Result;
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine(e.Message);
                    }

                    var states = JArray.Parse(result.Content.ReadAsStringAsync().Result);
                    stateCode = new string[states.Count];
                    for (int i = 0; i < stateCode.Length; i++)
                    {
                        //If user entered state is found, use only that and break out of loop.
                        if (city.State != null && states[i]["StateProvinceName"].Value <string>() == city.State)
                        {
                            stateCode = new string[] { states[i]["StateProvinceCode"].Value <string>() };
                            break;
                        }
                        stateCode[i] = states[i]["StateProvinceCode"].Value <string>();
                    }
                }

                //search for cities
                foreach (string state in stateCode)
                {
                    try
                    {
                        result = client.GetAsync(ENDPOINT + "/channel/" + CHANNEL_ID + "/countries/" + country + "/state-provinces/" + state + "/cities?fields=CityName").Result;
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine(e.Message);
                    }

                    var      cities   = JArray.Parse(result.Content.ReadAsStringAsync().Result);
                    string[] cityName = new string[cities.Count];
                    for (int i = 0; i < cityName.Length; i++)
                    {
                        if (cities[i]["CityName"].Value <string>().ToLower() == city.CityName.ToLower())
                        {
                            possibleMatchs.Add(new City(city.CityName, state, country));
                        }
                    }
                }
            }
            return(possibleMatchs);
        }
        /// <summary>
        /// Gets facilities based around a given city, state(optional), and country (optional).
        /// </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 city based API request. Ex. time, proximity, etc...</param>
        /// <returns>Key-Value paris of the returned TeeTimes by Facility.</returns>
        private static IDictionary <string, string> GetFacilityByCity(HttpClient client, HttpResponseMessage result, TeesByCityArgs city)
        {
            //All cities that could match the one entered by the user.
            List <City>        possibleMatchs = getCityInfo(client, result, city);
            FacilitiesResponse facilities     = new FacilitiesResponse();

            //Iterate through the potentially matching cities and gather combine eligable facilites.
            foreach (City possibleCity in possibleMatchs)
            {
                result = client.GetAsync(ENDPOINT + $"/channel/{CHANNEL_ID}/facilities?q=country-city-state&country-code={possibleCity.CountryCode}&state-province-code={possibleCity.StateCode}&city={possibleCity.CityName}&players={city.NumOfPlayers}&fields=ID,name").Result;
                facilities.Facilities = facilities.Facilities.Concat(JsonConvert.DeserializeObject <List <Facility> >(result.Content.ReadAsStringAsync().Result)).ToList();
            }

            //Send facilites to be searched for tee times. Return K/V pair array of results.
            return(getTeeTimesByFacilities(client, result, facilities, city.Time, city.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."
                }));
            }
        }