/// <summary>
        /// Erzeugt eine neue Konversation. Es wird ein Request an den Server abgesetzt, um die 
        /// Konversation auf dem Server anzulegen. Ist das Anlegen erfolgreich, wird auch lokal
        /// eine Kopie der Konversation angelegt.
        /// </summary>
        /// <param name="groupId">Die Id der Gruppe, in der eine neue Konversation angelegt werden soll.</param>
        /// <param name="newConversation">Das Objekt mit den Daten der neuen Konversation.</param>
        /// <returns>Liefert true, wenn das Anlegen erfolgreich war. Liefert false, wenn das Anlegen aufgrund
        ///     fehlender Daten oder sonstigen Validierungsfehlern nicht ausgeführt werden konnte.</returns>
        /// <exception cref="ClientException">Wirft ClientException, wenn das Anlegen fehlschlägt, oder 
        ///     vom Server abgelehnt wurde.</exception>
        public async Task<bool> CreateConversationAsync(int groupId, Conversation newConversation)
        {
            if (newConversation == null)
                return false;

            // Führe Validierung der Daten durch.
            clearValidationErrors();
            newConversation.ClearValidationErrors();
            newConversation.ValidateAll();
            if (newConversation.HasValidationErrors())
            {
                reportValidationErrors(newConversation.GetValidationErrors());
                return false;
            }

            // Parse Konversation zu JSON.
            string jsonContent = jsonParser.ParseConversationToJson(newConversation);
            if (jsonContent == null)
            {
                Debug.WriteLine("CreateConversationAsync: Failed to create a json object.");
                throw new ClientException(ErrorCodes.JsonParserError, "Failed to create json document.");
            }

            // Setze Request an den Server ab.
            string serverResponse = null;
            try
            {
                serverResponse = await groupAPI.SendCreateConversationRequest(
                    getLocalUser().ServerAccessToken,
                    groupId,
                    jsonContent);
            }
            catch (APIException ex)
            {
                Debug.WriteLine("CreateConversationAsync: Request failed. Error code is {0}.", ex.ErrorCode);

                handleGroupRelatedErrors(ex.ErrorCode, groupId, null, null);

                throw new ClientException(ex.ErrorCode, ex.Message);
            }

            if (serverResponse != null)
            {
                // Extrahiere Conversation aus der Serverantwort.
                Conversation createdConv = jsonParser.ParseConversationFromJson(serverResponse);

                if (createdConv != null)
                {
                    // Speichere die Konversation noch lokal ab.
                    bool successful = StoreConversation(groupId, createdConv);
                    if (!successful)
                        await UpdateUserDataAndStoreConversationAsync(groupId, createdConv);
                }
                else
                {
                    throw new ClientException(ErrorCodes.JsonParserError, "Failed to parse response document of server.");
                }
            }

            return true;
        }
        /// <summary>
        /// Speichere die Konversation zu der angegebenen Gruppe in den lokalen Datensätzen ab.
        /// </summary>
        /// <param name="groupId">Die Id der Gruppe, zu der die Konversation gehört.</param>
        /// <param name="conversation">Die Daten der Konversation als Objekt vom Typ Conversation.</param>
        /// <returns>Liefert true, wenn die Speicherung erfolgreich ist. Liefert false, wenn Speicherung
        ///     aufgrund fehlender Datensätze (z.B. Administrator-Nutzer) fehlschlägt.</returns>
        /// <exception cref="ClientException">Wirft ClientException, wenn Speicherung aufgrund eines
        ///     Datenbankfehlers fehlschlägt.</exception>
        public bool StoreConversation(int groupId, Conversation conversation)
        {
            try
            {
                // Prüfe, ob Gruppe lokal vorhanden ist.
                if (!groupDBManager.IsGroupStored(groupId))
                {
                    // Kann Konversation so nicht einfügen.
                    return false;
                }

                // Prüfe zunächst, ob der Nutzer, der als Admin eingetragen ist, auch lokal gespeichert ist.
                if (!userController.IsUserLocallyStored(conversation.AdminId))
                {
                    // Kann Konversation so nicht einfügen.
                    return false;
                }

                // Füge Konversation ein.
                groupDBManager.StoreConversation(groupId, conversation);
            }
            catch (DatabaseException ex)
            {
                Debug.WriteLine("StoreConversation: Storing conversation failed.");
                throw new ClientException(ErrorCodes.LocalDatabaseException, ex.Message);
            }

            return true;
        }
        /// <summary>
        /// Aktualisiert den Datensatz der angegebenen Konversation in den lokalen Datensätzen.
        /// </summary>
        /// <param name="groupId">Die Id der Gruppe, zu der diese Konversation gehört.</param>
        /// <param name="updatedConversation">Objekt vom Typ Conversation mit den aktualisierten Daten der Konversation.</param>
        /// <returns>Liefert true, wenn die Aktualisierung erfolgreich war. Liefert false, wenn die Aktualisierung aufgrund
        ///     fehlender Daten (z.B. lokaler Nutzerdatensatz des Admin) nicht durchgeführt werden kann.</returns>
        /// <exception cref="ClientException">Wirft ClientException, wenn Aktualisierung aufgrund eines
        ///     unerwarteten Fehlers fehlschlägt.</exception>
        public bool UpdateConversation(int groupId, Conversation updatedConversation)
        {
            try
            {
                if (!userController.IsUserLocallyStored(updatedConversation.AdminId) || 
                    !IsActiveParticipant(groupId, updatedConversation.AdminId))
                {
                    // Fehlende Daten.
                    Debug.WriteLine("UpdateConversation: Cannot perform update due to missing reference data.");
                    return false;
                }
                if (!groupDBManager.IsGroupStored(groupId))
                {
                    // Fehlender Gruppendatensatz.
                    Debug.WriteLine("UpdateConversation: Cannot perform update due to missing group dataset.");
                    return false;
                }

                groupDBManager.UpdateConversation(updatedConversation);
            }
            catch (DatabaseException ex)
            {
                Debug.WriteLine("UpdateConversation: Failed to update the conversation.");
                throw new ClientException(ErrorCodes.LocalDatabaseException, ex.Message);
            }

            return true;
        }
        /// <summary>
        /// Fragt zunächst für die Speicherung relevante Daten vom Server ab, z.B. die aktuellen
        /// Teilnehmer, so dass die aktuellesten Daten vorhanden sind. Speichert anschließend die 
        /// Konversation lokal ab.
        /// </summary>
        /// <param name="groupId">Die Id der Gruppe, für die die Konversation gespeichert wird.</param>
        /// <param name="conversation">Die Daten der Konversation in Form eines Objekts des Typs Conversation.</param>
        /// <exception cref="ClientException">Wirft ClientException, wenn Abruf der Informationen oder Speicherung
        ///     fehlschlägt.</exception>
        public async Task UpdateUserDataAndStoreConversationAsync(int groupId, Conversation conversation)
        {
            // Frage zunächst die Teilnehmer der Gruppe ab.
            List<User> participants = await GetParticipantsOfGroupAsync(groupId, false);

            // Speichere die fehlenden Nutzer ab.
            userController.StoreUsersLocally(participants);

            // Speichere nun Konversation lokal ab.
            bool successful = StoreConversation(groupId, conversation);
            if (!successful)
            {
                Debug.WriteLine("UpdateUserDataAndStoreConversationAsync: Still failed to store conversation.");
                Debug.WriteLine("UpdateUserDataAndStoreConversationAsync: Trying to store conversation with dummy user admin.");
                conversation.AdminId = 0;
                successful = StoreConversation(groupId, conversation);
                if (!successful)
                    throw new ClientException(ErrorCodes.ConversationStorageFailedDueToMissingAdmin, "Couldn't store conversation due "
                        + "to missing conversation administrator.");
            }

        }
        /// <summary>
        /// Bereitet ein Objekt vom Typ Conversation vor, welches alle Properties enthält, die sich geändert haben.
        /// Die Methode bekommt eine alte Version eines Conversation Objekts und eine neue Version und ermittelt 
        /// dann die Properties, die eine Aktualisierung erhalten haben und schreibt diese in eine neue Conversation
        /// Instanz. Die von der Methode zurückgelieferte Conversation Instanz kann dann direkt für die Aktualisierung auf
        /// dem Server verwendet werden. Achtung: Hat sich überhaupt keine Property geändert, so gibt die Methode null zurück.        
        /// </summary>
        /// <param name="oldConversation">Das Conversation Objekt vor der Aktualisierung.</param>
        /// <param name="newConversation">Das Channel Objekt mit den aktuellen Werten.</param>
        /// <returns>Ein Objekt der Klasse Conversation, bei dem die Properties, die sich geändert haben, mit den
        ///     aktualisierten Werten gefüllt sind.</returns>
        private Conversation prepareUpdatableConversationInstance(Conversation oldConversation, Conversation newConversation)
        {
            bool hasChanged = false;
            Conversation updatedConversation = new Conversation();

            // Vergleiche Properties.
            if (oldConversation.Title != newConversation.Title)
            {
                hasChanged = true;
                updatedConversation.Title = newConversation.Title;
            }

            if (newConversation.IsClosed.HasValue && 
                oldConversation.IsClosed != newConversation.IsClosed)
            {
                hasChanged = true;
                updatedConversation.IsClosed = newConversation.IsClosed;
            }

            // Prüfe, ob sich überhaupt eine Property geändert hat.
            if (!hasChanged)
            {
                Debug.WriteLine("No Property of conversation has been updated. Method will return null.");
                updatedConversation = null;
            }

            return updatedConversation;
        }
        /// <summary>
        /// Führe eine Aktualisierung der Konversationsdaten durch. Es wird ein Request an den Server 
        /// abgesetzt, um die Daten der Konversation zu aktualisieren. Ist dieser erfolgreich, so werden die 
        /// Daten auch lokal aktualisiert.
        /// </summary>
        /// <param name="groupId">Die Id der Gruppe, zu der die Konversation gehört.</param>
        /// <param name="oldConversation">Das Conversation Objekt vor der Aktualisierung.</param>
        /// <param name="newConversation">Das Conversation Objekt nach der Aktualisierung.</param>
        /// <returns>Liefer true, wenn die Aktualisierung erfolgreich war. Liefert false, wenn die Konversation 
        ///     aufgrund einer fehlenden Datenvalidierung nicht aktualisiert werden konnte.</returns>
        /// <exception cref="ClientException">Wirft ClientException, wenn Aktualisierung fehlschlägt oder 
        ///     vom Server abgelehnt wurde.</exception>
        public async Task<bool> UpdateConversationAsync(int groupId, Conversation oldConversation, Conversation newConversation)
        {
            if (oldConversation == null || newConversation == null)
                return false;

            // Führe Validierung der neuen Daten durch.
            clearValidationErrors();
            newConversation.ClearValidationErrors();
            newConversation.ValidateAll();
            if (newConversation.HasValidationErrors())
            {
                reportValidationErrors(newConversation.GetValidationErrors());
                return false;
            }

            // Erstelle ein Objekt für die Aktualisierung, welches die Daten enthält, die aktualisiert werden müssen.
            Conversation updatableConversationObj = prepareUpdatableConversationInstance(oldConversation, newConversation);

            if (updatableConversationObj == null)
            {
                // Keine Aktualisierung notwendig.
                return false;
            }

            // Erstelle JSON Dokument für die Aktualisierung.
            string jsonContent = jsonParser.ParseConversationToJson(updatableConversationObj);
            if (jsonContent == null)
            {
                Debug.WriteLine("UpdateConversationAsync: Failed to create json document.");
                throw new ClientException(ErrorCodes.JsonParserError, "Failed to generate json document.");
            }

            // Setze Request an den Server ab.
            string serverResponse = null;
            try
            {
                serverResponse = await groupAPI.SendUpdateConversationRequest(
                    getLocalUser().ServerAccessToken,
                    groupId,
                    oldConversation.Id,
                    jsonContent);
            }
            catch (APIException ex)
            {
                Debug.WriteLine("UpdateConversationAsync: Request failed. Error code: {0}.", ex.ErrorCode);

                handleGroupRelatedErrors(ex.ErrorCode, groupId, oldConversation.Id, null);

                // Bilde ab auf ClientException.
                throw new ClientException(ex.ErrorCode, ex.Message);
            }

            if (serverResponse != null)
            {
                // Parse Server Antwort.
                Conversation updatedConv = jsonParser.ParseConversationFromJson(serverResponse);

                if (updatedConv != null)
                {
                    // Aktualisiere lokalen Datensatz.
                    bool successful = UpdateConversation(groupId, updatedConv);
                    if (!successful)
                    {
                        Debug.WriteLine("UpdateConversationAsync: Failed to update group locally. Trying to fix it by synchronizing participants info.");
                        await SynchronizeGroupParticipantsAsync(groupId);

                        // Versuche es erneut. Diesmal ohne Fehlerbehandlung.
                        successful = UpdateConversation(groupId, updatedConv);
                        if (!successful)
                            throw new ClientException(ErrorCodes.LocalDatabaseException, "Couldn't update conversation.");
                    }
                }
                else
                {
                    throw new ClientException(ErrorCodes.JsonParserError, "Error parsing the response document from the server.");
                }
            }

            return true;
        }
        /// <summary>
        /// Ausführen des Befehls zum Ändern einer bestehenden Konversation.
        /// </summary>
        private async Task executeEditConversationCommandAsync()
        {
            try
            {
                displayIndeterminateProgressIndicator("AddAndEditConversationEditConversationStatus");

                Conversation newConversation = new Conversation()
                {
                    Title = EnteredTitle,
                    IsClosed = IsClosedChecked
                };

                bool successful = await groupController.UpdateConversationAsync(
                    CorrespondingGroup.Id,
                    EditableConversation,
                    newConversation);

                if (successful)
                {
                    if (_navService.CanGoBack())
                    {
                        _navService.GoBack();
                    }
                }
            }
            catch (ClientException ex)
            {
                Debug.WriteLine("executeEditConversationCommandAsync: Failed to edit the conversation.");
                displayError(ex.ErrorCode);
            }
            finally
            {
                hideIndeterminateProgressIndicator();
            }
        }
        /// <summary>
        /// Parse ein Objekt der Klasse Conversation zu einem JSON Dokument.
        /// </summary>
        /// <param name="conversation">Die umzuwandelnde Conversation Instanz.</param>
        /// <returns>Das JSON Dokument als String, oder null, falls die Umwandlung fehlgeschlagen ist.</returns>
        public string ParseConversationToJson(Conversation conversation)
        {
            string jsonString = null;
            try
            {
                jsonString = JsonConvert.SerializeObject(conversation);
            }
            catch (JsonException jsonEx)
            {
                Debug.WriteLine("ParseConversationToJson: Failed to parse conversation to json.");
                Debug.WriteLine("ParseConversationToJson: Msg is {0}.", jsonEx.Message);
            }

            return jsonString;
        }