/// <summary>
        /// Authenticates this <see cref="Budgie.TwitterClient"/> by requesting an OAuth access token and token_secret from Twitter.
        /// </summary>
        /// <param name="requestToken">An OAuth request token.</param>
        /// <param name="verifier">A verification PIN obtained by the user from Twitter.</param>
        /// <returns>An instance of <see cref="TwitterAccessToken"/> with the tokens returned from Twitter.</returns>
        public Task <TwitterAccessToken> AuthenticateAsync(TwitterRequestToken requestToken, string verifier)
        {
            return(HttpPostAsync("/oauth/access_token", "oauth_verifier=" + verifier, requestToken).ContinueWith(t =>
            {
                if (t.IsFaulted)
                {
                    return null;
                }

                var response = t.Result as HttpWebResponse;
                if (response.StatusCode != HttpStatusCode.OK)
                {
                    return null;
                }

                string responseString = response.GetContentString();
                if (responseString.IsNullOrWhiteSpace())
                {
                    return null;
                }

                var result = new TwitterAccessToken();
                foreach (var i in responseString.Split('&'))
                {
                    var kv = i.Split('=');
                    if (kv.Length < 2)
                    {
                        continue;
                    }

                    if (kv[0] == "oauth_token")
                    {
                        result.Token = kv[1];
                    }
                    else if (kv[0] == "oauth_token_secret")
                    {
                        result.Secret = kv[1];
                    }
                    else if (kv[0] == "user_id")
                    {
                        result.UserId = kv[1];
                    }
                    else if (kv[0] == "screen_name")
                    {
                        result.ScreenName = kv[1];
                    }
                }

                if (!result.IsValid)
                {
                    return null;
                }

                return (m_accessToken = result);
            }));
        }
        // mad props to http://www.codeproject.com/Articles/247336/Twitter-OAuth-authentication-using-Net

        /// <summary>
        /// Gets an AOuth request token from Twitter.
        /// </summary>
        /// <returns>A valid <see cref="TwitterRequestToken"/>.</returns>
        public Task <TwitterRequestToken> GetRequestTokenAsync(Uri callbackUri = null)
        {
            // this is very similar to all the other OAuth requests except that at this point we don't
            // have any tokens to pass along with the request, so for now I'm keeping this logic separate.

            var request = (HttpWebRequest)WebRequest.Create(new Uri(BaseUri, "/oauth/request_token"));

            request.Method      = "POST";
            request.ContentType = "application/x-www-form-urlencoded";

            var oauth_nonce     = Convert.ToBase64String(UTF8Encoding.UTF8.GetBytes(DateTime.Now.Ticks.ToString()));
            var timeSpan        = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
            var oauth_timestamp = Convert.ToInt64(timeSpan.TotalSeconds).ToString();
            var resource_url    = request.RequestUri.GetComponents(UriComponents.SchemeAndServer | UriComponents.Path, UriFormat.Unescaped);

            var parms = new SortedDictionary <string, string>
            {
                { "oauth_consumer_key", m_consumerKey },
                { "oauth_nonce", oauth_nonce },
                { "oauth_signature_method", "HMAC-SHA1" },
                { "oauth_timestamp", oauth_timestamp },
                { "oauth_version", "1.0" },
            };

            if (callbackUri != null)
            {
                parms.Add("oauth_callback", Uri.EscapeDataString(callbackUri.ToString()));
            }

            if (!String.IsNullOrEmpty(request.RequestUri.Query))
            {
                foreach (var p in request.RequestUri.Query.Substring(1).Split('&'))
                {
                    var kv = p.Split('=');
                    parms.Add(kv[0], kv[1]);
                }
            }

            var baseString = parms.First().Key + "=" + parms.First().Value;

            foreach (var p in parms.Skip(1))
            {
                baseString += "&" + p.Key + "=" + p.Value;
            }

            baseString = request.Method + "&" + Uri.EscapeDataString(resource_url) + "&" + Uri.EscapeDataString(baseString);

            var compositeKey = Uri.EscapeDataString(m_consumerSecret) + "&";

            var oauth_signature = m_platformAdaptor.ComputeSha1Hash(compositeKey, baseString);
            //using (var hasher = new HMACSHA1(UTF8Encoding.UTF8.GetBytes(compositeKey)))
            //{
            //    oauth_signature = Convert.ToBase64String(hasher.ComputeHash(UTF8Encoding.UTF8.GetBytes(baseString)));
            //}

            string authHeader;

            if (callbackUri == null)
            {
                authHeader = string.Format("OAuth oauth_nonce=\"{0}\", oauth_signature_method=\"HMAC-SHA1\", oauth_timestamp=\"{1}\", oauth_consumer_key=\"{2}\", oauth_signature=\"{3}\", oauth_version=\"1.0\"",
                                           Uri.EscapeDataString(oauth_nonce),
                                           Uri.EscapeDataString(oauth_timestamp),
                                           Uri.EscapeDataString(m_consumerKey),
                                           Uri.EscapeDataString(oauth_signature));
            }
            else
            {
                authHeader = string.Format("OAuth oauth_callback=\"{0}\", oauth_nonce=\"{1}\", oauth_signature_method=\"HMAC-SHA1\", oauth_timestamp=\"{2}\", oauth_consumer_key=\"{3}\", oauth_signature=\"{4}\", oauth_version=\"1.0\"",
                                           Uri.EscapeDataString(callbackUri.ToString()),
                                           Uri.EscapeDataString(oauth_nonce),
                                           Uri.EscapeDataString(oauth_timestamp),
                                           Uri.EscapeDataString(m_consumerKey),
                                           Uri.EscapeDataString(oauth_signature));
            }
            request.Headers["Authorization"] = authHeader;

            return(request.GetResponseAsync(Timeout).ContinueWith(t =>
            {
                if (t.IsFaulted)
                {
                    return null;
                }

                var response = t.Result as HttpWebResponse;
                if (response.StatusCode != HttpStatusCode.OK)
                {
                    return null;
                }

                string responseString = response.GetContentString();
                if (responseString.IsNullOrWhiteSpace())
                {
                    return null;
                }

                var result = new TwitterRequestToken(BaseUri);
                foreach (var i in responseString.Split('&'))
                {
                    var kv = i.Split('=');
                    if (kv.Length < 2)
                    {
                        continue;
                    }

                    if (kv[0] == "oauth_callback_confirmed")
                    {
                        if (kv[1] != "true")
                        {
                            return null;
                        }
                    }
                    else if (kv[0] == "oauth_token")
                    {
                        result.Token = kv[1];
                    }
                    else if (kv[0] == "oauth_token_secret")
                    {
                        result.Secret = kv[1];
                    }
                }

                return result.IsValid ? result : null;
            }));
        }