/*
 * @author Ranides Atterwim <ranides@gmail.com>
 * @copyright Ranides Atterwim
 * @license WTFPL
 * @url http://ranides.net/projects/assira
 */
package net.ranides.assira.collection.utils;

import net.ranides.assira.collection.IntComparator;
import net.ranides.assira.collection.Swapper;
import net.ranides.assira.collection.arrays.ArrayUtils;
import net.ranides.assira.collection.arrays.NativeArray;
import net.ranides.assira.collection.arrays.NativeArraySort;
import net.ranides.assira.collection.lists.IntList;
import net.ranides.assira.collection.lists.IntListUtils;
import net.ranides.assira.collection.lists.ListUtils;
import net.ranides.assira.generic.CompareUtils;

import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;

public final class CollectionSort {

    private CollectionSort() {
        // utility class
    }

    // @todo (assira #2) swapper/sorter for "connected" arrays
    //  zrobić to za pomocą fluent-api:
    //
    //  CollectionSorter
    //      .array(a)
    //      .list(b,c,d)
    //      .comparator(valuecomparator) // use cmp for values from "a"
    //      .comparator(cint) // use cmp for indexes from "a"
    //      .orderby(b) // use default comparator in "b" (or already specified"
    //      .sort()


    public static void sort1(Object collection) {
        // sort(comparator(collection), collection);
    }

    public static void sort2(IntComparator cmp, Object collection) {
        NativeArraySort.quickSort(0, size(collection), cmp, swapper(collection));
    }

    public static void msort1(Object... collections) {
//        msort(comparator(collections[0]), collections);
    }

    public static void msort2(Comparator<?> cmp, Object... collections) {
        NativeArraySort.quickSort(0, size(collections[0]), comparator(collections[0], cmp), concat(collections));
    }

    private static Swapper concat(Object... collections) {
        Swapper[] swappers = Arrays.stream(collections).map(CollectionSort::swapper).toArray(Swapper[]::new);
        return (a,b) -> {
            for(Swapper s : swappers) {
                s.swap(a,b);
            }
        };
    }

    private static Swapper swapper(Object object) {
        if(object == null) {
            throw new IllegalArgumentException("Collection is NULL");
        }
        if(object instanceof Swapper) {
            return (Swapper)object;
        }
        if(object instanceof List) {
            return ListUtils.swapper((List<?>)object);
        }
        if(object instanceof IntList) {
            return IntListUtils.swapper((IntList)object);
        }
        if(object.getClass().isArray()) {
            return NativeArray.wrap(object);
        }
        throw new IllegalArgumentException("Unsupported collection: " + object.getClass());
    }

    private static IntComparator comparator(Object object, Comparator cmp) {
        if(object == null) {
            throw new IllegalArgumentException("Collection is NULL");
        }
        if(object instanceof List) {
            List<?> list = (List<?>) object;
            return (a,b) -> cmp.compare(list.get(a), list.get(b));
        }
        if(object instanceof IntList) {
            IntList list = (IntList) object;
            return (a,b) -> cmp.compare(list.getInt(a), list.getInt(b));
        }
        if(object.getClass().isArray()) {
            return NativeArray.wrap(object).comparator(cmp);
        }
        throw new IllegalArgumentException("Unsupported collection: " + object.getClass());
    }

    private static IntComparator comparator(Object object) {
        if(object == null) {
            throw new IllegalArgumentException("Collection is NULL");
        }
        if(object instanceof IntComparator) {
            return (IntComparator)object;
        }
        if(object instanceof List) {
            List<?> list = (List<?>) object;
            return (a,b) -> CompareUtils.cmp(list.get(a), list.get(b));
        }
        if(object instanceof IntList) {
            IntList list = (IntList) object;
            return (a,b) -> CompareUtils.cmp(list.getInt(a), list.getInt(b));
        }
        if(object.getClass().isArray()) {
            return NativeArray.wrap(object).comparator();
        }
        throw new IllegalArgumentException("Unsupported collection: " + object.getClass());
    }

    private static int size(Object object) {
        if (object instanceof Collection) {
            return ((Collection) object).size();
        }
        if(object.getClass().isArray()) {
            return ArrayUtils.size(object);
        }
        throw new IllegalArgumentException("Unsupported collection: " + object.getClass());
    }

}
