public async Task LoginAsync(UserStatus initialStatus = UserStatus.Online)
        {

            await @lock.WriterLockAsync();

            try
            {

                if (IsLoggedIn)
                    return;

                IConnection connection = null;
                ConnectionStream stream = null;
                CommandReader reader = null;
                CommandWriter writer = null;
                ResponseTracker responseTracker = null;
                IConnectableObservable<Command> commands = null;
                IDisposable commandsDisposable = null;

                int transferCount = 0;

                SocketEndPoint endPoint = SocketEndPoint.Parse("messenger.hotmail.com:1863");

                string authTicket = null;

                while (authTicket == null)
                {

                    connection = new SocketConnection();

                    await connection.ConnectAsync(endPoint);

                    stream = new ConnectionStream(connection);

                    writer = new CommandWriter(stream);
                    reader = new CommandReader(stream, new Dictionary<string, Type> {
                        { "VER", typeof(VersionCommand) },
                        { "CVR", typeof(ClientVersionCommand) },
                        { "USR", typeof(AuthenticateCommand) },
                        { "XFR", typeof(TransferCommand) },
                        { "SYN", typeof(SynchronizeCommand) },
                        { "SBS", typeof(SbsCommand) },
                        { "MSG", typeof(MessageCommand) },
                        { "LST", typeof(UserCommand) },
                        { "LSG", typeof(GroupCommand) },
                        { "BPR", typeof(UserPropertyCommand) },
                        { "BLP", typeof(PrivacySettingCommand) },
                        { "GTC", typeof(PrivacySettingCommand) },
                        { "CHG", typeof(ChangeStatusCommand) },
                        { "UBX", typeof(BroadcastCommand) },
                        { "PRP", typeof(LocalPropertyCommand) },
                        { "NLN", typeof(UserOnlineCommand) },
                        { "ILN", typeof(InitialUserOnlineCommand) },
                        { "FLN", typeof(UserOfflineCommand) },
                        { "UUX", typeof(SendBroadcastCommand) },
                        { "NOT", typeof(NotificationCommand) },
                        { "QNG", typeof(PingCommand) },
                        { "CHL", typeof(ChallengeCommand) },
                        { "ADC", typeof(AddContactCommand) },
                        { "REM", typeof(RemoveContactCommand) },
                        { "ADG", typeof(AddGroupCommand) },
                        { "RMG", typeof(RemoveGroupCommand) },
                        { "REG", typeof(RenameGroupCommand) },  
                        { "QRY", typeof(AcceptChallengeCommand) },  
                        { "RNG", typeof(RingCommand) },
                        { "SBP", typeof(ChangeUserPropertyCommand) },
                        { "IMS", typeof(EnableIMCommand) },
                    });
                    
                    commands = reader.GetReadObservable().Publish();
                    responseTracker = new ResponseTracker(writer, commands);

                    commandsDisposable = commands.Connect();

                    var versionCommand = new VersionCommand("MSNP12");
                    var versionResponse = await responseTracker.GetResponseAsync<VersionCommand>(versionCommand, defaultTimeout);

                    if (versionResponse.Versions.Length == 0)
                        throw new ProtocolNotAcceptedException();

                    var clientVersionCommand = new ClientVersionCommand
                    {
                        LocaleId = "0x0409",
                        OsType = "winnt",
                        OsVersion = "5.0",
                        Architecture = "1386",
                        LibraryName = "MSMSGS",
                        ClientVersion = "5.0.0482",
                        ClientName = "WindowsMessenger",
                        LoginName = credentials.LoginName,
                    };

                    await responseTracker.GetResponseAsync<ClientVersionCommand>(clientVersionCommand, defaultTimeout);

                    var userCommand = new AuthenticateCommand("TWN", "I", credentials.LoginName);
                    var userResponse = await responseTracker.GetResponseAsync(userCommand, new Type[] { typeof(AuthenticateCommand), typeof(TransferCommand) }, defaultTimeout);

                    if (userResponse is AuthenticateCommand)
                    {
                        authTicket = (userResponse as AuthenticateCommand).Argument;
                    }

                    else if (userResponse is TransferCommand)
                    {
                        
                        TransferCommand transferResponse = userResponse as TransferCommand;

                        if (transferCount > 3)
                            throw new InvalidOperationException("The maximum number of redirects has been reached.");

                        transferCount++;

                        endPoint = SocketEndPoint.Parse(transferResponse.Host);

                        commandsDisposable.Dispose();

                        reader.Close();
                        writer.Close();
                        connection.Dispose();

                    }

                }

                PassportAuthentication auth = new PassportAuthentication();

                string authToken = await auth.GetToken(credentials.LoginName, credentials.Password, authTicket);

                var authCommand = new AuthenticateCommand("TWN", "S", authToken);
                var authResponse = await responseTracker.GetResponseAsync<AuthenticateCommand>(authCommand, defaultTimeout);

                var synCommand = new SynchronizeCommand(syncTimeStamp1 ?? "0", syncTimeStamp2 ?? "0");
                var synResponse = await responseTracker.GetResponseAsync<SynchronizeCommand>(synCommand, defaultTimeout);

                IDisposable syncCommandsSubscription = null;
                List<Command> syncCommands = null;

                if (synResponse.TimeStamp1 != syncTimeStamp1 || synResponse.TimeStamp2 != syncTimeStamp2)
                {

                    syncCommands = new List<Command>();

                    Type[] syncTypes = new Type[] { 
                        typeof(MessageCommand), 
                        typeof(UserCommand), 
                        typeof(GroupCommand), 
                        typeof(LocalPropertyCommand), 
                        typeof(PrivacySettingCommand),
                    };

                    syncCommandsSubscription = commands
                        .Where(c => syncTypes.Contains(c.GetType()))
                        .Catch(Observable.Empty<Command>())
                        .Subscribe(c => syncCommands.Add(c));

                    //if we're expecting users/groups, wait for them before we proceed
                    if (synResponse.UserCount + synResponse.GroupCount > 0)
                    {

                        await commands
                            .Where(c => c is UserCommand || c is GroupCommand)
                            .Take(synResponse.UserCount + synResponse.GroupCount)
                            .Timeout(defaultTimeout);

                    }

                }

                UserCapabilities capabilities = 0;
                MSNObject displayPicture = MSNObject.Empty;

                if (LocalUser != null)
                {
                    capabilities = LocalUser.Capabilities;
                    displayPicture = LocalUser.DisplayPicture;
                }

                Command changeStatusCommand = new ChangeStatusCommand(User.StatusToString(UserStatus.Online), (uint)capabilities, displayPicture != MSNObject.Empty ? displayPicture.ToString() : "0");
                await responseTracker.GetResponseAsync(changeStatusCommand, defaultTimeout);

                if (syncCommandsSubscription != null)
                    syncCommandsSubscription.Dispose();

                this.writer = writer;
                this.reader = reader;
                this.stream = stream;
                this.connection = connection;
                this.responseTracker = responseTracker;
                this.commands = commands;
                this.commandsDisposable = commandsDisposable;

                if (LocalUser == null)
                {
                    LocalUser = new LocalUser(this, credentials.LoginName);
                    userCache.Add(credentials.LoginName, new WeakReference(LocalUser));
                }

                LocalUser.Status = initialStatus;

                SyncEvents syncEvents = null;

                if (syncCommands != null)
                {
                    syncTimeStamp1 = synResponse.TimeStamp1;
                    syncTimeStamp2 = synResponse.TimeStamp2;

                    syncEvents = ProcessSyncCommands(syncCommands);
                }

                var commandsSafe = commands
                    .Catch<Command, ConnectionErrorException>(tx => Observable.Empty<Command>());

                commandsSafe.OfType<MessageCommand>().Subscribe(cmd => HandleMessages(cmd, connection));
                commandsSafe.OfType<RingCommand>().Subscribe(cmd => HandleRings(cmd, connection));
                commandsSafe.OfType<BroadcastCommand>().Subscribe(cmd => HandleBroadcasts(cmd, connection));
                commandsSafe.OfType<NotificationCommand>().Subscribe(cmd => HandleNotifications(cmd, connection));
                commandsSafe.OfType<AddContactCommand>().Subscribe(cmd => HandleNewUsers(cmd, connection));
                commandsSafe.OfType<OutCommand>().Subscribe(cmd => HandleOut(cmd, connection));
                commandsSafe.OfType<ChallengeCommand>().Subscribe(cmd => HandleChallenges(cmd, connection));
                commandsSafe.OfType<UserOnlineCommand>().Subscribe(cmd => HandleOnlineUsers(cmd, connection));
                commandsSafe.OfType<InitialUserOnlineCommand>().Subscribe(cmd => HandleOnlineUsers(cmd, connection));
                commandsSafe.OfType<UserOfflineCommand>().Subscribe(cmd => HandleOfflineUsers(cmd, connection));

                connection.Error += connection_Error;

                IsLoggedIn = true;

                OnLoggedIn();

                OnUserStatusChanged(new UserStatusEventArgs(LocalUser, initialStatus, UserStatus.Offline, true));

                if (syncEvents != null)
                    RaiseSyncEvents(syncEvents);


            }
            finally
            {
                @lock.WriterRelease();
            }

        }