/*
 * Copyright (c) 2009 JSCAPE
 * 1147 S. 53rd Pl., Mesa, Arizona, 85206, U.S.A.
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of
 * JSCAPE. ("Confidential Information").  You shall not disclose such
 * Confidential Information and shall use it only in accordance with
 * the terms of the license agreement you entered into with JSCAPE.
 *
 */

package com.jscape.util;

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

/**
 * @author Alex Usun
 */
public class ArrayTools {

    public static <T> boolean isValidArray(T[] value) {
        return value != null && value.length > 0;
    }

    public static <T> boolean isValidArray(byte[] value) {
        return value != null && value.length > 0;
    }

    public static <T> boolean isValidArray(short[] value) {
        return value != null && value.length > 0;
    }

    public static <T> boolean isValidArray(char[] value) {
        return value != null && value.length > 0;
    }

    public static <T> boolean isValidArray(int[] value) {
        return value != null && value.length > 0;
    }

    public static <T> boolean isValidArray(long[] value) {
        return value != null && value.length > 0;
    }

    @SafeVarargs
    public static <T> T[] reverse(T... array) {
        T[] reversed = array.clone();
        for (int i = 0; i < reversed.length; ++i) {
            reversed[i] = array[array.length - i - 1];
        }

        return reversed;
    }

    public static <T> T[] sort(T[] array) {
        Arrays.sort(array);
        return array;
    }

    public static <T> T[] sort(T[] array, Comparator<T> comparator) {
        Arrays.sort(array, comparator);
        return array;
    }

    public static byte[] fill(byte[] buffer, int offset, int length, byte value) {
        for (int i = offset, size = offset + length; i < size; ++i) {
            buffer[i] = value;
        }

        return buffer;
    }

    public static byte[] fill(byte[] buffer, byte value) {
        return fill(buffer, 0, buffer.length, value);
    }

    public static <T> boolean contains(T[] array, T value) {
        for (int i = 0; i < array.length; ++i) {
            T candidate = array[i];
            if (candidate != null && candidate.equals(value)) {
                return true;
            }
        }

        return false;
    }

    public static boolean contains(byte[] array, byte value) {
        for (int i = 0; i < array.length; ++i) {
            if (array[i] == value) {
                return true;
            }
        }

        return false;
    }

    public static boolean contains(short[] array, short value) {
        for (int i = 0; i < array.length; ++i) {
            if (array[i] == value) {
                return true;
            }
        }

        return false;
    }

    public static boolean contains(int[] array, int value) {
        for (int i = 0; i < array.length; ++i) {
            if (array[i] == value) {
                return true;
            }
        }

        return false;
    }

    public static boolean contains(long[] array, long value) {
        for (int i = 0; i < array.length; ++i) {
            if (array[i] == value) {
                return true;
            }
        }

        return false;
    }

    public static <T, V> V[] copy(T[] base, int baseOffset, V[] copy, int copyOffset, int length) {
        System.arraycopy(base, baseOffset, copy, copyOffset, length);
        return copy;
    }

    public static <T, V> V[] copy(T[] base, int baseOffset, V[] copy) {
        return copy(base, baseOffset, copy, 0, copy.length);
    }

    public static <T, V> V[] copy(T[] source, V[] destination) {
        return copy(source, 0, destination);
    }

    public static byte[] copy(byte[] base, int baseOffset, byte[] copy, int copyOffset, int length) {
        System.arraycopy(base, baseOffset, copy, copyOffset, length);
        return copy;
    }

    public static byte[] copy(byte[] base, int baseOffset, byte[] copy) {
        return copy(base, baseOffset, copy, 0, copy.length);
    }

    public static byte[] copy(byte[] base, byte[] copy) {
        return copy(base, 0, copy);
    }

    public static byte[] copy(byte[] data, int beginIndex, int endIndex) {
        return copy(data, beginIndex, new byte[endIndex - beginIndex], 0, endIndex - beginIndex);
    }

    public static byte[] concat(byte[]... parts) {
        int totalLength = 0;
        for (byte[] part : parts) {
            totalLength += part.length;
        }

        byte[] ret = new byte[totalLength];

        int offset = 0;
        for (byte[] part : parts) {
            System.arraycopy(part, 0, ret, offset, part.length);
            offset += part.length;
        }

        return ret;
    }

    public static byte[] concat(byte[] partOne, byte partTwo) {
        byte[] ret = new byte[partOne.length + 1];
        System.arraycopy(partOne, 0, ret, 0, partOne.length);
        ret[partOne.length] = partTwo;

        return ret;
    }

    public static boolean starts(byte[] data, byte[] with) {
        return starts(data, with, 0);
    }

    public static boolean starts(byte[] data, byte[] with, int beginIndex) {
        if (beginIndex < 0 || beginIndex > data.length - with.length) {
            return false;
        }

        int withIndex = 0;
        int count = with.length;

        while (--count >= 0) {
            if (data[beginIndex++] != with[withIndex++]) {
                return false;
            }
        }

        return true;
    }

    public static boolean ends(byte[] data, byte[] with, int lastIndex) {
        return starts(data, with, lastIndex - with.length);
    }

    public static boolean ends(byte[] data, byte[] with) {
        return ends(data, with, data.length);
    }

    public static int index(byte[] data, byte b) {
        return index(data, b, 0);
    }

    public static int index(byte[] data, byte b, int beginIndex) {
        return index(data, b, beginIndex, data.length);
    }

    public static int index(byte[] data, byte b, int beginIndex, int endIndex) {
        for (int i = beginIndex; i < endIndex; ++i) {
            if (b == data[i]) {
                return i;
            }
        }

        return -1;
    }

    public static int index(byte[] data, byte[] value) {
        return index(data, value, 0);
    }

    public static int index(byte[] data, byte[] value, int beginIndex) {
        return index(data, value, beginIndex, data.length);
    }

    public static int index(byte[] data, byte[] value, int beginIndex, int endIndex) {
        for (int i = beginIndex; i < endIndex; ++i) {
            if (data[i] != value[0]) {
                continue;
            }

            int j = 0;
            for (; j < value.length && i < endIndex; ++j, ++i) {
                if (data[i] != value[j]) {
                    break;
                }
            }

            if (j == value.length && i <= endIndex) {
                return i - j;
            }
        }

        return -1;
    }

    public static int lastIndex(byte[] data, byte b) {
        return lastIndex(data, b, data.length - 1);
    }

    public static int lastIndex(byte[] data, byte b, int beginIndex) {
        for (int i = data.length - 1; i >= beginIndex; --i) {
            if (b == data[i]) {
                return i;
            }
        }

        return -1;
    }

    public static byte[] subarray(byte[] data, int beginIndex) {
        return subarray(data, beginIndex, data.length);
    }

    public static byte[] subarray(byte[] data, int beginIndex, int endIndex) {
        byte[] ret = new byte[endIndex - beginIndex];
        System.arraycopy(data, beginIndex, ret, 0, ret.length);

        return ret;
    }

    public static byte[][] split(byte[] data, byte[] regex) {
        List<byte[]> ret = new LinkedList<byte[]>();
        int offset = 0;

        while (offset < data.length) {
            int index = index(data, regex, offset);
            if (index == -1) {
                break;
            }
            ret.add(subarray(data, offset, index));
            offset = index + regex.length;
        }

        if (offset < data.length) {
            ret.add(subarray(data, offset));
        }

        return ret.toArray(new byte[ret.size()][]);
    }

    public static boolean equals(byte[] data1, byte[] data2, int length) {
        if (data1.length < length || data2.length < length) {
            return false;
        }

        for (int i = 0; i < length; ++i) {
            if (data1[i] != data2[i]) {
                return false;
            }

        }

        return true;
    }

    public static boolean equals(byte[] data1, int offset1, byte[] data2, int offset2, int length) {
        if (offset1 >= data1.length || data1.length - offset1 < length || offset2 >= data2.length || data2.length - offset2 < length) {
            return false;
        }

        for (int i = 0; i < length; ++i) {
            if (data1[i + offset1] != data2[i + offset2]) {
                return false;
            }

        }

        return true;
    }

    public static String toString(Object[] data) {
        if (data == null) {
            return "null";
        }

        if (data.length == 0) {
            return "[]";
        }

        StringBuilder b = new StringBuilder();

        b.append('[');

        for (int i = 0; i < data.length; ++i) {
            if (i > 0) {
                b.append(", ");
            }
            b.append(String.valueOf(data[i]));
        }

        b.append(']');

        return b.toString();
    }

    public static String toString(byte[] data, int maxLength) {
        if (data == null) {
            return "null";
        }

        if (maxLength == 0) {
            return "[]";
        }

        StringBuilder b = new StringBuilder();

        b.append('[');

        for (int i = 0, size = Math.min(data.length, maxLength); i < size; ++i) {
            if (i > 0) {
                b.append(", ");
            }
            if (data[i] < 16) {
                b.append('0');
            }
            b.append(Integer.toHexString(data[i]));
        }

        b.append(']');

        return b.toString();
    }

    public static String toString(byte[] data) {
        return toString(data, data != null ? data.length : 0);
    }

    public static String toString(long[] data) {
        if (data == null) {
            return "null";
        }

        if (data.length == 0) {
            return "[]";
        }

        StringBuilder b = new StringBuilder();

        b.append('[');

        for (int i = 0; i < data.length; ++i) {
            if (i > 0) {
                b.append(", ");
            }
            if (data[i] < 16) {
                b.append('0');
            }
            b.append(Long.toHexString(data[i]));
        }

        b.append(']');

        return b.toString();
    }

    public static String toString(int[] data) {
        if (data == null) {
            return "null";
        }

        if (data.length == 0) {
            return "[]";
        }

        StringBuilder b = new StringBuilder();

        b.append('[');

        for (int i = 0; i < data.length; ++i) {
            if (i > 0) {
                b.append(", ");
            }
            if (data[i] < 16) {
                b.append('0');
            }
            b.append(Integer.toHexString(data[i]));
        }

        b.append(']');

        return b.toString();
    }

    private ArrayTools() {
        // empty
    }

}
