package com.mocircle.cidrawing.utils;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class ListUtils {

    /**
     * Convert integer list to int array.
     *
     * @param intList integer list
     * @return int array
     */
    public static int[] toIntArray(List<Integer> intList) {
        int[] result = new int[intList.size()];
        int index = 0;
        for (Integer i : intList) {
            result[index++] = i;
        }
        return result;
    }

    /**
     * Shift object in the list
     *
     * @param list   list
     * @param index  object index
     * @param offset offset value can be positive or negative.
     * @return new index of the object
     */
    public static int shiftItem(List<?> list, int index, int offset) {
        if (offset == 0 || index < 0) {
            return 0;
        }
        if (offset > 0) {
            int end = index + offset + 1;
            if (end > list.size()) {
                end = list.size();
            }
            Collections.rotate(list.subList(index, end), -1);
            return end - 1;
        } else {
            int start = index + offset;
            if (start < 0) {
                start = 0;
            }
            Collections.rotate(list.subList(start, index + 1), 1);
            return start;
        }
    }

    /**
     * Shift object to the top of list
     *
     * @param list  list
     * @param index object index
     */
    public static void shiftItemToFront(List<?> list, int index) {
        if (index >= 0) {
            Collections.rotate(list.subList(index, list.size()), -1);
        }
    }

    /**
     * Shift object to the bottom of list
     *
     * @param list  list
     * @param index object index
     */
    public static void shiftItemToBack(List<?> list, int index) {
        if (index >= 0) {
            Collections.rotate(list.subList(0, index + 1), 1);
        }
    }

    /**
     * Shift objects in the list, objects will try to move to the furthest distance.
     * <pre>
     * e.g. ABCED -> Shift AC with offset=5 -> BEDAC
     * </pre>
     *
     * @param list       list
     * @param indexArray object index array
     * @param offset     offset value can be positive or negative
     */
    public static void shiftItemsAsMuchAsPossible(List<?> list, int[] indexArray, int offset) {
        Arrays.sort(indexArray);
        if (offset > 0) {
            int last = list.size();
            for (int i = indexArray.length - 1; i >= 0; i--) {
                last = shiftItem(list.subList(0, last), indexArray[i], offset);
            }
        } else {
            int last = -1;
            for (int i = 0; i < indexArray.length; i++) {
                int index = indexArray[i] - (last + 1);
                last = shiftItem(list.subList(last + 1, list.size()), index, offset);
            }
        }
    }

    /**
     * Shift objects in the list, objects will keep the distance between them.
     * <pre>
     * e.g. ABCED -> Shift AC with offset=5 -> BEADC
     * </pre>
     *
     * @param list       list
     * @param indexArray object index array
     * @param offset     offset value can be positive or negative
     */
    public static void shiftItemsWithFixedDistance(List<?> list, int[] indexArray, int offset) {
        Arrays.sort(indexArray);
        int unionOffset = computeUnionOffset(list, indexArray, offset);
        shiftItemsDirectly(list, indexArray, unionOffset);
    }

    /**
     * Shift object directly without any pre-processing
     *
     * @param list       list
     * @param indexArray object index array
     * @param offset     offset value can be positive or negative
     */
    private static void shiftItemsDirectly(List<?> list, int[] indexArray, int offset) {
        if (offset > 0) {
            for (int i = indexArray.length - 1; i >= 0; i--) {
                shiftItem(list, indexArray[i], offset);
            }
        } else {
            for (int i = 0; i < indexArray.length; i++) {
                shiftItem(list, indexArray[i], offset);
            }
        }
    }

    /**
     * Calculate a offset for all objects in list.which make items will not shift out of the list.
     *
     * @param list       list
     * @param indexArray object index array
     * @param offset     initial offset value
     * @return new offset value
     */
    private static int computeUnionOffset(List<?> list, int[] indexArray, int offset) {
        if (offset > 0) {
            int unionIndex = indexArray[indexArray.length - 1];
            if (unionIndex + offset < list.size()) {
                return offset;
            } else {
                return list.size() - 1 - unionIndex;
            }
        } else {
            int unionIndex = indexArray[0];
            if (unionIndex + offset >= 0) {
                return offset;
            } else {
                return -unionIndex;
            }
        }
    }

}