using System.Diagnostics;
using System.Reflection.Metadata;
using gehGassi.Dto;
using gehGassi.Dto.Common;
using gehGassi.Dto.News;
using gehGassiApp.Core.Data;
using gehGassiApp.Core.Interfaces;
using gehGassiApp.Core.Interfaces.Synchronization;
using gehGassiApp.Core.Resources;
using gehGassiApp.Domain.Common;
using gehGassiApp.Domain.News;
namespace gehGassiApp.Core.Services
{
///
/// Service der alle Funktionen rund um News zur Verfügung stellt
///
public class NewsService : ServiceBase, INewsService
{
private readonly ICommunicationService _communicationService;
private readonly ILocationService _locationService;
private readonly ISyncInfoPullService _syncInfoPullService;
///
/// Erstellt eine Instanz
///
/// Instanz eines IUnitOfWork
/// Instanz eines ICommunicationService
/// Instanz eines ILocationService
/// Instanz eines ISyncInfoPullService
public NewsService(IUnitOfWork unitOfWork, ICommunicationService communicationService, ILocationService locationService, ISyncInfoPullService syncInfoPullService) : base(unitOfWork)
{
_communicationService = communicationService;
_locationService = locationService;
_syncInfoPullService = syncInfoPullService;
}
///
/// Gibt eine Liste der aktuellen News Kategorien zurück.
/// Entweder von der DB oder vom Server
///
/// Aktuelles Accesstoken
/// Gewünschte Sprache
/// CancellationToken
/// Liste der News Kategorien
public async Task> GetCategoriesAsync(string accessToken, string language, CancellationToken token)
{
//Zuerst DB
var localCategories = await Repository.FindAsync(c => c.Language == language && c.OnlineStatus == OnlineStatus.Online, noTracking: true).ConfigureAwait(false);
if (localCategories != null && localCategories.Any())
return localCategories.Where(c => c.OnlineStatus == OnlineStatus.Online && c.Deleted == false).OrderBy(c => c.Name).ToList();
else
{
//Dann wenn lokal nicht verfügbar immer online...
var isConnected = await _communicationService.IsConnected();
//isConnected = false;
if (isConnected)
{
var categoriesResult = await _communicationService.GetNewsCategoriesAsync(null, language, accessToken, token);
if (categoriesResult.Success)
{
var result = await HandleChangesAsync(categoriesResult.Value);
if (result.LastUpdate != null)
await _syncInfoPullService.AddOrUpddateAsync(nameof(NewsCategory), result.LastUpdate.Value).ConfigureAwait(false);
return categoriesResult.Value.Where(c => c.OnlineStatus == OnlineStatus.Online && c.Deleted == false).OrderBy(c => c.Name).ToList();
}
}
}
//Keine Daten online, keine offline
return new List();
}
///
/// Abrufen von News. Berücksichtigt auch den Standort.
///
/// Id der Kategorie. Leer wenn ohne gewünscht
/// Aktuelle Position
/// Wie viele News sollen abgerufen werden?
/// Wie viele News sollen ausgelassen werden?
/// Akteuller Modus der App
/// Aktuelles Accesstoken
/// Gewünschte Sprache
/// CancellationToken
/// Liste der News
public async Task>> GetNewsAsync(string categoryId, LocationDto location, int take, int skip, AppMode appMode, string language, string accessToken, CancellationToken token)
{
var result = new ListCommunicationResult>() { Value = new List() };
var query = new NewsQueryDto
{
Location = location,
CategoryId = categoryId,
Language = language,
AppMode = (AppModeDto)appMode,
Take = take,
Skip = skip
};
var isConnected = await _communicationService.IsConnected();
if (isConnected)
{
var newsResult = await _communicationService.GetNewsAsync(query, accessToken, token);
if (newsResult.Success)
return newsResult;
}
else
{
result.Success = false;
result.ErrorCode = CommunicationErrors.ServerNoConnection;
result.ErrorMessage = Errors.Server_NoConnection;
}
return result;
}
///
/// Gibt eine Entität anhand der eindeutigen Id zurück
///
/// Id der Entität
/// Gibt an ob NoTRacking verwendet werden soll. Es werden keine Entitäten im EF-Speicher gehalten
/// Entität oder null, wenn nicht gefunden
public override NewsCategory Get(object id, bool noTracking = true)
{
return Repository.SingleOrDefault(c => c.Id == id.ToString(), noTracking);
}
///
/// Gibt eine Entität anhand der eindeutigen Id zurück
///
/// Id der Entität
/// Gibt an ob NoTRacking verwendet werden soll. Es werden keine Entitäten im EF-Speicher gehalten
/// Entität oder null, wenn nicht gefunden
public override async Task GetAsync(object id, bool noTracking = true)
{
return await Repository.SingleOrDefaultAsync(c => c.Id == id.ToString(), noTracking).ConfigureAwait(false);
}
#region Pull-Push Implementation
///
/// 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();
//Es gibt hier kein Push!
await Task.Delay(1);
return result;
}
///
/// 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)
{
//Zuerst lezte Aktivität holen...
DateTimeOffset? lastUpdate = null;
var lastSyncInfo = await _syncInfoPullService.GetAsync(nameof(NewsCategory));
if (lastSyncInfo != null)
lastUpdate = lastSyncInfo.LastUpdate;
var ctsQuery = new CancellationTokenSource(Common.Constants.PushPullTimeout);
var categoriesResult = await _communicationService.GetNewsCategoriesAsync(lastUpdate, language, accessToken, token);
if (categoriesResult.Success)
{
result = await HandleChangesAsync(categoriesResult.Value);
if(result.LastUpdate != null)
await _syncInfoPullService.AddOrUpddateAsync(nameof(NewsCategory), result.LastUpdate.Value).ConfigureAwait(false);
}
result.LastUpdate ??= lastUpdate;
}
return result;
}
#endregion
#region Private
///
/// Behandeln der Liste von News-Kategorien wenn welche vom Online-Store geholt werden.
///
/// Liste der Kategorien
/// Task
private async Task> HandleChangesAsync(List categories)
{
var result = new SyncResult();
//Je Kategorie durchgehen ob was gemacht werden soll
foreach (var newsCategory in categories)
{
var localCategory = await Repository.FirstOrDefaultAsync(c => c.Id == newsCategory.Id, false);
if (localCategory != null && localCategory.UpdatedAt < newsCategory.UpdatedAt)
{
if (localCategory.UpdatedAt >= newsCategory.UpdatedAt)
{
if (result.LastUpdate == null || result.LastUpdate < localCategory.UpdatedAt)
result.LastUpdate = localCategory.UpdatedAt;
continue;
}
var deleted = localCategory.Deleted == false && newsCategory.Deleted;
localCategory.Version = newsCategory.Version;
localCategory.UpdatedAt = newsCategory.UpdatedAt;
localCategory.Deleted = newsCategory.Deleted;
localCategory.Language = newsCategory.Language;
localCategory.Name = newsCategory.Name;
localCategory.Description = newsCategory.Description;
localCategory.Image = newsCategory.Image;
localCategory.ImageLanguage = newsCategory.ImageLanguage;
localCategory.OnlineStatus = newsCategory.OnlineStatus;
localCategory.Created = newsCategory.Created;
if(result.LastUpdate == null || result.LastUpdate < localCategory.UpdatedAt)
result.LastUpdate = localCategory.UpdatedAt;
if(!deleted)
result.Updated.Add(localCategory);
else
result.Deleted.Add(localCategory);
Repository.Update(localCategory);
}
if (localCategory == null)
{
localCategory = new NewsCategory
{
Id = newsCategory.Id,
Version = newsCategory.Version,
UpdatedAt = newsCategory.UpdatedAt,
Deleted = newsCategory.Deleted,
Language = newsCategory.Language,
Name = newsCategory.Name,
Description = newsCategory.Description,
Image = newsCategory.Image,
ImageLanguage = newsCategory.ImageLanguage,
OnlineStatus = newsCategory.OnlineStatus,
Created = newsCategory.Created
};
Repository.Add(localCategory);
if (result.LastUpdate == null || result.LastUpdate < localCategory.UpdatedAt)
result.LastUpdate = localCategory.UpdatedAt;
result.Added.Add(localCategory);
}
}
if (result.HasChanges)
{
try
{
await CommitAsync().ConfigureAwait(false);
}
catch (Exception ex)
{
var err = ex.Message;
}
}
return result;
}
#endregion
}
}