package org.atlasapi.persistence.media.entity;
import java.util.List;
import java.util.Set;
import org.atlasapi.media.entity.EntityType;
import org.atlasapi.media.entity.Episode;
import org.atlasapi.media.entity.Film;
import org.atlasapi.media.entity.Item;
import org.atlasapi.media.entity.ParentRef;
import org.atlasapi.media.entity.ReleaseDate;
import org.atlasapi.media.entity.ReleaseDate.ReleaseType;
import org.atlasapi.media.entity.Song;
import org.atlasapi.media.entity.Subtitles;
import org.atlasapi.media.entity.Version;
import org.atlasapi.persistence.ModelTranslator;
import org.atlasapi.persistence.content.mongo.DbObjectHashCodeDebugger;
import org.joda.time.Duration;
import org.joda.time.LocalDate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Function;
import com.google.common.collect.ComparisonChain;
import com.google.common.collect.Iterables;
import com.google.common.collect.Ordering;
import com.google.common.collect.Sets;
import com.metabroadcast.common.ids.NumberToShortStringCodec;
import com.metabroadcast.common.intl.Countries;
import com.metabroadcast.common.intl.Country;
import com.metabroadcast.common.persistence.translator.TranslatorUtils;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
public class ItemTranslator implements ModelTranslator<Item> {
private static final Logger log = LoggerFactory.getLogger(ItemTranslator.class);
public static final String CONTAINER = "container";
public static final String SERIES = "series";
public static final String SERIES_ID = "seriesId";
public static final String CONTAINER_ID = "containerId";
private static final String FILM_RELEASES_KEY = "releases";
private static final String FILM_SUBTITLES_KEY = "subtitles";
private static final String PART_NUMBER = "partNumber";
private static final String EPISODE_NUMBER = "episodeNumber";
private static final String SERIES_NUMBER = "seriesNumber";
private static final String SPECIAL = "special";
private static final String SHORT_DESCRIPTION = "shortDescription";
private static final String MEDIUM_DESCRIPTION = "mediumDescription";
private static final String LONG_DESCRIPTION = "longDescription";
private static final String TYPE_KEY = "type";
private static final String IS_LONG_FORM_KEY = "isLongForm";
private static final String EPISODE_SERIES_URI_KEY = "seriesUri";
private static final String FILM_WEBSITE_URL_KEY = "websiteUrl";
private static final String BLACK_AND_WHITE_KEY = "blackAndWhite";
private static final String COUNTRIES_OF_ORIGIN_KEY = "countries";
private final ContentTranslator contentTranslator;
private final DbObjectHashCodeDebugger dboHashCodeDebugger = new DbObjectHashCodeDebugger();
private final Function<DBObject, Subtitles> subtitlesFromDbo = new Function<DBObject, Subtitles>() {
@Override
public Subtitles apply(DBObject input) {
return new Subtitles(TranslatorUtils.toString(input, "language"));
}
};
private final Function<DBObject, ReleaseDate> releaseDateFromDbo = new Function<DBObject, ReleaseDate>() {
@Override
public ReleaseDate apply(DBObject input) {
LocalDate date = TranslatorUtils.toLocalDate(input, "date");
Country country = Countries.fromCode(TranslatorUtils.toString(input, "country"));
ReleaseType type = ReleaseType.valueOf(TranslatorUtils.toString(input, "type"));
return new ReleaseDate(date, country, type);
}
};
ItemTranslator(ContentTranslator contentTranslator, NumberToShortStringCodec idCodec) {
this.contentTranslator = contentTranslator;
}
public ItemTranslator(NumberToShortStringCodec idCodec) {
this(new ContentTranslator(idCodec), idCodec);
}
public Item fromDB(DBObject dbObject) {
return fromDBObject(dbObject, null);
}
@Override
public Item fromDBObject(DBObject dbObject, Item item) {
if (item == null) {
item = (Item) DescribedTranslator.newModel(dbObject);
}
contentTranslator.fromDBObject(dbObject, item);
item.setIsLongForm((Boolean) dbObject.get(IS_LONG_FORM_KEY));
item.setBlackAndWhite(TranslatorUtils.toBoolean(dbObject, BLACK_AND_WHITE_KEY));
item.setCountriesOfOrigin(Countries.fromCodes(TranslatorUtils.toSet(dbObject, COUNTRIES_OF_ORIGIN_KEY)));
if (dbObject.containsField(FILM_RELEASES_KEY)) {
item.setReleaseDates(Iterables.transform(TranslatorUtils.toDBObjectList(dbObject, FILM_RELEASES_KEY), releaseDateFromDbo));
}
if(dbObject.containsField(CONTAINER)) {
String containerUri = TranslatorUtils.toString(dbObject, CONTAINER);
Long containerId = TranslatorUtils.toLong(dbObject, CONTAINER_ID);
item.setParentRef(new ParentRef(containerUri, containerId));
}
if (item instanceof Episode) {
Episode episode = (Episode) item;
Long seriesId = TranslatorUtils.toLong(dbObject, SERIES_ID);
if (dbObject.containsField(SERIES)) {
String seriesUri = TranslatorUtils.toString(dbObject, SERIES);
episode.setSeriesRef(new ParentRef(seriesUri, seriesId));
}
episode.setPartNumber(TranslatorUtils.toInteger(dbObject, PART_NUMBER));
episode.setEpisodeNumber((Integer) dbObject.get(EPISODE_NUMBER));
episode.setSeriesNumber((Integer) dbObject.get(SERIES_NUMBER));
episode.setSpecial(TranslatorUtils.toBoolean(dbObject, SPECIAL));
episode.setShortDescription(TranslatorUtils.toString(dbObject, SHORT_DESCRIPTION));
episode.setMediumDescription(TranslatorUtils.toString(dbObject, MEDIUM_DESCRIPTION));
episode.setLongDescription(TranslatorUtils.toString(dbObject, LONG_DESCRIPTION));
if (dbObject.containsField(EPISODE_SERIES_URI_KEY)) {
episode.setSeriesRef(new ParentRef((String) dbObject.get(EPISODE_SERIES_URI_KEY),seriesId));
}
}
if (item instanceof Film) {
Film film = (Film) item;
film.setWebsiteUrl(TranslatorUtils.toString(dbObject, FILM_WEBSITE_URL_KEY));
if (dbObject.containsField(FILM_SUBTITLES_KEY)) {
film.setSubtitles(Iterables.transform(TranslatorUtils.toDBObjectList(dbObject, FILM_SUBTITLES_KEY), subtitlesFromDbo));
}
}
if (item instanceof Song) {
Song song = (Song) item;
song.setIsrc(TranslatorUtils.toString(dbObject, "isrc"));
Long duration = TranslatorUtils.toLong(dbObject, "duration");
if (duration != null) {
song.setDuration(Duration.standardSeconds(duration));
}
}
item.setReadHash(generateHashByRemovingFieldsFromTheDbo(dbObject));
return item;
}
public String hashCodeOf(Item item) {
return generateHashByRemovingFieldsFromTheDbo(toDB(item));
}
private String generateHashByRemovingFieldsFromTheDbo(DBObject dbObject) {
// don't include the last-fetched/update time in the hash
removeFieldsForHash(dbObject);
if (log.isTraceEnabled()) {
dboHashCodeDebugger.logHashCodes(dbObject, log);
}
return String.valueOf(dbObject.hashCode());
}
@SuppressWarnings("unchecked")
public void removeFieldsForHash(DBObject dbObject) {
contentTranslator.removeFieldsForHash(dbObject);
}
public DBObject toDB(Item item) {
return toDBObject(null, item);
}
@Override
public DBObject toDBObject(DBObject itemDbo, Item entity) {
itemDbo = contentTranslator.toDBObject(itemDbo, entity);
itemDbo.put(TYPE_KEY, EntityType.from(entity).toString());
encodeReleases(itemDbo, entity.getReleaseDates());
itemDbo.put(IS_LONG_FORM_KEY, entity.getIsLongForm());
TranslatorUtils.from(itemDbo, BLACK_AND_WHITE_KEY, entity.getBlackAndWhite());
if (! entity.getCountriesOfOrigin().isEmpty()) {
TranslatorUtils.fromIterable(itemDbo, Countries.toCodes(entity.getCountriesOfOrigin()), COUNTRIES_OF_ORIGIN_KEY);
}
if(entity.getContainer() != null) {
itemDbo.put(CONTAINER, entity.getContainer().getUri());
itemDbo.put(CONTAINER_ID, entity.getContainer().getId());
}
if (entity instanceof Episode) {
Episode episode = (Episode) entity;
if(episode.getSeriesRef() != null) {
itemDbo.put(SERIES_ID, episode.getSeriesRef().getId());
itemDbo.put(SERIES, episode.getSeriesRef().getUri());
}
TranslatorUtils.from(itemDbo, PART_NUMBER, episode.getPartNumber());
TranslatorUtils.from(itemDbo, EPISODE_NUMBER, episode.getEpisodeNumber());
TranslatorUtils.from(itemDbo, SERIES_NUMBER, episode.getSeriesNumber());
TranslatorUtils.from(itemDbo, SPECIAL, episode.getSpecial());
TranslatorUtils.from(itemDbo, SHORT_DESCRIPTION, episode.getShortDescription());
TranslatorUtils.from(itemDbo, MEDIUM_DESCRIPTION, episode.getMediumDescription());
TranslatorUtils.from(itemDbo, LONG_DESCRIPTION, episode.getLongDescription());
ParentRef series = episode.getSeriesRef();
if (series != null) {
TranslatorUtils.from(itemDbo, EPISODE_SERIES_URI_KEY, series.getUri());
}
}
if (entity instanceof Film) {
Film film = (Film) entity;
TranslatorUtils.from(itemDbo, FILM_WEBSITE_URL_KEY, film.getWebsiteUrl());
encodeSubtitles(itemDbo, film.getSubtitles());
}
if (entity instanceof Song) {
Song song = (Song) entity;
TranslatorUtils.from(itemDbo, "isrc", song.getIsrc());
if (song.getDuration() != null) {
TranslatorUtils.from(itemDbo, "duration", song.getDuration().getStandardSeconds());
}
}
return itemDbo;
}
private void encodeReleases(DBObject dbo, Set<ReleaseDate> releaseDates) {
if(!releaseDates.isEmpty()) {
BasicDBList values = new BasicDBList();
for(ReleaseDate releaseDate : releaseDates) {
DBObject releaseDateDbo = new BasicDBObject();
TranslatorUtils.fromLocalDate(releaseDateDbo, "date", releaseDate.date());
TranslatorUtils.from(releaseDateDbo, "country", releaseDate.country().code());
TranslatorUtils.from(releaseDateDbo, "type", releaseDate.type().toString());
values.add(releaseDateDbo);
}
dbo.put(FILM_RELEASES_KEY, values);
}
}
private void encodeSubtitles(DBObject dbo, Set<Subtitles> subtitles) {
if(!subtitles.isEmpty()) {
BasicDBList values = new BasicDBList();
for(Subtitles subtitle : subtitles) {
DBObject subtitleDbo = new BasicDBObject();
subtitleDbo.put("language", subtitle.code());
values.add(subtitleDbo);
}
dbo.put(FILM_SUBTITLES_KEY, values);
}
}
}