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

import lombok.experimental.UtilityClass;
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 java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;

/**
 * Those methods implement "group" sort.
 * Methods sort provided collections according to order established by comparator and first "leading" collection.
 */
@UtilityClass
public final class CollectionSort {

    /**
     * Sorts provided collections according to order established by comparator and first "leading" collection.
     *
     * @param cmp cmp
     * @param collections collections
     */
    public static void sortIntArrays(IntComparator cmp, Object... collections) {
        NativeArraySort.quickSort(0, size(collections[0]), cmp, concat0(collections));
    }

    /**
     * Sorts provided collections according to order established by comparator and first "leading" collection.
     *
     * @param cmp cmp
     * @param collections collections
     */
    public static void sortArrays(Comparator<?> cmp, Object... collections) {
        NativeArraySort.quickSort(0, size(collections[0]), comparator(collections[0], cmp), concat0(collections));
    }

    /**
     * Creates concatenated swapper which swaps in one call elements inside all provided swappers
     *
     * @param swappers swappers
     * @return swapper
     */
    public static Swapper concat(Swapper... swappers) {
        return (a,b) -> {
            for(Swapper s : swappers) {
                s.swap(a,b);
            }
        };
    }

    private static Swapper concat0(Object... collections) {
        return concat(Arrays.stream(collections).map(CollectionSort::swapper).toArray(Swapper[]::new));

    }

    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 IntList) {
            return IntListUtils.swapper((IntList)object);
        }
        if(object instanceof List) {
            return ListUtils.swapper((List<?>)object);
        }
        if(object.getClass().isArray()) {
            return NativeArray.wrap(object);
        }
        throw new IllegalArgumentException("Unsupported collection: " + object.getClass());
    }

    @SuppressWarnings("unchecked")
    private static IntComparator comparator(Object object, Comparator cmp) {
        if(object == null) {
            throw new IllegalArgumentException("Collection is NULL");
        }
        if(object instanceof IntList) {
            IntList list = (IntList) object;
            return (a,b) -> cmp.compare(list.getInt(a), list.getInt(b));
        }
        if(object instanceof List) {
            List<?> list = (List<?>) object;
            return (a,b) -> cmp.compare(list.get(a), list.get(b));
        }
        if(object.getClass().isArray()) {
            return NativeArray.wrap(object).comparator(cmp);
        }
        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());
    }

}
