/// <summary>
        /// Remove one RB Conversation (by ID) from the ViewModel
        /// </summary>
        /// <param name="id">Rainbow Conversation's ID</param>
        public void RemoveRbConversationFromModel(String id)
        {
            ConversationLight conversation = GetConversationById(id);

            lock (lockObservableConversations)
                Conversations.Remove(conversation);
        }
        /// <summary>
        /// Add one Conversation Model in the ViewModel
        /// </summary>
        /// <param name="rbConversation">a Rainbow Conversation object</param>
        private void AddConversationToModel(ConversationLight newConversation)
        {
            if (newConversation != null)
            {
                // First remove this conversation if already in the model
                RemoveRbConversationFromModel(newConversation.Id);

                lock (lockObservableConversations)
                {
                    Boolean itemAdded = false;
                    int     nb        = Conversations.Count;
                    for (int i = 0; i < nb; i++)
                    {
                        if (newConversation.LastMessageDateTime.ToUniversalTime() > Conversations[i].LastMessageDateTime.ToUniversalTime())
                        {
                            Conversations.Insert(i, newConversation);
                            itemAdded = true;
                            break;
                        }
                    }
                    if (!itemAdded)
                    {
                        Conversations.Add(newConversation);
                    }

                    // Check if we nee to update the view due to DateTime purpose
                    CheckIfUpdateModelForDateTimePurpose(newConversation.LastMessageDateTime);
                }
            }
        }
        private void AvatarPool_BubbleAvatarChanged(object sender, Rainbow.Events.IdEventArgs e)
        {
            ConversationLight conversation = GetConversationByPeerId(e.Id);

            if (conversation != null)
            {
                conversation.AvatarSource = Helper.GetConversationAvatarImageSource(conversation);
            }
        }
        /// <summary>
        /// Update the ViewModel using the Presence Level of the specified JID
        /// </summary>
        /// <param name="jid">Jid of the contact</param>
        /// <param name="presence">Presence of the contact</param>
        public void UpdateModelUsingPresence(String jid, Presence presence)
        {
            ConversationLight conversation = GetConversationByJid(jid);

            if (conversation != null)
            {
                conversation.PresenceSource = InstantMessaging.Helpers.Helper.GetPresenceSourceFromPresence(presence);
            }
        }
        public Boolean UpdateConversationNameByPeerId(string peerId, String name)
        {
            Boolean result = false;

            ConversationLight conversation = GetConversationByPeerId(peerId);

            if (conversation != null)
            {
                result            = true;
                conversation.Name = name;
            }
            return(result);
        }
        private void AvatarPool_ContactAvatarChanged(object sender, Rainbow.Events.IdEventArgs e)
        {
            ConversationLight conversation = GetConversationByPeerId(e.Id);

            if (conversation != null)
            {
                log.Debug("[AvatarPool_ContactAvatarChanged] - contactId:[{0}] - conversationId:[{1}]", e.Id, conversation.Id);
                conversation.AvatarSource = Helper.GetConversationAvatarImageSource(conversation);
            }
            else
            {
                log.Debug("[AvatarPool_ContactAvatarChanged] - Avatar contactId:[{0}] but no conversation found ...", e.Id);
            }
        }
        private ConversationLight GetConversationByPeerId(String peerId)
        {
            ConversationLight conversationFound = null;

            lock (lockObservableConversations)
            {
                foreach (ConversationLight conversation in Conversations)
                {
                    if (conversation.PeerId == peerId)
                    {
                        conversationFound = conversation;
                        break;
                    }
                }
            }
            return(conversationFound);
        }
        void ConversationsListView_OnItemTapped(object sender, ItemTappedEventArgs e)
        {
            if (e.Item != null)
            {
                if (e.Item is ConversationLight)
                {
                    ConversationLight conversation = e.Item as ConversationLight;

                    if (conversation != null)
                    {
                        ShowConversationStreamPage(conversation.Id);
                    }
                }
            }

            ConversationsListView.SelectedItem = null;
        }
        private void SetConversationAvatar(ConversationLight conversation)
        {
            if (conversation == null)
            {
                return;
            }

            String filePath = null;

            try
            {
                if (conversation.Type == Rainbow.Model.Conversation.ConversationType.User)
                {
                    filePath = avatarPool.GetContactAvatarPath(conversation.PeerId);
                }
                else if (conversation.Type == Rainbow.Model.Conversation.ConversationType.Room)
                {
                    filePath = avatarPool.GetBubbleAvatarPath(conversation.PeerId);
                }
            }
            catch (Exception exc)
            {
                log.Warn("[SetConversationAvatar] PeerId:[{0}] - exception occurs to create avatar:[{1}]", conversation.PeerId, Util.SerializeException(exc));
            }

            if (!String.IsNullOrEmpty(filePath))
            {
                if (File.Exists(filePath))
                {
                    log.Debug("[SetConversationAvatar] - file used - filePath:[{0}] - PeerId:[{1}]", filePath, conversation.PeerId);
                    try
                    {
                        conversation.AvatarSource = ImageSource.FromFile(filePath);
                    }
                    catch (Exception exc)
                    {
                        log.Warn("[SetConversationAvatar] PeerId:[{0}] - exception occurs to display avatar[{1}]", conversation.PeerId, Util.SerializeException(exc));
                    }
                }
                else
                {
                    log.Warn("[SetConversationAvatar] - file not found - filePath:[{0}] - PeerId:[{1}]", filePath, conversation.PeerId);
                }
            }
        }
        public void UpdateRBConversationToModel(Rainbow.Model.Conversation rbConversation)
        {
            log.Debug("[UpdateRBConversationToModel] - IN");
            if (rbConversation != null)
            {
                // Now add it to the model
                ConversationLight newConversation = Helper.GetConversationFromRBConversation(rbConversation);
                ConversationLight oldConversation = GetConversationById(newConversation.Id);

                if (oldConversation == null)
                {
                    AddConversationToModel(newConversation);
                }
                else
                {
                    lock (lockObservableConversations)
                    {
                        oldConversation.Id             = newConversation.Id;
                        oldConversation.Jid            = newConversation.Jid;
                        oldConversation.PeerId         = newConversation.PeerId;
                        oldConversation.Name           = newConversation.Name;
                        oldConversation.PresenceSource = newConversation.PresenceSource;

                        oldConversation.NbMsgUnread          = newConversation.NbMsgUnread;
                        oldConversation.NbMsgUnreadIsVisible = (oldConversation.NbMsgUnread > 0) ? "True" : "False";

                        oldConversation.LastMessage         = newConversation.LastMessage;
                        oldConversation.LastMessageDateTime = newConversation.LastMessageDateTime;
                        oldConversation.MessageTimeDisplay  = newConversation.MessageTimeDisplay;

                        int oldIndex = 0;
                        int newIndex = -1;

                        int nb = Conversations.Count;

                        // Get old Index
                        for (int i = 0; i < nb; i++)
                        {
                            if (Conversations[i].Id == oldConversation.Id)
                            {
                                oldIndex = i;
                                break;
                            }
                        }

                        // Try to update order only if are not already the first row
                        if (oldIndex != 0)
                        {
                            // Get new Index
                            for (int i = 0; i < nb; i++)
                            {
                                if (oldConversation.LastMessageDateTime.ToUniversalTime() > Conversations[i].LastMessageDateTime.ToUniversalTime())
                                {
                                    newIndex = i;
                                    break;
                                }
                            }

                            // If we have found no new index, we need to move it to the end
                            if (newIndex == -1)
                            {
                                newIndex = nb - 1;
                            }

                            // Update order only if indexes are different
                            if (oldIndex != newIndex)
                            {
                                Conversations.Move(oldIndex, newIndex);
                            }

                            log.Debug("[UpdateRBConversationToModel] MOVED - oldIndex:[{0}] - newIndex:[{1}] - nb:[{2}] - TopDate:[{3}] - LastMessageDateTime:[{4}]", oldIndex, newIndex, nb, Conversations[0].LastMessageDateTime.ToString("o"), oldConversation.LastMessageDateTime.ToString("o"));
                        }
                        else
                        {
                            log.Debug("[UpdateRBConversationToModel] NOT MOVED - oldIndex:[{0}] - nb:[{1}] - TopDate:[{2}] - LastMessageDateTime:[{3}]", oldIndex, nb, Conversations[0].LastMessageDateTime.ToString("o"), oldConversation.LastMessageDateTime.ToString("o"));
                        }
                    }

                    // Check if we need to update the view due to DateTime purpose
                    CheckIfUpdateModelForDateTimePurpose(newConversation.LastMessageDateTime);
                }
            }
            log.Debug("[UpdateRBConversationToModel] - OUT");
        }
        }                                                      // Need to be public - Used as Binding from XAML

        #endregion XAML BINDING ELEMENTS

        #region PUBLIC METHODS

        /// <summary>
        /// Constructor
        /// </summary>
        public ConversationStreamViewModel(String conversationId)
        {
            // Get Xamarin Application
            XamarinApplication = (InstantMessaging.App)Xamarin.Forms.Application.Current;

            // Manage event(s) from FilePool
            filePool = FilePool.Instance;
            filePool.FileDescriptorAvailable += FilePool_FileDescriptorAvailable;
            filePool.ThumbnailAvailable      += FilePool_ThumbnailAvailable;

            // Manage event(s) from AvatarPool
            avatarPool = AvatarPool.Instance;
            avatarPool.ContactAvatarChanged += AvatarPool_ContactAvatarChanged;
            avatarPool.BubbleAvatarChanged  += AvatarPool_BubbleAvatarChanged;

            // Manage event(s) from InstantMessaging
            XamarinApplication.RbInstantMessaging.MessageReceived   += RbInstantMessaging_MessageReceived;
            XamarinApplication.RbInstantMessaging.ReceiptReceived   += RbInstantMessaging_ReceiptReceived;
            XamarinApplication.RbInstantMessaging.MessagesAllRead   += RbInstantMessaging_MessagesAllRead;
            XamarinApplication.RbInstantMessaging.UserTypingChanged += RbInstantMessaging_UserTypingChanged;

            // Manage event(s) from Contacts
            XamarinApplication.RbContacts.ContactAdded       += RbContacts_ContactAdded;
            XamarinApplication.RbContacts.ContactInfoChanged += RbContacts_ContactInfoChanged;

            currentContactJid = XamarinApplication.RbContacts.GetCurrentContactJid();

            // Store conversation Id
            this.conversationId = conversationId;

            // Create default ConversationStream object
            ConversationStream = new ConversationStream
            {
                LoadingIndicatorIsVisible = "False",
                ListViewIsEnabled         = "True"
            };

            // Create default MessagesList  object
            MessagesList = new ObservableRangeCollection <InstantMessaging.Model.Message>();

            if (string.IsNullOrEmpty(this.conversationId))
            {
                //TODO
                Conversation = new ConversationLight();
            }
            else
            {
                // Get Rainbow Conversation object
                rbConversation = XamarinApplication.RbConversations.GetConversationByIdFromCache(this.conversationId);

                // Get Conversation Model Object using Rainbow Conversation
                Conversation = Helper.GetConversationFromRBConversation(rbConversation);
                if (Conversation != null)
                {
                    Conversation.AvatarSource = Helper.GetConversationAvatarImageSource(Conversation);
                }

                // Get Messages
                List <Rainbow.Model.Message> rbMessagesList = XamarinApplication.RbInstantMessaging.GetAllMessagesFromConversationIdFromCache(this.conversationId);
                if (rbMessagesList != null)
                {
                    if (rbMessagesList.Count > 0)
                    {
                        AddToModelRbMessages(rbMessagesList);
                    }

                    if (rbMessagesList.Count < NB_MESSAGE_LOADED_BY_ROW)
                    {
                        LoadMoreMessages();
                    }
                }
                else
                {
                    LoadMoreMessages();
                }
            }
        }