/// <summary>
 /// Aktualisiert die für die View relevanten Properties der aktuell
 /// verwalteten Gruppen-Instanz mit den Werten der übergebenen neuen
 /// Gruppen-Instanz.
 /// </summary>
 /// <param name="currentGroup">Die aktuell im ViewModel verwaltete Gruppe.</param>
 /// <param name="newGroup">Die neue Gruppen-Instanz.</param>
 private void updateViewRelatedGroupProperties(Group currentGroup, Group newGroup)
 {
     currentGroup.Name = newGroup.Name;
     currentGroup.Term = newGroup.Term;
     currentGroup.HasNewEvent = newGroup.HasNewEvent;
 }
        /// <summary>
        /// Führt Aktualisierung der Gruppe aus. Es wird ermittelt welche Properties eine Aktualisierung 
        /// erhalten haben. Die Aktualisierungen werden an den Server übermittelt, der die Aktualisierung auf
        /// dem Serverdatensatz ausführt und die Teilnehmer über die Änderung informiert. Aktualisiert lokal jedoch
        /// nicht die Benachrichtigungseinstellungen für die Gruppe. Hierfür gibt es eine separate Funktion.
        /// </summary>
        /// <param name="oldGroup">Der Datensatz der Gruppe vor der Aktualisierung.</param>
        /// <param name="newGroup">Der Datensatz mit aktualisierten Daten.</param>
        /// <param name="ignorePassword">Gibt an, ob das Passwort Property bei der Aktualisierung ignoriert werden soll.</param>
        /// <returns>Liefert true, wenn die Aktualisierung erfolgreich war, ansonsten false.</returns>
        /// <exception cref="ClientException">Wirft ClientException, wenn Fehler während des Aktualisierungsvorgangs auftritt.</exception>
        public async Task<bool> UpdateGroupAsync(Group oldGroup, Group newGroup, bool ignorePassword)
        {
            if (oldGroup == null || newGroup == null)
                return false;

            User localUser = getLocalUser();
            if (localUser == null)
                return false;

            // Gruppen-Admin sollte nicht 0 sein, falls das der Fall ist, setzte ihn auf den alten Wert.
            if (newGroup.GroupAdmin == 0)
            {
                newGroup.GroupAdmin = oldGroup.GroupAdmin;
            }

            // Führe Validierung aus. Wenn Validierung fehlschlägt, kann abgebrochen werden.
            clearValidationErrors();
            newGroup.ClearValidationErrors();
            newGroup.ValidateAll();

            if (newGroup.HasValidationErrors() && !ignorePassword)
            {
                reportValidationErrors(newGroup.GetValidationErrors());
                return false;
            }
            else if (ignorePassword)
            {
                // Prüfe alle Properties bis auf Passwort separat.
                if (newGroup.HasValidationError("Name") ||
                    newGroup.HasValidationError("Term") || 
                    newGroup.HasValidationError("Description"))
                {
                    Dictionary<string, string> validationErrors = newGroup.GetValidationErrors();
                    if (validationErrors.ContainsKey("Password"))
                    {
                        validationErrors.Remove("Password");
                    }
                    reportValidationErrors(validationErrors);
                    return false;
                }
            }

            // Erstelle ein Objekt für die Aktualisierung, welches die Daten enthält, die aktualisiert werden müssen.
            Group updatableGroupObj = prepareUpdatableGroupInstance(oldGroup, newGroup);

            if (updatableGroupObj == null)
                return false;   // Keine Aktualisierung notwendig.

            // Hash Passwort bei Änderung.
            if (updatableGroupObj.Password != null)
            {
                HashingHelper.HashingHelper hashHelper = new HashingHelper.HashingHelper();
                string hashedPassword = hashHelper.GenerateSHA256Hash(updatableGroupObj.Password);
                updatableGroupObj.Password = hashedPassword;
            }

            // Erstelle Json-Dokument für die Aktualisierung.
            string jsonContent = base.jsonParser.ParseGroupToJson(updatableGroupObj);
            if (jsonContent == null)
            {
                Debug.WriteLine("UpdateGroupAsync: Group object could not be translated to a json document.");
                throw new ClientException(ErrorCodes.JsonParserError, "Could not generate json document.");
            }

            // Server Request.
            string serverResponse = null;
            try
            {
                serverResponse = await groupAPI.SendUpdateGroupRequest(
                    getLocalUser().ServerAccessToken,
                    oldGroup.Id,
                    jsonContent);
            }
            catch (APIException ex)
            {
                //Keine explizite Behandlung von GroupNotFound, GroupParticipantNotFound, ... hier, 
                //da nur Admin normal diese Funktionalität ausführen kann.
                Debug.WriteLine("UpdateGroupAsync: Request failed. Error code: {0}, status code: {1}.", ex.ErrorCode, ex.ResponseStatusCode);
                throw new ClientException(ex.ErrorCode, ex.Message);
            }

            // Führe lokale Aktualisierung des Datensatzes aus.
            Group updatedGroup = base.jsonParser.ParseGroupFromJson(serverResponse);
            if (updatedGroup == null)
            {
                throw new ClientException(ErrorCodes.JsonParserError, "Couldn't parse server response.");
            }

            // Speichere neuen Datensatz ab.
            UpdateGroup(updatedGroup, false);

            return true;
        }
        /// <summary>
        /// Bereitet ein Objekt vom Typ Group vor, welches alle Properties enthält, die sich geändert haben.
        /// Die Methode bekommt eine alte Version eines Group Objekts und eine neue Version und ermittelt 
        /// dann die Properties, die eine Aktualisierung erhalten haben und schreibt diese in eine neue Group
        /// Instanz. Die von der Methode zurückgelieferte Group 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="oldGroup">Das Group Objekt vor der Aktualisierung.</param>
        /// <param name="newGroup">Das Group Objekt mit den aktuellen Werten.</param>
        /// <returns>Ein Objekt der Klasse Group, bei dem die Properties, die sich geändert haben, mit den
        ///     aktualisierten Werten gefüllt sind.</returns>
        private Group prepareUpdatableGroupInstance(Group oldGroup, Group newGroup)
        {
            bool hasChanged = false;
            Group updatedGroup = new Group();

            // Vergleiche die Properties.
            if (oldGroup.Name != newGroup.Name)
            {
                hasChanged = true;
                updatedGroup.Name = newGroup.Name;
            }

            if (oldGroup.Description != newGroup.Description)
            {
                hasChanged = true;
                updatedGroup.Description = newGroup.Description;
            }

            // Aktualisiere Passwort, wenn es in der neuen Instanz gesetzt ist.
            if (newGroup.Password != null)
            {
                hasChanged = true;
                updatedGroup.Password = newGroup.Password;
            }

            if (oldGroup.Term != newGroup.Term)
            {
                hasChanged = true;
                updatedGroup.Term = newGroup.Term;
            }

            if (oldGroup.GroupAdmin != newGroup.GroupAdmin)
            {
                hasChanged = true;
                updatedGroup.GroupAdmin = newGroup.GroupAdmin;
            }

            // Prüfe, ob sich überhaupt ein Property geändert hat.
            if (!hasChanged)
            {
                Debug.WriteLine("prepareUpdatableGroupInstance: No property of group has been updated. Method will return null.");
                updatedGroup = null;
            }

            return updatedGroup;
        }
 /// <summary>
 /// Aktualisiert den lokalen Datensatz der Gruppe.
 /// </summary>
 /// <param name="updatedGroup">Objekt der Klasse Group mit den aktualisierten Daten.</param>
 /// <param name="updateNotificationSettings">Gibt an, ob ebenfalls die Benachrichtigungseinstellungen der Gruppe
 ///     aktualisiert werden sollen.</param>
 public void UpdateGroup(Group updatedGroup, bool updateNotificationSettings)
 {
     try
     {
         groupDBManager.UpdateGroup(updatedGroup, updateNotificationSettings);
     }
     catch (ClientException ex)
     {
         Debug.WriteLine("UpdateGroup: Failed to update the group. Msg is: '{0}'.", ex.Message);
         throw new ClientException(ErrorCodes.LocalDatabaseException, ex.Message);
     }
 }
        /// <summary>
        /// Der lokale Nutzer verlässt die Gruppe mit der angegebenen Id.
        /// Schickt einen Request an den Server, um den Teilnehmer von der Gruppe zu entfernen.
        /// Behandelt auch den Fall, dass der lokale Nutzer Administrator dieser Gruppe ist. In diesem Fall
        /// wird zunächst ein neuer Administrator bestimmt und die Gruppe aktualisiert.
        /// </summary>
        /// <param name="groupId">Die Id der Gruppe, aus der der lokale Nutzer austritt.</param>
        /// <exception cref="ClientException">Wirft Exception, wenn der Austritt fehlschlägt, oder der
        ///     entsprechende Request vom Server abgelehnt wurde.</exception>
        public async Task LeaveGroupAsync(int groupId)
        {
            User localUser = getLocalUser();
            bool hasLeft = false;

            // Hole die betroffene Gruppe aus dem lokalen Speicher.
            Group affectedGroup = GetGroup(groupId);

            // Sonderfallbehandlung: Nutzer ist Administrator der Gruppe.
            if (affectedGroup.GroupAdmin == localUser.Id)
            {
                // Frage in diesem Fall zunächst alle Teilnehmer der Gruppe vom Server ab, um sicher den aktuellsten Datensatz zu haben.
                List<User> participants = await GetParticipantsOfGroupAsync(groupId, false);

                // Sortiere alle "inaktiven" Nutzer aus.
                for (int i=0; i < participants.Count; i++)
                {
                    if (!participants[i].Active)
                    {
                        participants.RemoveAt(i);
                    }
                }

                if (participants.Count <= 1)
                {
                    // Sonderfall: Es gibt nur einen Teilnehmer der Gruppe, den lokalen Nutzer.
                    // Nutzer kann Gruppe nicht verlassen, da diese sonst leer wäre.
                    throw new ClientException(ErrorCodes.GroupAdminNotAllowedToExit, "Administrator cannot leave group.");
                }

                // Ansonsten: Wähle zufälligen Nutzer und übertrage die Administrationsrechte.
                Random rnd = new Random();
                User newAdmin = localUser;
                while (newAdmin.Id == localUser.Id)
                {
                    int rndIndex = rnd.Next(0, participants.Count);
                    newAdmin = participants[rndIndex];
                }
                Group newGroup = new Group()
                {
                    Id = affectedGroup.Id,
                    Name = affectedGroup.Name,
                    Description = affectedGroup.Description,
                    CreationDate = affectedGroup.CreationDate,
                    ModificationDate = affectedGroup.ModificationDate,
                    GroupNotificationSetting = affectedGroup.GroupNotificationSetting,
                    Deleted = affectedGroup.Deleted,
                    Participants = affectedGroup.Participants,
                    Password = affectedGroup.Password,
                    Term = affectedGroup.Term,
                    Type = affectedGroup.Type
                };
                newGroup.GroupAdmin = newAdmin.Id;

                // Führe Aktualisierung aus.
                bool successful = await UpdateGroupAsync(affectedGroup, newGroup, true);
                if (!successful)
                {
                    // Konnte Rechte nicht übertragen.
                    throw new ClientException(ErrorCodes.GroupAdminRightsTransferHasFailed, "Couldn't transfer admin rights.");
                }                
            }

            // Aus Gruppe austreten.
            try
            {
                // Sende Request an den Server, um lokalen Nutzer von der Gruppe zu entfernen.
                await groupAPI.SendLeaveGroupRequest(
                    localUser.ServerAccessToken,
                    groupId,
                    localUser.Id);
                hasLeft = true;

                Debug.WriteLine("LeaveGroupAsync: Successfully left group with id {0}.", groupId);
            }
            catch (APIException ex)
            {
                if (ex.ErrorCode == ErrorCodes.GroupNotFound)
                {
                    Debug.WriteLine("LeaveGroupAsync: The group with id {0} seems to be deleted " + 
                        "from the server. The leaving process can then be considered successful.");
                    hasLeft = true;
                }
                else if (ex.ErrorCode == ErrorCodes.GroupParticipantNotFound)
                {
                    Debug.WriteLine("LeaveGroupAsync: The user seems to be already removed from the group. " +
                        "The leaving process can then be considered successful.");
                    hasLeft = true;
                }
                else
                {
                    throw new ClientException(ex.ErrorCode, ex.Message);
                }
            }

            // Entferne die Gruppe aus den lokalen Datensätzen. Zugehörige Daten werden kaskadierend gelöscht.
            if (hasLeft)
            {
                DeleteGroupLocally(groupId);
            }
        }
 /// <summary>
 /// Speichert die Daten der Gruppe in den lokalen Datensätzen ab.
 /// </summary>
 /// <param name="group">Die zu speichernde Gruppe.</param>
 /// <exception cref="ClientException">Wenn die Speicherung fehlschlägt.</exception>
 public void StoreGroupLocally(Group group)
 {
     try
     {
         if (!groupDBManager.IsGroupStored(group.Id))
         {
             groupDBManager.StoreGroup(group);
         }
         else
         {
             Debug.WriteLine("StoreGroupLocally: Group is already locally stored.");
         }
     }
     catch (DatabaseException ex)
     {
         Debug.WriteLine("StoreGroupLocally: Failed to store the group.");
         throw new ClientException(ErrorCodes.LocalDatabaseException, ex.Message);
     }
 }
        /// <summary>
        /// Anlegen einer neuen Gruppe im System. Die Daten der Gruppe
        /// werden in Form eines Objekts der Klasse Group übergeben. Ein Request
        /// zum Anlegen der Gruppe auf dem Server wird ausgeführt und die erzeugte
        /// Ressource lokal gespeichert.
        /// </summary>
        /// <param name="newGroup">Das Objekt mit den Daten der neuen Gruppe.</param>
        /// <returns>Liefert true, wenn das Anlegen erfolgreich war, ansonsten false.</returns>
        /// <exception cref="ClientException">Wirft ClientException, wenn Erstellungsvorgang fehlschlägt.</exception>
        public async Task<bool> CreateGroupAsync(Group newGroup)
        {
            if (newGroup == null)
                return false;

            // Hole das lokale Nutzerobjekt.
            User localUser = getLocalUser();

            // Setze Nutzer als Gruppenadmin.
            newGroup.GroupAdmin = localUser.Id;

            // Führe Validierung durch.
            clearValidationErrors();
            newGroup.ClearValidationErrors();
            newGroup.ValidateAll();
            if (newGroup.HasValidationErrors())
            {
                // Melde Validierungsfehler.
                reportValidationErrors(newGroup.GetValidationErrors());
                // Breche ab.
                return false;
            }

            // Hashe das Passwort, das der Nutzer eingegeben hat.
            HashingHelper.HashingHelper hashingHelper = new HashingHelper.HashingHelper();
            newGroup.Password = hashingHelper.GenerateSHA256Hash(newGroup.Password);

            // Umwandeln des Objekts in Json.
            string jsonContent = jsonParser.ParseGroupToJson(newGroup);
            if (jsonContent == null)
            {
                Debug.WriteLine("Error during serialization from group object to json string. Could " +
                    "not create a group. Execution is aborted.");
                throw new ClientException(ErrorCodes.JsonParserError, "Failed to generate json document.");
            }

            // Absetzen des Erstellungsrequests.
            string serverResponse = null;
            try
            {
                // Setzte Request zum Anlegen eines Kanals ab.
                serverResponse = await groupAPI.SendCreateGroupRequest(
                    localUser.ServerAccessToken,
                    jsonContent);
            }
            catch (APIException ex)
            {
                // Bilde ab auf ClientException.
                throw new ClientException(ex.ErrorCode, "Server rejected create group request.");
            }

            // Extrahiere Group Objekt aus Antwort des Servers.
            Group responseGroupObj = jsonParser.ParseGroupFromJson(serverResponse);
            if (responseGroupObj != null)
            {
                // Speichere Gruppe lokal ab.
                try
                {
                    // Prüfe, ob Gruppenadmin (lokaler Nutzer in diesem Fall) als User lokal bereits gespeichert ist.
                    if (!userController.IsUserLocallyStored(localUser.Id))
                    {
                        // Speichere lokalen Nutzer in den lokalen Nutzerdatensätzen für Gruppen ab.
                        // Wird benötigt, da sonst Foreign Key constraints verletzt werden beim Speichern der Gruppe.
                        userController.StoreUserLocally(localUser);
                    }

                    groupDBManager.StoreGroup(responseGroupObj);

                    // Füge Teilnehmer der Gruppe hinzu.
                    AddParticipantToGroup(responseGroupObj.Id, getLocalUser());
                }
                catch (DatabaseException ex)
                {
                    Debug.WriteLine("Database Exception, message is: {0}.", ex.Message);
                    // Bilde ab auf ClientException.
                    throw new ClientException(ErrorCodes.LocalDatabaseException, "Storing process of " +
                        "created group object in local DB has failed");
                }   
            }
            else
            {
                throw new ClientException(ErrorCodes.JsonParserError, "Parsing of server response has failed.");
            }

            return true;
        }
        /// <summary>
        /// Aktualisiert die für die View relevanten Eigenschaften der Gruppeninstanzen.
        /// Führt somit eine Aktualisierung der Anzeige aus.
        /// Muss im UI-Thread ausgeführt werden.
        /// </summary>
        /// <param name="oldGroup">Das derzeit an die View gebundenen Gruppen Objekt.</param>
        /// <param name="newGroup">Das Gruppen Objekt mit den aktuellen Daten.</param>
        private void updateViewRelatedGroupProperties(Group oldGroup, Group newGroup)
        {
            oldGroup.Name = newGroup.Name;
            oldGroup.Description = newGroup.Description;
            oldGroup.Term = newGroup.Term;
            oldGroup.CreationDate = newGroup.CreationDate;
            oldGroup.ModificationDate = newGroup.ModificationDate;
            oldGroup.GroupAdmin = newGroup.GroupAdmin;
            oldGroup.Participants = newGroup.Participants;

            if (oldGroup.Deleted != newGroup.Deleted)
            {
                oldGroup.Deleted = newGroup.Deleted;
                checkCommandExecution();
            }
        }
        /// <summary>
        /// Erzeugt eine Instanz der Klasse Group aus den Daten
        /// der Eingabefelder.
        /// </summary>
        /// <returns>Eine Instanz von Group.</returns>
        private Group createGroupFromEnteredData()
        {
            string term = string.Empty;
            if (IsNoTermSelected)
            {
                // Semester ignorieren.
                term = null;
            }
            else if (IsWinterTermSelected)
            {
                term = "W" + TermYear;
            }
            else if (IsSummerTermSelected)
            {
                term = "S" + TermYear;
            }

            // Erzeuge neue Gruppeninstanz aus eingegebenen Daten.
            Group newGroup = new Group()
            {
                Name = GroupName,
                Description = GroupDescription,
                Term = term,
                Type = SelectedGroupType,
                Password = GroupPassword
            };

            // Setze Passwort null, falls es nicht eingegeben wurde.
            if (newGroup.Password != null && newGroup.Password.Trim().Length == 0)
                newGroup.Password = null;

            return newGroup;
        }
        /// <summary>
        /// Wandelt ein Objekt der Klasse Group in ein entsprechendes JSON Dokument um.
        /// </summary>
        /// <param name="group">Das umzuwandelnde Group Objekt.</param>
        /// <returns>Ein JSON Dokument in Form eines Strings. Oder null, wenn die Umwandlung fehlgeschlagen hat.</returns>
        public string ParseGroupToJson(Group group)
        {
            string groupJson = null;
            try
            {
                groupJson = JsonConvert.SerializeObject(group);
            }
            catch (JsonException ex)
            {
                Debug.WriteLine("ParseGroupToJson: Json parser error occurred. " +
                    "Message is {0}.", ex.Message);
            }

            return groupJson;
        }