/**
 * Copyright 2014 Netflix, Inc.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package rx.operators;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import rx.Observable.Operator;
import rx.Subscriber;
import rx.functions.Func2;

/**
 * Return an Observable that emits the items emitted by the source Observable, in a sorted order
 * (each item emitted by the Observable must implement Comparable with respect to all other items
 * in the sequence, or you must pass in a sort function).
 * <p>
 * <img width="640" src="https://github.com/Netflix/RxJava/wiki/images/rx-Observers/toSortedList.png">
 * 
 * @param <T>
 */
public final class OperatorToObservableSortedList<T> implements Operator<List<T>, T> {
    private final Func2<? super T, ? super T, Integer> sortFunction;

    @SuppressWarnings("unchecked")
    public OperatorToObservableSortedList() {
        this.sortFunction = defaultSortFunction;
    }

    public OperatorToObservableSortedList(Func2<? super T, ? super T, Integer> sortFunction) {
        this.sortFunction = sortFunction;
    }

    @Override
    public Subscriber<? super T> call(final Subscriber<? super List<T>> o) {
        return new Subscriber<T>(o) {

            final List<T> list = new ArrayList<T>();

            @Override
            public void onCompleted() {
                try {

                    // sort the list before delivery
                    Collections.sort(list, new Comparator<T>() {

                        @Override
                        public int compare(T o1, T o2) {
                            return sortFunction.call(o1, o2);
                        }

                    });

                    o.onNext(Collections.unmodifiableList(list));
                    o.onCompleted();
                } catch (Throwable e) {
                    onError(e);
                }
            }

            @Override
            public void onError(Throwable e) {
                o.onError(e);
            }

            @Override
            public void onNext(T value) {
                list.add(value);
            }

        };
    }

    // raw because we want to support Object for this default
    @SuppressWarnings("rawtypes")
    private static Func2 defaultSortFunction = new DefaultComparableFunction();

    private static class DefaultComparableFunction implements Func2<Object, Object, Integer> {

        // unchecked because we want to support Object for this default
        @SuppressWarnings("unchecked")
        @Override
        public Integer call(Object t1, Object t2) {
            Comparable<Object> c1 = (Comparable<Object>) t1;
            Comparable<Object> c2 = (Comparable<Object>) t2;
            return c1.compareTo(c2);
        }

    }
}
