package org.atlasapi.output;

import java.util.List;

import com.metabroadcast.applications.client.model.internal.Application;
import org.atlasapi.entity.Id;
import org.atlasapi.entity.Identifiable;
import org.atlasapi.entity.Sourced;
import org.atlasapi.entity.Sourceds;
import org.atlasapi.equivalence.ApplicationEquivalentsMerger;
import org.atlasapi.equivalence.Equivalable;

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.Iterables;
import com.google.common.collect.Ordering;

import static com.google.common.base.Preconditions.checkNotNull;

public class StrategyBackedEquivalentsMerger<E extends Equivalable<E>>
        implements ApplicationEquivalentsMerger<E> {

    private final EquivalentsMergeStrategy<E> strategy;

    public StrategyBackedEquivalentsMerger(EquivalentsMergeStrategy<E> strategy) {
        this.strategy = checkNotNull(strategy);
    }

    @Override
    public <T extends E> List<T> merge(final Optional<Id> id, Iterable<T> equivalents,
            Application application) {
        if (!application.getConfiguration().isPrecedenceEnabled()) {
            return ImmutableList.copyOf(equivalents);
        }
        Ordering<Sourced> equivsOrdering = applicationEquivalentsOrdering(application);
        ImmutableList<T> sortedEquivalents = equivsOrdering.immutableSortedCopy(equivalents);
        if (sortedEquivalents.isEmpty()) {
            return ImmutableList.of();
        }
        if (trivialMerge(sortedEquivalents)) {
            return ImmutableList.of(
                    strategy.merge(
                            Iterables.getFirst(sortedEquivalents, null),
                            ImmutableList.of(),
                            application
                    )
            );
        }
        T chosen = sortedEquivalents.get(0);

        if (id.isPresent()) {
            Optional<T> requested = Iterables.tryFind(equivalents, idIs(id.get()));

            if (requested.isPresent()
                    && chosen.getSource().equals(requested.get().getSource())) {
                chosen = requested.get();
            }
        }

        Iterable<T> notChosen = Iterables.filter(
                sortedEquivalents,
                Predicates.not(idIs(chosen.getId()))
        );
        return ImmutableList.of(strategy.merge(chosen, notChosen, application));
    }

    private boolean trivialMerge(ImmutableList<?> sortedEquivalents) {
        return sortedEquivalents.size() == 1;
    }

    private Ordering<Sourced> applicationEquivalentsOrdering(Application application) {
        return application.getConfiguration()
                .getReadPrecedenceOrdering()
                .onResultOf(Sourceds.toPublisher());
    }

    private Predicate<Identifiable> idIs(final Id id) {
        return input -> input.getId().equals(id);
    }

}

@@ -45,16 +45,91 @@
 il.List;
+%0Aimport java.util.stream.Collectors;%0Aimport java.util.stream.StreamSupport;
 %0A%0Aimport
@@ -705,16 +705,60 @@
 rdering;
+%0Aimport org.atlasapi.media.entity.Publisher;
 %0A%0Aimport
@@ -1190,16 +1190,29 @@
 %3E merge(
+%0A            
 final Op
@@ -1225,16 +1225,28 @@
 %3CId%3E id,
+%0A           
  Iterabl
@@ -1290,32 +1290,37 @@
 tion application
+%0A    
 ) %7B%0A        if (
@@ -1432,32 +1432,33 @@
 nts);%0A        %7D%0A
+%0A
         Ordering
@@ -1526,19 +1526,33 @@
 lication
+, equivalents
 );%0A
+%0A
         
@@ -1629,32 +1629,33 @@
 y(equivalents);%0A
+%0A
         if (sort
@@ -2777,16 +2777,30 @@
  private
+ %3CT extends E%3E
  Orderin
@@ -2841,16 +2841,29 @@
 rdering(
+%0A            
 Applicat
@@ -2877,28 +2877,71 @@
 lication
+,%0A            Iterable%3CT%3E equivalents%0A    
 ) %7B%0A
+%0A
         
 return a
@@ -2932,22 +2932,55 @@
         
-return
+List%3CPublisher%3E applicationPublishers =
  applica
@@ -3064,16 +3064,584 @@
         
+.sortedCopy(application.getConfiguration().getEnabledReadSources());%0A%0A        List%3CPublisher%3E equivPublishers = StreamSupport.stream(equivalents.spliterator(), false)%0A                .map(Sourced::getSource)%0A                .filter(publisher -%3E !applicationPublishers.contains(publisher))%0A                .collect(Collectors.toList());%0A%0A        return Ordering.explicit(%0A                ImmutableList.%3CPublisher%3Ebuilder()%0A                        .addAll(applicationPublishers)%0A                        .addAll(equivPublishers)%0A                        .build()%0A        )
 .onResul

fix publisher missing from explicit ordering (#517)