package org.atlasapi.persistence.content.people;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import com.metabroadcast.applications.client.model.internal.Application;
import org.atlasapi.equiv.OutputContentMerger;
import org.atlasapi.media.entity.Identified;
import org.atlasapi.media.entity.LookupRef;
import org.atlasapi.media.entity.Person;
import org.atlasapi.media.entity.Publisher;
import org.atlasapi.persistence.content.PeopleQueryResolver;
import org.atlasapi.persistence.content.PeopleResolver;
import org.atlasapi.persistence.lookup.entry.LookupEntry;
import org.atlasapi.persistence.lookup.entry.LookupEntryStore;
import com.google.common.base.Function;
import com.google.common.base.Functions;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.Iterables;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Maps;
import com.metabroadcast.common.base.MorePredicates;
import com.metabroadcast.common.query.Selection;
public class EquivalatingPeopleResolver implements PeopleQueryResolver {
private static final Person DEFAULTING_TO_NULL = null;
private final PeopleResolver peopleResolver;
private final LookupEntryStore peopleLookupEntryStore;
private final OutputContentMerger outputContentMerger;
public EquivalatingPeopleResolver(PeopleResolver peopleResolver, LookupEntryStore peopleLookupEntryStore) {
this.peopleResolver = peopleResolver;
this.peopleLookupEntryStore = peopleLookupEntryStore;
this.outputContentMerger = new OutputContentMerger();
}
@Override
public Optional<Person> person(String uri, Application application) {
List<Person> people = people(ImmutableList.of(uri), application);
return Optional.fromNullable(Iterables.getOnlyElement(people, null));
}
@Override
public Optional<Person> person(Long id, Application application) {
List<Person> people = peopleByIds(ImmutableList.of(id), application);
return Optional.fromNullable(Iterables.getOnlyElement(people, null));
}
@Override
public Iterable<Person> people(Iterable<Publisher> publishers, Application application,
Selection selection) {
Iterable<LookupEntry> entries = peopleLookupEntryStore.entriesForPublishers(publishers, selection);
Map<String, LookupEntry> entriesIndex = Maps.uniqueIndex(entries, Functions.compose(LookupRef.TO_URI, LookupEntry.TO_SELF));
Map<String, Person> peopleIndex = Maps.uniqueIndex(peopleForEntries(entriesIndex, application), Identified.TO_URI);
ListMultimap<String, Person> idToPeople = keysToPeople(entriesIndex, peopleIndex, LookupRef.TO_URI);
return findOrMerge(peopleIndex.keySet(), idToPeople, Identified.TO_URI, application);
}
@Override
/*
* Find people for URIs by resolving entries for those URIs to createDefault an
* entry index.
* All required people are resolved from the set of all
* equivalents of the resolved entries, creating an index of required
* people.
* The requested URIs are transformed to people equivalence sets and
* merged as necessary.
*/
public List<Person> people(Iterable<String> uris, final Application application) {
Iterable<LookupEntry> entries = peopleLookupEntryStore.entriesForIdentifiers(uris, true);
Map<String, LookupEntry> entriesIndex = Maps.uniqueIndex(entries, LookupEntry.TO_ID);
Map<String, Person> peopleIndex = Maps.uniqueIndex(peopleForEntries(entriesIndex, application), Identified.TO_URI);
ListMultimap<String, Person> urisToPeople = keysToPeople(entriesIndex, peopleIndex, LookupRef.TO_URI);
return findOrMerge(uris, urisToPeople, Identified.TO_URI, application);
}
public List<Person> peopleByIds(Iterable<Long> ids, final Application application) {
Iterable<LookupEntry> entries = peopleLookupEntryStore.entriesForIds(ids);
Map<Long, LookupEntry> entriesIndex = Maps.uniqueIndex(entries, Functions.compose(LookupRef.TO_ID, LookupEntry.TO_SELF));
Map<Long, Person> peopleIndex = Maps.uniqueIndex(peopleForEntries(entriesIndex, application), Identified.TO_ID);
ListMultimap<Long, Person> idToPeople = keysToPeople(entriesIndex, peopleIndex, LookupRef.TO_ID);
return findOrMerge(ids, idToPeople, Identified.TO_ID, application);
}
private <T> List<Person> findOrMerge(Iterable<T> keys, ListMultimap<T, Person> keyToPeople,
Function<? super Person, T> personToKey, Application application) {
return application.getConfiguration().isPrecedenceEnabled() ? merge(keys, keyToPeople, application)
: find(keys, keyToPeople, personToKey);
}
private <T> List<Person> merge(Iterable<T> keys, ListMultimap<T, Person> keyToPeople, Application application) {
Builder<Person> result = ImmutableList.builder();
for (T key : keys) {
List<Person> people = keyToPeople.get(key);
Person person = merge(people, application);
if (person != null) {
result.add(person);
}
}
return result.build();
}
private <T> List<Person> find(Iterable<T> keys, ListMultimap<T, Person> keyToPeople, Function<? super Person, T> personToKey) {
Builder<Person> result = ImmutableList.builder();
for (T key : keys) {
List<Person> people = keyToPeople.get(key);
Person person = find(key, people, personToKey);
if (person != null) {
result.add(person);
}
}
return result.build();
}
private <T> Iterable<Person> peopleForEntries(Map<T, LookupEntry> entriesIndex,
Application application) {
if (entriesIndex.isEmpty()) {
return ImmutableList.of();
}
Iterable<Set<LookupRef>> entryRefs = Iterables.transform(entriesIndex.values(),LookupEntry.TO_EQUIVS);
Predicate<LookupRef> sourceFilter = MorePredicates.transformingPredicate(LookupRef.TO_SOURCE, Predicates.in(application.getConfiguration().getEnabledReadSources()));
Iterable<LookupRef> enabledRefs = Iterables.filter(Iterables.concat(entryRefs), sourceFilter);
return peopleResolver.people(enabledRefs);
}
private <T> ListMultimap<T,Person> keysToPeople(Map<T, LookupEntry> entriesIndex,
Map<T, Person> peopleIndex, Function<LookupRef, T> refToKey) {
Function<LookupRef, Person> refToPerson = Functions.compose(Functions.forMap(peopleIndex, DEFAULTING_TO_NULL), refToKey);
ImmutableListMultimap.Builder<T,Person> result = ImmutableListMultimap.builder();
for (Entry<T, LookupEntry> entryMapping : entriesIndex.entrySet()) {
Set<LookupRef> equivs = entryMapping.getValue().equivalents();
Iterable<Person> people = Iterables.filter(Iterables.transform(equivs, refToPerson), Predicates.notNull());
result.putAll(entryMapping.getKey(), setEquivalentToFields(ImmutableList.copyOf(people), equivs));
}
return result.build();
}
private Person merge(List<Person> people, Application application) {
if (people == null || people.isEmpty()) {
return null;
}
return Iterables.getFirst(outputContentMerger.merge(application, people), null);
}
private <T> Person find(T key, List<Person> people, Function<? super Person, T> personToKey) {
if (people == null || people.isEmpty()) {
return null;
}
for (Person person : people) {
if (key.equals(personToKey.apply(person))) {
return person;
}
}
return null;
}
private List<Person> setEquivalentToFields(List<Person> people, Set<LookupRef> equivs) {
for (Person person : people) {
person.setEquivalentTo(equivs);
}
return people;
}
}