public void ChangeStatus(UserStatus status, EventHandler <ReplyEventArgs> callback)
        {
            VerifyLoggedIn();

            Contract.Requires(protocol != null);

            var message = protocol.CreateMessage("me.status");

            message["status"] = status.ToString();

            protocol.SendMessage(message, (messageName, result, reply) =>
            {
                ReplyEventArgs args;
                switch (result)
                {
                case Network.ReplyResult.Success:
                    me.Update(new UserDescription()
                    {
                        Name        = me.Name,
                        DisplayName = me.DisplayName,
                        Status      = status,
                        Friend      = false,
                    });

                    var meArray = new IUser[] { me };

                    UserDetailsChange.SafeInvoke(this, new UserDetailsEventArgs(Enumerable.Empty <IUser>(), meArray));

                    args = new ReplyEventArgs(Convert.ToInt32(reply["result"]), Convert.ToString(reply["result_message"]));
                    break;

                case Network.ReplyResult.Fail:
                    args = new ReplyEventArgs((int)ResultCode.NotSent, MessageNotSentString);
                    break;

                case Network.ReplyResult.Expired:
                    args = new ReplyEventArgs((int)ResultCode.NoReply, NoReplyString);
                    break;

                default:
                    throw new Exception("This should not happen");
                }
                callback.SafeInvoke(this, args);
            });
        }
        public void LogIn(string username, string password, EventHandler <LoginReplyEventArgs> callback)
        {
            VerifyConnected();

            Contract.Requires(protocol != null);
            Contract.Requires(username != null);
            Contract.Requires(password != null);

            string hashedPassword;

            using (var algorithm = SHA256.Create())
            {
                algorithm.ComputeHash(Encoding.UTF8.GetBytes(password));

                var sb = new StringBuilder(32);
                foreach (byte b in algorithm.Hash)
                {
                    sb.Append(b.ToString("x2"));
                }
                hashedPassword = sb.ToString();
            }

            var message = protocol.CreateMessage("login");

            message["username"]       = username;
            message["password"]       = hashedPassword;
            message["initial_status"] = UserStatus.Available.ToString();

            protocol.SendMessage(message, (messageName, result, reply) =>
            {
                loggedIn = true;

                LoginReplyEventArgs args;
                switch (result)
                {
                case Network.ReplyResult.Success:
                    var resultCode    = Convert.ToInt32(reply["result"]);
                    var resultMessage = Convert.ToString(reply["result_message"]);
                    if (protocol.CheckObjectFields("login", reply, "me"))
                    {
                        me = new User(this, reply["me"].ToObject <UserDescription>());
                        userCache[me.Name] = me;
                        loggedIn           = true;

                        args = new LoginReplyEventArgs(resultCode, resultMessage, Me);
                    }
                    else
                    {
                        args = new LoginReplyEventArgs(resultCode, resultMessage, null);
                    }
                    break;

                case Network.ReplyResult.Fail:
                    args = new LoginReplyEventArgs((int)ResultCode.NotSent, MessageNotSentString, null);
                    break;

                case Network.ReplyResult.Expired:
                    args = new LoginReplyEventArgs((int)ResultCode.NoReply, NoReplyString, null);
                    break;

                default:
                    throw new Exception("This should not happen");
                }
                if (callback != null)
                {
                    callback(this, args);
                }

                // Need to detail the logged-in user
                if (result == Network.ReplyResult.Success)
                {
                    UserDetailsChange.SafeInvoke(this, new UserDetailsEventArgs(new IUser[] { Me }, Enumerable.Empty <IUser>()));
                }
            });

            log.InfoFormat("Trying to log in as {0}", username);
        }