package org.atlasapi.output;

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

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 org.atlasapi.media.entity.Publisher;

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, equivalents);

        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 <T extends E> Ordering<Sourced> applicationEquivalentsOrdering(
            Application application,
            Iterable<T> equivalents
    ) {

        List<Publisher> applicationPublishers = application.getConfiguration()
                .getReadPrecedenceOrdering()
                .sortedCopy(application.getConfiguration().getEnabledReadSources());

        List<Publisher> equivPublishers = StreamSupport.stream(equivalents.spliterator(), false)
                .map(Sourced::getSource)
                .filter(publisher -> !applicationPublishers.contains(publisher))
                .distinct()
                .collect(Collectors.toList());

        return Ordering.explicit(
                ImmutableList.<Publisher>builder()
                        .addAll(applicationPublishers)
                        .addAll(equivPublishers)
                        .build()
        ).onResultOf(Sourceds.toPublisher());
    }

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

}

@@ -49,83 +49,8 @@
 ist;
-%0Aimport java.util.stream.Collectors;%0Aimport java.util.stream.StreamSupport;
 %0A%0Aim
@@ -232,45 +232,8 @@
 ed;%0A
-import org.atlasapi.entity.Sourceds;%0A
 impo
@@ -597,52 +597,8 @@
 ing;
-%0Aimport org.atlasapi.media.entity.Publisher;
 %0A%0Aim
@@ -1034,29 +1034,16 @@
 %3E merge(
-%0A            
 final Op
@@ -1056,28 +1056,16 @@
 %3CId%3E id,
-%0A           
  Iterabl
@@ -1117,21 +1117,16 @@
 lication
-%0A    
 ) %7B%0A    
@@ -1246,33 +1246,32 @@
 nts);%0A        %7D%0A
-%0A
         Ordering
@@ -1312,52 +1312,119 @@
 tion
-EquivalentsOrdering(application, equivalents
+.getConfiguration()%0A                .getReadPrecedenceOrdering()%0A                .onResultOf(Sourced::getSource
 );%0A%0A
@@ -2650,946 +2650,8 @@
  %7D%0A%0A
-    private %3CT extends E%3E Ordering%3CSourced%3E applicationEquivalentsOrdering(%0A            Application application,%0A            Iterable%3CT%3E equivalents%0A    ) %7B%0A%0A        List%3CPublisher%3E applicationPublishers = application.getConfiguration()%0A                .getReadPrecedenceOrdering()%0A                .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                .distinct()%0A                .collect(Collectors.toList());%0A%0A        return Ordering.explicit(%0A                ImmutableList.%3CPublisher%3Ebuilder()%0A                        .addAll(applicationPublishers)%0A                        .addAll(equivPublishers)%0A                        .build()%0A        ).onResultOf(Sourceds.toPublisher());%0A    %7D%0A%0A
     

Revert publisher ordering including other publishers (#521)