namespace Modules.Library.Domain.Entities.MediaContent.CommonProperties; public class CommonProperties : ValueObject { private readonly List _names = []; public IReadOnlyCollection Names => _names.AsReadOnly(); public MediaInfo? Preview { get; private set; } private readonly List _descriptions = []; public IReadOnlyCollection Descriptions => _descriptions.AsReadOnly(); private readonly List _genres = []; public IReadOnlyCollection Genres => _genres.AsReadOnly(); private readonly List _relatedContent = []; public IReadOnlyCollection RelatedContent => _relatedContent.AsReadOnly(); public DateTimeOffset? AnnouncementDate { get; private set; } public DateTimeOffset? EstimatedReleaseDate { get; private set; } public DateTimeOffset? ReleaseDate { get; private set; } internal CommonProperties() { } private CommonProperties(Models.CommonProperties commonProperties) { Preview = commonProperties.Preview != null ? new MediaInfo(commonProperties.Preview.Url, commonProperties.Preview.Type, commonProperties.Preview.ContentType) : null; AnnouncementDate = commonProperties.AnnouncementDate; EstimatedReleaseDate = commonProperties.EstimatedReleaseDate; ReleaseDate = commonProperties.ReleaseDate; _names.AddRange(commonProperties.Names.Select(NameItem.New)); _descriptions.AddRange(commonProperties.Descriptions.Select(Description.New)); _genres.AddRange(commonProperties.Genres.Select(GenreProportion.New)); _relatedContent.AddRange(commonProperties.RelatedContent.Select(q => new MediaInfo(q.Url, q.Type, q.ContentType))); } internal CommonProperties(IEnumerable names) { foreach (var name in names) { AddName(name.Type, name.LanguageId, name.Value); } } internal static CommonProperties New(Models.CommonProperties commonProperties) => new(commonProperties); #region Name public void AddName(NameType nameType, Guid languageId, string value) { switch (nameType) { case NameType.Original: AddNameOriginal(languageId, value); break; case NameType.OriginalInAnotherLanguage: AddNameOriginalInAnotherLanguage(languageId, value); break; case NameType.Translation: AddNameTranslation(languageId, value); break; case NameType.Abbreviation: AddNameAbbreviation(languageId, value); break; default: throw new NotImplementedException(); } } private void AddNameOriginal(Guid languageId, string value) { if (_names.Any(q => q.Type == NameType.Original)) throw new Exception("Original name is already exist."); var name = new NameItem(languageId, NameType.Original, value); _names.Add(name); } private void AddNameOriginalInAnotherLanguage(Guid languageId, string value) { if (_names.FirstOrDefault(q => q.Type == NameType.Original)?.LanguageId == languageId) throw new Exception("Language must not match original name language"); if (_names.Any(q => q.Type == NameType.OriginalInAnotherLanguage && q.LanguageId == languageId)) throw new Exception("Name in following language is already exist."); var name = new NameItem(languageId, NameType.OriginalInAnotherLanguage, value); _names.Add(name); } private void AddNameTranslation(Guid languageId, string value) { var name = new NameItem(languageId, NameType.Translation, value); _names.Add(name); } private void AddNameAbbreviation(Guid languageId, string value) { var name = new NameItem(languageId, NameType.Abbreviation, value); _names.Add(name); } public void RemoveName(NameItem nameItem) { var name = GetName(nameItem); if (name.Type == NameType.Original) throw new Exception($"Unable to remove original name"); _names.Remove(name); } public void SetNameValue(NameItem nameItem, Guid? languageId, string? value) { var name = GetName(nameItem); var newValue = value?.Trim(); if (string.IsNullOrWhiteSpace(newValue)) throw new ArgumentNullException(nameof(value)); if (nameItem.Type != NameType.Original && _names.Any(q => q.LanguageId == (languageId ?? nameItem.LanguageId) && q.Value == (newValue ?? nameItem.Value))) throw new Exception("Name item with same language and same value is already exists"); else if (nameItem.Type == NameType.Original && languageId.HasValue && _names.Any(q => q.LanguageId == languageId && q.Type == NameType.OriginalInAnotherLanguage)) throw new Exception("Could not change original name language to one of \"original in another language\" item's one"); if (languageId.HasValue) name.SetLanguage(languageId.Value); if (!string.IsNullOrWhiteSpace(newValue)) name.SetValue(newValue); } private NameItem GetName(NameItem name) => _names.FirstOrDefault(q => q == name) ?? throw new Exception($"Name with is not found"); #endregion public void SetPreview(string url, MediaInfoType type, string contentType) { if (string.IsNullOrWhiteSpace(url)) throw new ArgumentNullException(nameof(url)); Preview ??= new MediaInfo(url, type, contentType); Preview.SetUrl(url); Preview.SetType(type); Preview.SetContentType(contentType); } public void DeletePreview() => Preview = null; #region Description public void AddOriginalDescription(Guid languageId, string value) { if (_descriptions.Any(q => q.IsOriginal)) throw new Exception("Original description is already exist."); var description = new Description(languageId, true, value); _descriptions.Add(description); } public void AddNotOriginalDescription(Guid languageId, string value) { var description = new Description(languageId, false, value); _descriptions.Add(description); } public void RemoveDescription(Description descriptionItem) { var description = GetDescription(descriptionItem); if (description.IsOriginal) throw new Exception($"Unable to remove original description"); _descriptions.Remove(description); //TODO: valueobject convert //description.SetDeleted(); } public void SetDescriptionValue(Description descriptionItem, Guid? languageId, string? value) { var description = GetDescription(descriptionItem); var newValue = value?.Trim(); if (string.IsNullOrWhiteSpace(newValue)) throw new ArgumentNullException(nameof(value)); if (!descriptionItem.IsOriginal && _descriptions.Any(q => q.LanguageId == (languageId ?? descriptionItem.LanguageId) && q.Value == (newValue ?? descriptionItem.Value))) throw new Exception("Descriptoin item with with same value is already exists"); if (languageId.HasValue) descriptionItem.SetLanguage(languageId.Value); if (!string.IsNullOrWhiteSpace(newValue)) descriptionItem.SetValue(newValue); } private Description GetDescription(Description description) { return _descriptions.FirstOrDefault(q => q == description) ?? throw new Exception($"Description is not found"); } #endregion #region Genre public void AddGenre(Guid genreId, decimal? proportion = null) { if (_genres.Any(q => q.GenreId == genreId)) throw new Exception("Genre is already in the list"); var genreProportionItem = new GenreProportion(genreId, proportion); _genres.Add(genreProportionItem); if (proportion.HasValue) genreProportionItem.SetProportion(proportion); } public void SetGenreProportion(Guid genreId, decimal? value = null) { var genreProportion = GetGenreProportionItem(genreId); genreProportion.SetProportion(value); } public void RemoveGenre(Guid genreId) { var genreProportion = GetGenreProportionItem(genreId); _genres.Remove(genreProportion); } private GenreProportion GetGenreProportionItem(Guid genreId) { return _genres.FirstOrDefault(q => q.GenreId == genreId) ?? throw new Exception($"Genre proportion is not found"); } /* private GenreProportion GetGenreProportionItem(GenreProportion genreProportionItem) { return _genres.FirstOrDefault(q => q == genreProportionItem) ?? throw new Exception($"Genre proportion is not found"); } */ #endregion #region RelatedContent public void AddRelatedContent(string url, MediaInfoType type, string contentType) { if (string.IsNullOrWhiteSpace(url)) throw new ArgumentNullException(nameof(url)); CheckIfCanAddOrEdit(url, type); _relatedContent.Add(new MediaInfo(url, type, contentType)); } public void EditRelatedContent(MediaInfo relatedContentItem, string? url, MediaInfoType? type, string? contentType) { var relatedContent = GetRelatedContent(relatedContentItem); CheckIfCanAddOrEdit(url, type); if (!string.IsNullOrWhiteSpace(url)) relatedContent.SetUrl(url); if (type.HasValue) relatedContent.SetType(type.Value); if (!string.IsNullOrWhiteSpace(contentType)) relatedContent.SetContentType(contentType); } public void RemoveRelatedContent(MediaInfo relatedContentItem) { var relatedContent = GetRelatedContent(relatedContentItem); _relatedContent.Remove(relatedContent); //TODO: valueobject convert } private void CheckIfCanAddOrEdit(string? url, MediaInfoType? type) { if (string.IsNullOrWhiteSpace(url) && !type.HasValue) return; var query = _relatedContent.AsQueryable(); if (!string.IsNullOrWhiteSpace(url)) query = query.Where(q => q.Url == url); if (type.HasValue) query = query.Where(q => q.Type == type); if (query.Any()) throw new Exception("Related content with same url and same type is already exists"); } private MediaInfo GetRelatedContent(MediaInfo relatedContentItem) { return _relatedContent.FirstOrDefault(q => q == relatedContentItem) ?? throw new Exception($"Related content is not found"); } #endregion public void SetAnnouncementDate(DateTimeOffset? value) { AnnouncementDate = value; } public void SetEstimatedReleaseDate(DateTimeOffset? value) { //if (value == default) throw new ArgumentNullException(nameof(value)); if (value.HasValue && value.Value == default) throw new ArgumentOutOfRangeException(nameof(value)); if (AnnouncementDate.HasValue && value <= AnnouncementDate.Value) throw new Exception("Estimated release date can not be less or equal to announcement date"); EstimatedReleaseDate = value; } public void SetReleaseDate(DateTimeOffset? value) { if (value.HasValue && value.Value == default) throw new ArgumentOutOfRangeException(nameof(value)); if (AnnouncementDate.HasValue && value <= AnnouncementDate.Value) throw new Exception("Release date can not be less or equal to announcement date"); ReleaseDate = value; } protected override IEnumerable GetEqualityComponents() { foreach (var name in _names) { yield return name; } yield return Preview; foreach (var descriptions in _descriptions) { yield return descriptions; } foreach (var genre in _genres) { yield return genre; } foreach (var relatedContent in _relatedContent) { yield return relatedContent; } yield return AnnouncementDate; yield return EstimatedReleaseDate; yield return ReleaseDate; } }