/// <summary>
        /// Obtain a new token using AuthorizationResult.
        /// 
        /// Exceptions: 
        ///   - IllegalArgumentException : if authorizationResult is null 
        ///   - InvalidTokenRequestException : if the token request is invalid (note that this won't really happen in current implementation) 
        ///   - InvalidOAuthClientException : if the client information is invalid 
        ///   - InvalidOAuthGrantException : if the authorization Code or refresh token is invalid or expired, the 
        ///   redirect_uri does not match, or the hash Value does not match the client secret and/or Code 
        ///   - UnsupportedOAuthGrantTypeException : if the grant Type is invalid (note that this won't really happen in 
        ///   current implementation) 
        ///   - OAuthTokenException : if any other error occurred during the operation
        /// </summary>
        /// <param name="authorizationResult"> the authorization RequestResult </param>
        /// <returns> the token </returns>
        /// <exception cref="OAuthTokenException"> the o auth token exception </exception>
        /// <exception cref="JSONSerializationException"> the JSON serializer exception </exception>
        /// <exception cref="System.UriFormatException"> the URI syntax exception </exception>
        /// <exception cref="InvalidRequestException"> the invalid request exception </exception>
        public virtual Token ObtainNewToken(AuthorizationResult authorizationResult)
        {
            if (authorizationResult == null)
            {
                throw new System.ArgumentException();
            }

            // create a Map of the parameters
            Dictionary<string, string> @params = new Dictionary<string, string>();
            @params["grant_type"] = "authorization_code";
            @params["client_id"] = clientId;
            @params["code"] = authorizationResult.Code;
            @params["redirect_uri"] = redirectURL;
            @params["hash"] = getHash(authorizationResult.Code);

            // Generate the URL and then get the token
            return RequestToken(GenerateURL(tokenURL, @params));
        }
        /// <summary>
        /// Extract AuthorizationResult from the authorization response URL (i.e. the RedirectURL with the response
        /// parameters from Smartsheet OAuth server).
        /// 
        /// Exceptions: 
        ///   - IllegalArgumentException : if authorizationResponseURL is null/empty, or a malformed URL 
        ///   - AccessDeniedException : if the user has denied the authorization request 
        ///   - UnsupportedResponseTypeException : if the response Type isn't supported (note that this won't really happen in current implementation) 
        ///   - InvalidScopeException : if some of the specified scopes are invalid 
        ///   - OAuthAuthorizationCodeException : if any other error occurred during the operation
        /// </summary>
        /// <param name="authorizationResponseURL"> the authorization response URL </param>
        /// <returns> the authorization RequestResult </returns>
        /// <exception cref="UriFormatException "> the URI syntax exception </exception>
        /// <exception cref="OAuthAuthorizationCodeException"> the o auth authorization Code exception </exception>
        public virtual AuthorizationResult ExtractAuthorizationResult(string authorizationResponseURL)
        {
            Util.ThrowIfNull(authorizationResponseURL);
            Util.ThrowIfEmpty(authorizationResponseURL);

            // Get all of the parms from the URL
            Uri uri = new Uri(authorizationResponseURL);
            string query = uri.Query;
            if (String.IsNullOrEmpty(query))
            {
                throw new OAuthAuthorizationCodeException("There must be a query string in the response URL");
            }

            IDictionary<string, string> map = new Dictionary<string, string>();
            string[] @params = query.TrimStart('?').Split(new Char[] { '&' });
            for (int i = 0; i < @params.Length; i++)
            {
                int index = @params[i].IndexOf('=');
                map[@params[i].Substring(0, index)] = @params[i].Substring(index + 1);
            }

            // Check for an error response in the URL and throw it.
            if (map.ContainsKey("error") && map["error"].Length > 0)
            {
                string error = map["error"];
                if ("access_denied".Equals(error))
                {
                    throw new AccessDeniedException("Access denied.");
                }
                else if ("unsupported_response_type".Equals(error))
                {
                    throw new UnsupportedResponseTypeException("response_type must be set to \"code\".");
                }
                else if ("invalid_scope".Equals(error))
                {
                    throw new InvalidScopeException("One or more of the requested access scopes are invalid. " + "Please check the list of access scopes");
                }
                else
                {
                    throw new OAuthAuthorizationCodeException("An undefined error was returned of type: " + error);
                }
            }

            AuthorizationResult authorizationResult = new AuthorizationResult();

            if (map.ContainsKey("code"))
            {
                authorizationResult.Code = map["code"];
            }

            if (map.ContainsKey("state"))
            {
                authorizationResult.State = map["state"];
            }

            long? expiresIn = 0L;
            try
            {
                expiresIn = Convert.ToInt64(map["expires_in"]);
            }
            catch (Exception)
            {
                expiresIn = 0L;
            }
            authorizationResult.ExpiresInSeconds = expiresIn;

            return authorizationResult;
        }