/// <summary>
        /// Aktualisiert die lokal gespeicherten Datensätze der übergebenen Abstimmung.
        /// Gibt an, ob die Aktualisierung erfolgreich war, oder ob fehlende Referenzen (z.B. 
        /// der lokale Datensatz des Administrators) eine Aktualisierung verhindert haben.
        /// Methode aktualisisert nur die Abstimmungsdaten selbst, Subressourcen werden nicht aktualisiert,
        /// d.h. keine Optionen und Abstimmungsergebnisse werden aktualisiert.
        /// </summary>
        /// <param name="newBallot">Das Objekt mit den neuen Abstimmungsdaten.</param>
        /// <returns>Liefert true, wenn die Aktualisierung erfolgreich war, false, wenn die
        ///     Aktualisierung aufgrund fehlender Referenzen nicht durchgeführt werden konnte.</returns>
        /// <exception cref="ClientException">Wirft ClientException, wenn Aktualisierung fehlgeschlagen ist.</exception>
        public bool UpdateBallot(Ballot newBallot)
        {
            bool updatedSuccessfully = false;

            try
            {
                if (newBallot != null && userController.IsUserLocallyStored(newBallot.AdminId)
                    && groupDBManager.IsBallotStored(newBallot.Id))
                {
                    groupDBManager.UpdateBallot(newBallot);
                    updatedSuccessfully = true;
                }
            }
            catch (DatabaseException ex)
            {
                Debug.WriteLine("UpdateBallot: Failed to update ballot with id {0} in local datasets.", newBallot.Id);
                throw new ClientException(ErrorCodes.LocalDatabaseException, ex.Message);
            }

            return updatedSuccessfully;
        }
        /// <summary>
        /// Erzeugt ein Objekt vom Typ Ballot, welches die vom Nutzer eingegebenen Daten beinhaltet.
        /// </summary>
        /// <returns>Ein Objekt vom Typ Ballot.</returns>
        private Ballot generateBallotObjectFromEnteredData()
        {
            Ballot ballot = new Ballot();

            ballot.Title = EnteredTitle;
            ballot.Description = EnteredDescription;

            ballot.IsMultipleChoice = IsMultipleChoiceSelected;
            ballot.HasPublicVotes = IsPublicVotesSelected;
            ballot.IsClosed = IsClosedSelected;

            ballot.AdminId = groupController.GetLocalUser().Id;

            if (BallotOptionsCollection != null)
            {
                ballot.Options = new List<Option>();
                foreach (Option option in BallotOptionsCollection)
                {
                    ballot.Options.Add(option);
                }
            } 

            return ballot;
        }
        /// <summary>
        /// Speichert die Abstimmung in den lokalen Datensätzen ab.
        /// </summary>
        /// <param name="groupId">Die Id der Gruppe, der die Abstimmung zugeordnet ist.</param>
        /// <param name="ballot">Die zu speichernde Abstimmung in Form eines Ballot Objekts.</param>
        /// <returns>Liefert true, falls Speicherung erfolgreich, liefert false, falls Speicherung aufgrund fehlender
        ///     Referenzen (z.B. fehlender Nutzerdatensatz in der Datenbank) nicht ausgeführt werden konnte.</returns>
        /// <exception cref="ClientException">Wirft ClientException, wenn bei der Speicherung ein unerwarteter Fehler auftritt.</exception>
        public bool StoreBallot(int groupId, Ballot ballot)
        {
            bool insertedSuccessfully = false;
            bool fulfillsConstraints = true;

            try
            {
                if (groupDBManager.IsGroupStored(groupId) && !groupDBManager.IsBallotStored(ballot.Id))
                {
                    if (!userController.IsUserLocallyStored(ballot.AdminId))
                    {
                        Debug.WriteLine("StoreBallot: The ballot's admin (id: {0}) is not locally stored. Cannot continue.", ballot.AdminId);
                        fulfillsConstraints = false;
                    }

                    if (ballot.Options != null)
                    {
                        Dictionary<int, bool> userStoredStatus = new Dictionary<int, bool>();

                        foreach (Option option in ballot.Options)
                        {
                            // Falls Voter gesetzt, so müssen diese geprüft werden.
                            if (option.VoterIds != null)
                            {
                                foreach (int voter in option.VoterIds)
                                {
                                    if (!userStoredStatus.ContainsKey(voter))
                                    {
                                        userStoredStatus.Add(voter, userController.IsUserLocallyStored(voter));
                                    }
                                }
                            }
                        }

                        // Prüfe, ob alle Bedingungen für Insert erfüllt sind.
                        foreach (bool status in userStoredStatus.Values)
                        {
                            if (!status)
                            {
                                Debug.WriteLine("StoreBallot: There are missing users. Cannot insert ballot.");
                                fulfillsConstraints = false;
                            }
                        }
                    }

                    if (fulfillsConstraints)
                    {
                        // Speichere Abstimmung ab.
                        groupDBManager.StoreBallot(groupId, ballot);
                        insertedSuccessfully = true;
                    }

                }
                else
                {
                    if (groupDBManager.IsBallotStored(ballot.Id))
                    {
                        Debug.WriteLine("StoreBallot: Ballot is already stored.");
                        return true;
                    }
                    else
                    {
                        Debug.WriteLine("StoreBallot: Cannot store without corresponding group.");
                    }
                }
            }
            catch (DatabaseException ex)
            {
                Debug.WriteLine("StoreBallot: Storing failed. Message is {0}.", ex.Message);
                throw new ClientException(ErrorCodes.LocalDatabaseException, ex.Message);
            }

            Debug.WriteLine("StoreBallot: Returns {0}.", insertedSuccessfully);
            return insertedSuccessfully;
        }
        /// <summary>
        /// Bereitet ein Objekt vom Typ Ballot vor, welches alle Properties enthält, die sich geändert haben.
        /// Die Methode bekommt eine alte Version eines Ballot Objekts und eine neue Version und ermittelt 
        /// dann die Properties, die eine Aktualisierung erhalten haben und schreibt diese in eine neue Ballot
        /// Instanz. Die von der Methode zurückgelieferte Ballot 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="oldBallot">Das Ballot Objekt vor der Aktualisierung.</param>
        /// <param name="newBallot">Das Ballot Objekt mit den aktuellen Werten.</param>
        /// <returns>Ein Objekt der Klasse Ballot, bei dem nur die Eigenschaften mit neuem Wert gesetzt sind.
        ///     Hat sich keine Eigenschaft geändert, so wird null zurückgegeben.</returns>
        private Ballot perpareUpdatableBallotInstance(Ballot oldBallot, Ballot newBallot)
        {
            bool hasChanged = false;
            Ballot updatedBallot = new Ballot();

            if (oldBallot.Title != newBallot.Title)
            {
                hasChanged = true;
                updatedBallot.Title = newBallot.Title;
            }

            if (oldBallot.Description != newBallot.Description)
            {
                hasChanged = true;
                updatedBallot.Description = newBallot.Description;
            }

            if (oldBallot.IsClosed.HasValue && newBallot.IsClosed.HasValue && 
                oldBallot.IsClosed.Value != newBallot.IsClosed.Value)
            {
                hasChanged = true;
                updatedBallot.IsClosed = newBallot.IsClosed;
            }

            // Prüfe, ob sich überhaupt eine Property geändert hat.
            if (!hasChanged)
            {
                Debug.WriteLine("perpareUpdatableBallotInstance: No property of ballot has changed. Method will return null.");
                updatedBallot = null;
            }

            return updatedBallot;
        }
        /// <summary>
        /// Führt Aktualisierung der Abstimmung aus. Es wird ermittelt welche Eigenschaften eine Aktualisierung 
        /// erhalten haben. Die Aktualisierungen werden an den Server übermittelt, der die Aktualisierung auf
        /// dem Serverdatensatz ausführt und die Gruppenmitglieder über die Änderung informiert.
        /// Ebenso werden die Abstimmungsoptionen aktualisiert, falls Änderungen vorgenommen wurden.
        /// </summary>
        /// <param name="groupId">Die Id der Gruppe, zu der die Abstimmung gehört.</param>
        /// <param name="oldBallot">Der Datensatz der Abstimmung, vor der Aktualisierung.</param>
        /// <param name="newBallot">Der Datensatz mit den neu eingegebenen Daten. Enthält auch die festgelegten
        ///     Abstimmungsoptionen.</param>
        /// <returns>Liefert true, wenn die Aktualisierung erfolgreich war, ansonsten false.</returns>
        /// <exception cref="ClientException">Wirft ClientException, wenn Aktualisierung fehlgeschlagen ist, oder nur
        ///     in Teilen erfolgreich durchgeführt werden konnte.</exception>
        public async Task<bool> EditBallotAsync(int groupId, Ballot oldBallot, Ballot newBallot)
        {
            if (oldBallot == null || newBallot == null)
                return false;

            // Validiere zunächst die neu eingegebenen Daten. Bei Validierungsfehlern kann man hier gleich abbrechen.
            clearValidationErrors();
            newBallot.ClearValidationErrors();
            newBallot.ValidateAll();
            if (newBallot.HasValidationErrors())
            {
                reportValidationErrors(newBallot.GetValidationErrors());
                return false;
            }

            // Generiere Ballot Objekt für die Aktualisierung. Dieses Objekt enthält nur die aktualisierten Eigenschaften.
            bool requiresUpdate = true;
            Ballot updatableBallot = perpareUpdatableBallotInstance(oldBallot, newBallot);
            if (updatableBallot == null)
            {
                Debug.WriteLine("EditBallotAsync: No update required.");
                requiresUpdate = false;
            }

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

                // Setze Request zur Aktualisierung der Abstimmung ab.
                string serverResponse = null;
                try
                {
                    serverResponse = await groupAPI.SendUpdateBallotRequest(
                        getLocalUser().ServerAccessToken,
                        groupId,
                        oldBallot.Id,
                        jsonContent);
                }
                catch (APIException ex)
                {
                    Debug.WriteLine("EditBallotAsync: Failed request. Error code: {0}, msg is: '{1}'.", ex.ErrorCode, ex.Message);
                    handleGroupRelatedErrors(ex.ErrorCode, groupId, null, oldBallot.Id);

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

                // Parse Antwort des Servers.
                Ballot serverBallot = jsonParser.ParseBallotFromJson(serverResponse);
                if (serverBallot != null)
                {
                    Debug.WriteLine("EditBallotAsync: Start local updating of ballot.");
                    // Aktualisiere die Abstimmung.
                    bool successful = UpdateBallot(serverBallot);
                    if (!successful)
                    {
                        throw new ClientException(ErrorCodes.LocalDatabaseException, "Failed to update local ballot");
                    }
                }
                else
                {
                    throw new ClientException(ErrorCodes.JsonParserError, "Failed to parse server response");
                }
            }

            // Führe noch eine Aktualisierung der Abstimmungsoptionen durch.
            List<Option> currentOptions = GetOptions(oldBallot.Id, false);
            // Extrahiere die Liste von Abstimmungsoptionen der neuen Abstimmung.
            List<Option> newOptionList = newBallot.Options;

            await SynchronizeBallotOptionsAsync(groupId, oldBallot.Id, currentOptions, newOptionList);

            return true;
        }
        /// <summary>
        /// Sendet einen Request zum Anlegen einer neuen Abstimmung an den Server.
        /// </summary>
        /// <param name="groupId">Die Id der Gruppe, in der die Abstimmung angelegt werden soll.</param>
        /// <param name="newBallot">Das Abstimmungsojekt mit den Daten der neuen Abstimmung.</param>
        /// <returns>Liefert true, wenn die Abstimmung erfolgreich angelegt werden konnte. Liefert false, 
        ///     wenn die Abstimmung nicht angelegt werden konnte, da z.B. die Validierung der Daten fehlgeschlagen ist.</returns>
        /// <exception cref="ClientException">Wirft ClientException, wenn Erzeugung der Abstimmung fehlgeschlagen ist, oder 
        ///     vom Server abgelehnt wurde.</exception>
        public async Task<bool> CreateBallotAsync(int groupId, Ballot newBallot)
        {
            bool successful = false;

            if (newBallot == null)
                return successful;

            // Führe Validierung der Abstimmungsdaten durch. Abbruch bei aufgetretenem Validierungsfehler.
            clearValidationErrors();
            newBallot.ClearValidationErrors();
            newBallot.ValidateAll();
            if (newBallot.HasValidationErrors())
            {
                reportValidationErrors(newBallot.GetValidationErrors());
                return successful;
            }

            // Die für die neue Abstimmung angegebenen Optionen.
            List<Option> ballotOptions = newBallot.Options;

            // Setze Optionen im Objekt selbst auf null zwecks Create Request.
            newBallot.Options = null;

            // Generiere JSON-Dokument.
            string jsonContent = jsonParser.ParseBallotToJson(newBallot);
            if (jsonContent == null)
            {
                Debug.WriteLine("CreateBallotAsync: Couldn't serialize ballot object to json. Cannot continue.");
                throw new ClientException(ErrorCodes.JsonParserError, "Failed to generate json document.");
            }

            // Setze Request zum Anlegen der Abstimmung ab.
            string serverResponse = null;
            try
            {
                serverResponse = await groupAPI.SendCreateBallotRequest(
                    getLocalUser().ServerAccessToken,
                    groupId,
                    jsonContent);

                successful = true;
            }
            catch (APIException ex)
            {
                Debug.WriteLine("CreateBallotAsync: Request failed. Error code is: {0}.", ex.ErrorCode);
                handleGroupRelatedErrors(ex.ErrorCode, groupId, null, null);

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

            Ballot serverBallot = null;
            if (serverResponse != null)
            {
                // Parse Server Antwort.
                serverBallot = jsonParser.ParseBallotFromJson(serverResponse);

                if (serverBallot != null)
                {
                    // Speichere Abstimmung ab.
                    if (!StoreBallot(groupId, serverBallot))
                    {
                        Debug.WriteLine("CreateBallotAsync: Failed to store ballot.");
                        throw new ClientException(ErrorCodes.LocalDatabaseException, "Failed to store ballot with id " + serverBallot.Id + ".");
                    }
                }
                else
                {
                    throw new ClientException(ErrorCodes.JsonParserError, "Failed to parse server response.");
                }
            }

            // Sende noch die Requests zum Anlegen der Abstimmungsoptionen.
            if (serverBallot != null && ballotOptions != null)
            {
                bool successfullyCreatedOptions = true;

                foreach (Option option in ballotOptions)
                {
                    try
                    {
                        await CreateBallotOptionAsync(groupId, serverBallot.Id, option);
                    }
                    catch (ClientException ex)
                    {
                        // Nicht die ganze Ausführung abbrechen bei fehlgeschlagenem Request.
                        // Versuche den Rest der Optionen dennoch erfolgreich anzulegen.
                        Debug.WriteLine("CreateBallotAsync: Failed to store a ballot option. " + 
                            "The option with id {0} could not be created. Msg is: {1}.", option.Id, ex.Message);
                        successfullyCreatedOptions = false;
                    }
                }

                if (!successfullyCreatedOptions)
                {
                    // Werfe Fehler mit entsprechendem Fehlercode. Hinweis, dass nicht alle Optionen angelegt wurden.
                    throw new ClientException(ErrorCodes.OptionCreationHasFailedInBallotCreationProcess, "Failed to store options.");
                }
            }

            return successful;
        }
        /// <summary>
        /// Wandelt ein übergebenes Objekt vom Typ Ballot in ein JSON-Dokument um.
        /// </summary>
        /// <param name="ballot">Das Abstimmungsobjekt (Typ Ballot), das umgewandelt werden soll.</param>
        /// <returns>Das erstellte JSON-Dokument, oder null, falls die Umwandung fehlgeschlagen ist.</returns>
        public string ParseBallotToJson(Ballot ballot)
        {
            string jsonContent = null;
            try
            {
                jsonContent = JsonConvert.SerializeObject(ballot);
            }
            catch (JsonException ex)
            {
                Debug.WriteLine("ParseBallotToJson: Json parser error occurred. " +
                  "Message is {0}.", ex.Message);
            }

            return jsonContent;
        }