using System;
using System.Collections.Generic;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using gehGassi.Dto.Common;
using gehGassi.Dto;
using gehGassiApp.Core.Data;
using gehGassiApp.Core.Interfaces;
using gehGassiApp.Core.Interfaces.Synchronization;
using gehGassiApp.Core.Mapper;
using gehGassiApp.Core.Resources;
using gehGassiApp.Domain.Common;
using gehGassiApp.Domain.Favourites;
using gehGassiApp.Domain.Users;
using gehGassi.Dto.Favourites;
namespace gehGassiApp.Core.Services
{
///
/// Service der die Verwaltung von Favoriten ermöglicht
///
public class FavouriteService : ServiceBase, IFavouriteService
{
private readonly ICommunicationService _communicationService;
private readonly ISyncInfoPullService _pullService;
private readonly ISyncInfoPushService _pushService;
private readonly ILocationService _locationService;
private readonly IRepository _appUserRepository;
///
/// Erstellt eine Instanz
///
/// Instanz eines IUnitOfWork
/// Instanz eines ICommunicationService
/// Instanz eines ISyncInfoPullService
/// Instanz eines ISyncInfoPushService
/// Instanz eines ILocationService
public FavouriteService(IUnitOfWork unitOfWork, ICommunicationService communicationService, ISyncInfoPullService syncInfoPullService, ISyncInfoPushService syncInfoPushService, ILocationService locationService) : base(unitOfWork)
{
_communicationService = communicationService;
_pullService = syncInfoPullService;
_pushService = syncInfoPushService;
_locationService = locationService;
_appUserRepository = unitOfWork.GetRepository();
}
public override Favourite Get(object id, bool noTracking = true)
{
throw new NotImplementedException();
}
public override async Task GetAsync(object id, bool noTracking = true)
{
await Task.Delay(1);
throw new NotImplementedException();
}
///
/// Erstellen eines Favoriten
///
/// Favourite
public Favourite Create()
{
var favourite = new Favourite()
{
Id = Guid.NewGuid().ToString("N"),
Created = DateTimeOffset.UtcNow
};
return favourite;
}
///
/// Hinzufügen eines Favoriten
///
/// Favourite
/// Aktuelles Accesstoken
/// CancellationToken
/// Angelegter Favorit
public async Task AddAsync(Favourite favourite, string accessToken, CancellationToken token)
{
var existingFavourite = await Repository.GetAsync(favourite.Id);
if (existingFavourite == null)
{
Repository.Add(favourite);
await CommitAsync();
//Jetzt das Rating an der Server übertragen oder ein Delta erstellen
var dto = favourite.ToDto();
var isConnected = await _communicationService.IsConnected();
var createDelta = true;
if (isConnected)
{
var createResult = await _communicationService.CreateFavouriteAsync(dto, accessToken, token);
if (createResult.Success && createResult.Value.Status != CreateStatus.Error)
{
createDelta = false;
}
}
if (createDelta)
{
await _pushService.AddAsync(nameof(Favourite), favourite.Id, SyncOperation.Create, favourite).ConfigureAwait(false);
}
return favourite;
}
return null;
}
///
/// Aktualisieren eines Favoriten
///
/// Favourite
/// Aktuelles Accesstoken
/// CancellationToken
/// true wenn erfolgreich, false sonst
public async Task UpdateAsync(Favourite favourite, string accessToken, CancellationToken token)
{
var localFavourite = await Repository.FirstOrDefaultAsync(c => c.Id == favourite.Id, false).ConfigureAwait(false);
if (localFavourite != null && !localFavourite.Deleted)
{
localFavourite.AppUserId = favourite.AppUserId;
localFavourite.Table = favourite.Table;
localFavourite.Key = favourite.Key;
localFavourite.UpdatedAt = DateTimeOffset.UtcNow;
Repository.Update(localFavourite);
await CommitAsync().ConfigureAwait(false);
var dto = localFavourite.ToDto();
var isConnected = await _communicationService.IsConnected();
var createDelta = true;
if (isConnected)
{
var updateResult = await _communicationService.UpdateFavouriteAsync(dto, accessToken, token);
if (updateResult.Success && updateResult.Value)
createDelta = false;
}
if (createDelta)
{
await _pushService.AddAsync(nameof(Favourite), localFavourite.Id, SyncOperation.Edit, localFavourite).ConfigureAwait(false);
}
return true;
}
return false;
}
///
/// Echtes Löschen eines Favoriten
///
/// Id des Favoriten
/// Id des AppBenutzers
/// Aktuelles Accesstoken
/// CancellationToken
/// true wenn erfolgreich, false sonst
public async Task DeleteAsync(string favouriteId, string appUserId, string accessToken, CancellationToken token)
{
var localFavourite = await Repository.FirstOrDefaultAsync(c => c.Id == favouriteId, false).ConfigureAwait(false);
if (localFavourite != null && localFavourite.AppUserId == appUserId)
{
Repository.Remove(localFavourite);
await CommitAsync().ConfigureAwait(false);
var dto = localFavourite.ToDto();
var isConnected = await _communicationService.IsConnected();
var createDelta = true;
if (isConnected)
{
var deleteResult = await _communicationService.DeleteFavouriteAsync(dto, appUserId, accessToken, token);
if (deleteResult.Success && deleteResult.Value)
createDelta = false;
}
if (createDelta)
{
await _pushService.AddAsync(nameof(Favourite), localFavourite.Id, SyncOperation.Delete, localFavourite).ConfigureAwait(false);
}
return true;
}
return false;
}
///
/// Prüfen ob ein Objekt ein Favorit ist. Prüft nur Lokal!
///
/// Id des Objekts
/// Typ des Objekts
/// true wenn ein Favorit, false sonst
public async Task IsFavouriteLocalAsync(string key, string table)
{
var existing = await Repository.FirstOrDefaultAsync(c => c.Key == key && c.Table == table && c.Deleted == false);
return existing != null;
}
///
/// Toggeln des Favoriten-Status eines Objektes.
/// Entweder wird der Favorit angelegt, oder gelöscht
///
/// Id des Objekts
/// Typ des Objekts
/// Id des AppUsers
/// Aktuelles Accesstoken
/// CancellationToken
/// true wenn erfolgreich, false sonst
public async Task ToggleAsync(string key, string table, string appUserId, string accessToken, CancellationToken token)
{
var existing = await Repository.FirstOrDefaultAsync(c => c.Key == key && c.Table == table && c.Deleted == false);
if (existing != null)
{
//Favorit existiert, also löschen
return await DeleteAsync(existing.Id, appUserId, accessToken, token);
}
else
{
//Favorit existiert nicht, also anlegen
var favourite = Create();
favourite.Key = key;
favourite.Table = table;
favourite.AppUserId = appUserId;
favourite.Created = DateTimeOffset.UtcNow;
favourite.UpdatedAt = DateTimeOffset.UtcNow;
var created = await AddAsync(favourite, accessToken, token);
return created != null;
}
}
///
/// Gibt eine Liste von Favoriten für eine Objektklasse für einen Benutzer zurück
///
/// Gewünschtes Objekt
/// Aktuelle Position
/// Gewünschte Sprache
/// Aktuelles Accesstoken
/// CancellationToken
/// Id des AppUsers
/// ListCommunicationResult
public async Task>> GetListAsync(string appUserId, string table, LocationDto location, string language, string accessToken, CancellationToken token)
{
var result = new CommunicationResult>() { Value = new List() };
var isConnected = await _communicationService.IsConnected();
if (isConnected)
{
var query = new FavouriteListQueryDto
{
Location =location,
Language = language,
AppUserId = appUserId,
Table = table
};
var queryResult = await _communicationService.GetFavouritesListAsync(query, accessToken, token);
if (queryResult.Success)
{
result.Success = true;
result.Value = queryResult.Value;
}
}
else
{
result.Success = false;
result.ErrorCode = CommunicationErrors.ServerNoConnection;
result.ErrorMessage = Errors.Server_NoConnection;
}
return result;
}
#region Pull-Push Implementation
///
/// Holen der letzten Daten vom Server und Synchronisieren mit den lokalen Daten
///
/// Sprache
/// Aktuelles Accesstoken
/// CancellationToken
/// Task
public async Task> PullAsync(string language, string accessToken, CancellationToken token)
{
var result = new SyncResult();
var isConnected = await _communicationService.IsConnected();
if (isConnected)
{
var user = await _appUserRepository.FirstOrDefaultAsync(c => c.Id != "", false).ConfigureAwait(false);
//Zuerst lezte Aktivität holen...
DateTimeOffset? lastUpdate = null;
var lastSyncInfo = await _pullService.GetAsync(nameof(Favourite));
if (lastSyncInfo != null)
lastUpdate = lastSyncInfo.LastUpdate;
var requestResult = await _communicationService.GetFavouritesForSyncAsync(user.Id, lastUpdate, accessToken, token);
if (requestResult.Success)
{
result = await HandleChangesAsync(requestResult.Value);
if (result.LastUpdate != null)
await _pullService.AddOrUpddateAsync(nameof(Favourite), result.LastUpdate.Value).ConfigureAwait(false);
}
result.LastUpdate ??= lastUpdate;
}
return result;
}
///
/// Holen der Daten vom lokalen Speicher die noch nicht synchronisiert wurden und senden an den Server
///
/// Aktuelles Accesstoken
/// CancellationToken
/// Task
public async Task> PushAsync(string accessToken, CancellationToken token)
{
var result = new SyncResult();
if (await _pushService.HasOpenAsync(nameof(Favourite)) > 0)
{
var deltas = await _pushService.GetAllAsync(nameof(Favourite));
if (deltas.Any())
{
var user = await _appUserRepository.FirstOrDefaultAsync(c => c.Id != "", false).ConfigureAwait(false);
deltas = deltas.OrderBy(c => c.DateTime).ToList();
var isConnected = await _communicationService.IsConnected();
if (!isConnected) return result;
foreach (var syncInfoPush in deltas)
{
try
{
if (!string.IsNullOrWhiteSpace(syncInfoPush.Value))
{
var request = JsonSerializer.Deserialize(syncInfoPush.Value, new JsonSerializerOptions(JsonSerializerDefaults.Web));
if (syncInfoPush.Operation == SyncOperation.Create)
{
var createDto = request.ToDto();
var createResult = await _communicationService.CreateFavouriteAsync(createDto, accessToken, token);
if (createResult.Success && createResult.Value.Status != CreateStatus.Error)
{
await _pushService.RemoveAsync(syncInfoPush.Id).ConfigureAwait(false);
}
//TODO: Was tun wenn Fehler?
}
else if (syncInfoPush.Operation == SyncOperation.Edit)
{
var updateDto = request.ToDto();
var updateResult = await _communicationService.UpdateFavouriteAsync(updateDto, accessToken, token);
if (updateResult.Success && updateResult.Value)
await _pushService.RemoveAsync(syncInfoPush.Id).ConfigureAwait(false);
//TODO: Was tun wenn Fehler?
}
else
{
var deleteDto = request.ToDto();
var deleteResult = await _communicationService.DeleteFavouriteAsync(deleteDto, user.Id, accessToken, token);
if (deleteResult.Success && deleteResult.Value)
await _pushService.RemoveAsync(syncInfoPush.Id).ConfigureAwait(false);
//TODO: Was tun wenn Fehler?
}
}
else
{
await _pushService.RemoveAsync(syncInfoPush.Id).ConfigureAwait(false);
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
await _pushService.RemoveAsync(syncInfoPush.Id).ConfigureAwait(false);
}
}
//Else ist nichts tun, konnte nicht übertragen werden.
}
}
return result;
}
#endregion
#region Private
///
/// Behandeln der Liste von Favoriten wenn welche vom Online-Store geholt werden.
///
/// Liste der Favoriten
/// Task
private async Task> HandleChangesAsync(List favourites)
{
var result = new SyncResult();
//Je Favorit durchgehen ob was gemacht werden soll
foreach (var favourite in favourites)
{
var localFavourite = await Repository.FirstOrDefaultAsync(c => c.Id == favourite.Id, false);
if (localFavourite != null && localFavourite.UpdatedAt < favourite.UpdatedAt)
{
if (localFavourite.UpdatedAt >= favourite.UpdatedAt)
{
if (result.LastUpdate == null || result.LastUpdate < localFavourite.UpdatedAt)
result.LastUpdate = localFavourite.UpdatedAt;
continue;
}
var deleted = localFavourite.Deleted == false && favourite.Deleted;
localFavourite.Version = favourite.Version;
localFavourite.UpdatedAt = favourite.UpdatedAt;
localFavourite.Deleted = favourite.Deleted;
localFavourite.AppUserId = favourite.AppUserId;
localFavourite.Key = favourite.Key;
localFavourite.Table = favourite.Table;
localFavourite.Created = favourite.Created;
if (result.LastUpdate == null || result.LastUpdate < localFavourite.UpdatedAt)
result.LastUpdate = localFavourite.UpdatedAt;
if (!deleted)
result.Updated.Add(localFavourite);
else
result.Deleted.Add(localFavourite);
Repository.Update(localFavourite);
}
if (localFavourite == null)
{
localFavourite = new Favourite()
{
Id = favourite.Id,
Version = favourite.Version,
UpdatedAt = favourite.UpdatedAt,
Deleted = favourite.Deleted,
AppUserId = favourite.AppUserId,
Key = favourite.Key,
Table = favourite.Table,
Created = favourite.Created
};
Repository.Add(localFavourite);
if (result.LastUpdate == null || result.LastUpdate < localFavourite.UpdatedAt)
result.LastUpdate = localFavourite.UpdatedAt;
result.Added.Add(localFavourite);
}
}
if (result.HasChanges)
{
try
{
await CommitAsync().ConfigureAwait(false);
}
catch (Exception ex)
{
var err = ex.Message;
}
}
return result;
}
#endregion
}
}