/* UtilLib - Copyright (c) 2005-2026 Vincent Calame - Exemole
 * Logiciel libre donné sous triple licence :
 * 1) selon les termes de la CeCILL V2
 * 2) selon les termes de l’EUPL V.1.1
 * 3) selon les termes de la GNU GPLv3
 * Voir le fichier licences.txt
 */


package net.mapeadores.util.misc;

import java.util.AbstractList;
import java.util.List;
import java.util.RandomAccess;


/**
 * Contient des méthodes statiques de traitement des tableaux comme
 * java.util.Arrays.
 *
 * @author Vincent Calame
 */
public class ArrayUtils {


    private ArrayUtils() {
    }

    /**
     * Déplace l'objet de l'index <code>i</code> à l'index <code>j</code>. Les
     * objets entre i et j sont avancés d'un cran si i < j ou reculé d'un cran si i
     * > j.
     */
    public static Object move(Object[] tab, int i, int j) {
        Object value = tab[i];
        if (i == j) {
            return value;
        }
        if (i < j) {
            System.arraycopy(tab, i + 1, tab, i, j - i);
        } else {
            System.arraycopy(tab, j, tab, j + 1, i - j);
        }
        tab[j] = value;
        return value;
    }

    /**
     * Déplace l'entier de l'index <code>i</code> à l'index <code>j</code>. Les
     * entiers entre i et j sont avancés d'un cran si i < j ou reculés d'un cran si i
     * > j.
     */
    public static int move(int[] tab, int i, int j) {
        int value = tab[i];
        if (i == j) {
            return value;
        }
        if (i < j) {
            System.arraycopy(tab, i + 1, tab, i, j - i);
        } else {
            System.arraycopy(tab, j, tab, j + 1, i - j);
        }
        tab[j] = value;
        return value;
    }

    /**
     * Supprime l'objet situé à l'index <code>i</code>. C'est l'index situé à
     * clearIndex qui prend la valeur null. si clearIndex est supérieur à i,
     * cela signifie que tab[clearindex] == null et que les valeurs entre i et
     * clearIndex compris, sont décalées. Si clearindex < i, c'est
     * tab[tab.length-1] qui prend la valeur null.
     */
    public static Object remove(Object[] tab, int i, int clearindex) {
        if (clearindex < i) {
            clearindex = tab.length - 1;
        }
        Object value = tab[i];
        int length = clearindex - i;
        if (length > 0) {
            System.arraycopy(tab, i + 1, tab, i, length);
        }
        tab[clearindex] = null;
        return value;
    }

    /**
     * Supprime l'entier situé à l'index <code>i</code>, tous les valeurs
     * suivantes entre l'index i et maxindex (compris) sont décalées, . Si
     * maxindex est inférieur à i (par exemple, égal à -1), toutes les valeurs
     * après i sont décalées. Retourne l'entier supprimé.
     */
    public static int remove(int[] tab, int i, int maxindex) {
        if (maxindex < i) {
            maxindex = tab.length - 1;
        }
        int value = tab[i];
        int lg = maxindex - i;
        if (lg > 0) {
            System.arraycopy(tab, i + 1, tab, i, lg);
        }
        tab[maxindex] = 0;
        return value;
    }

    /**
     * Ajoute un objet à un tableau si et seulement si il ne s'y trouve pas
     * déjà. Le tableau destination doit être un tableau de même type que
     * origine avec une taille supérieure de 1. Si l'objet se trouve déjà dans
     * origine, c'est origine qui est renvoyé sinon c'est destination qui est
     * une copie d'origine avec un élément supplémentaire qui est objet.
     */
    public static Object[] addUnique(Object[] origine, Object object, Object[] destination) {
        int length = origine.length;
        for (int i = 0; i < length; i++) {
            if (origine[i].equals(object)) {
                return origine;
            }
        }
        System.arraycopy(origine, 0, destination, 0, length);
        destination[length] = object;
        return destination;
    }

    /**
     * Supprime l'objet d'origine en renvoyant destination qui est un tableau de
     * longueur inférieur de 1 à origine. Si l'objet n'est pas dans origine,
     * c'est origine qui est renvoyé.
     */
    public static Object[] removeUnique(Object[] origine, Object object, Object[] destination) {
        int length = origine.length;
        int rmvIndex = -1;
        for (int i = 0; i < length; i++) {
            if (origine[i].equals(object)) {
                rmvIndex = i;
                break;
            }
        }
        if (rmvIndex == -1) {
            return origine;
        }
        if (rmvIndex == 0) {
            System.arraycopy(origine, 1, destination, 0, length - 1);
        } else if (rmvIndex == length - 1) {
            System.arraycopy(origine, 0, destination, 0, length - 1);
        } else {
            System.arraycopy(origine, 0, destination, 0, rmvIndex);
            System.arraycopy(origine, rmvIndex + 1, destination, rmvIndex, length - 1 - rmvIndex);
        }
        return destination;
    }

    public static int indexOf(Object[] objs, Object obj) {
        if (obj == null) {
            for (int i = 0; i < objs.length; i++) {
                if (objs[i] == null) {
                    return i;
                }
            }
            return -1;
        } else {
            for (int i = 0; i < objs.length; i++) {
                if ((objs[i] != null) && (obj.equals(objs[i]))) {
                    return i;
                }
            }
            return -1;
        }
    }

    /**
     * Retourne une copie du tableau <code>array</code> où tous les doublons ont
     * été supprimés.
     */
    public static int[] cleanCopy(int[] array) {
        int length = array.length;
        int[] result = new int[length];
        int p = 0;
        for (int i = 0; i < length; i++) {
            int val = array[i];
            if (i < (length - 1)) {
                boolean doublon = false;
                for (int j = i + 1; j < length; j++) {
                    if (val == array[j]) {
                        doublon = true;
                        break;
                    }
                }
                if (doublon) {
                    continue;
                }
            }
            result[p] = val;
            p++;
        }
        if (p == length) {
            return result;
        }
        int[] nv = new int[p];
        System.arraycopy(result, 0, nv, 0, p);
        return nv;
    }

    /**
     * Retourne <em>true</em> si la valeur <code>value</code> se trouve dans le
     * tableau <code>array</code> entre les index <code>index1</code> (compris)
     * et <code>index2</code> (non compris).
     */
    public static boolean containsBetweenIndexes(int[] array, int value, int index1, int index2) {
        for (int i = index1; i < index2; i++) {
            if (array[i] == value) {
                return true;
            }
        }
        return false;
    }

    public static boolean compare(int[] array1, int[] array2) {
        int length = array1.length;
        if (length != array2.length) {
            return false;
        }
        for (int i = 0; i < length; i++) {
            if (array1[i] != array2[i]) {
                return false;
            }
        }
        return true;
    }

    public static int[] checkIntArray(int[] array, int p) {
        if (p == array.length) {
            return array;
        }
        int[] result = new int[p];
        System.arraycopy(array, 0, result, 0, p);
        return result;
    }

    public static int[] merge(int[] array1, int[] array2) {
        int ln1 = array1.length;
        int ln2 = array2.length;
        int[] result = new int[ln1 + ln2];
        System.arraycopy(array1, 0, result, 0, ln1);
        System.arraycopy(array2, 0, result, ln1, ln2);
        return result;
    }

    public static Object[] toObjectArray(Object val1, Object val2) {
        Object[] array = new Object[2];
        array[0] = val1;
        array[1] = val2;
        return array;
    }

    public static Object[] toObjectArray(Object val1, Object val2, Object val3) {
        Object[] array = new Object[3];
        array[0] = val1;
        array[1] = val2;
        array[2] = val3;
        return array;
    }

    public static Object[] toObjectArray(Object val1, Object val2, Object val3, Object val4) {
        Object[] array = new Object[4];
        array[0] = val1;
        array[1] = val2;
        array[2] = val3;
        array[3] = val4;
        return array;
    }

    public static List<Object> wrap(Object[] array) {
        return new ObjectList(array);
    }


    private static class ObjectList extends AbstractList<Object> implements RandomAccess {

        private final Object[] array;

        private ObjectList(Object[] array) {
            this.array = array;
        }

        @Override
        public int size() {
            return array.length;
        }

        @Override
        public Object get(int index) {
            return array[index];
        }

    }

}
