/*
 * Copyright 2010-2015 Institut Pasteur.
 * 
 * This file is part of Icy.
 * 
 * Icy is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Icy is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with Icy. If not, see <http://www.gnu.org/licenses/>.
 */
package icy.type.collection.array;

import icy.type.DataType;

/**
 * Utilities class to convert any data array from/to byte array.
 * 
 * @author Stephane
 */
public class ByteArrayConvert
{
    /**
     * Get maximum length in bytes for a copy from in to out with specified offset.<br>
     * If specified length != -1 then the value is directly returned (assumed to be in bytes).
     */
    static int getCopyLengthInBytes(Object in, int inOffset, Object out, int outOffset, int length)
    {
        if (length == -1)
            return getCopyLengthInBytes(in, inOffset, out, outOffset);

        return length;
    }

    /**
     * Get maximum length in bytes for a copy from in to out with specified offset.
     */
    public static int getCopyLengthInBytes(Object in, int inOffset, Object out, int outOffset)
    {
        // 'in' object can't be null !
        final int len = getCopyLengthInBytes(in, inOffset);

        if (out == null)
            return len;

        return Math.min(len, getCopyLengthInBytes(out, outOffset));
    }

    /**
     * Get length in bytes for a copy from or to array with specified offset.
     */
    public static int getCopyLengthInBytes(Object array, int offset)
    {
        return ArrayUtil.getCopyLength(array, offset) * ArrayUtil.getDataType(array).getSize();
    }

    /**
     * Read a byte from the input byte array at specified position.
     */
    public static byte readByte(byte[] array, int offset)
    {
        return array[offset];
    }

    /**
     * Read a short value from the input byte array at specified position.
     */
    public static short readShort(byte[] array, int offset, boolean littleEndian)
    {
        if (littleEndian)
            return (short) (((array[offset + 0] & 0xFF) << 0) + ((array[offset + 1] & 0xFF) << 8));

        return (short) (((array[offset + 0] & 0xFF) << 8) + ((array[offset + 1] & 0xFF) << 0));
    }

    /**
     * Read a int value from the input byte array at specified position.
     */
    public static int readInt(byte[] array, int offset, boolean littleEndian)
    {
        if (littleEndian)
            return ((array[offset + 0] & 0xFF) << 0) + ((array[offset + 1] & 0xFF) << 8)
                    + ((array[offset + 2] & 0xFF) << 16) + ((array[offset + 3] & 0xFF) << 24);

        return ((array[offset + 0] & 0xFF) << 24) + ((array[offset + 1] & 0xFF) << 16)
                + ((array[offset + 2] & 0xFF) << 8) + ((array[offset + 3] & 0xFF) << 0);
    }

    /**
     * Read a long value from the input byte array at specified position.
     */
    public static long readLong(byte[] array, int offset, boolean littleEndian)
    {
        if (littleEndian)
        {
            final int v1 = ((array[offset + 0] & 0xFF) << 0) + ((array[offset + 1] & 0xFF) << 8)
                    + ((array[offset + 2] & 0xFF) << 16) + ((array[offset + 3] & 0xFF) << 24);
            final int v2 = ((array[offset + 4] & 0xFF) << 0) + ((array[offset + 5] & 0xFF) << 8)
                    + ((array[offset + 6] & 0xFF) << 16) + ((array[offset + 7] & 0xFF) << 24);
            return ((v1 & 0xFFFFFFFFL) << 0) + ((v2 & 0xFFFFFFFFL) << 32);
        }

        final int v1 = ((array[offset + 0] & 0xFF) << 24) + ((array[offset + 1] & 0xFF) << 16)
                + ((array[offset + 2] & 0xFF) << 8) + ((array[offset + 3] & 0xFF) << 0);
        final int v2 = ((array[offset + 4] & 0xFF) << 24) + ((array[offset + 5] & 0xFF) << 16)
                + ((array[offset + 6] & 0xFF) << 8) + ((array[offset + 7] & 0xFF) << 0);
        return ((v1 & 0xFFFFFFFFL) << 32) + ((v2 & 0xFFFFFFFFL) << 0);
    }

    /**
     * Read a long value from the input byte array at specified position.
     */
    public static float readFloat(byte[] array, int offset, boolean littleEndian)
    {
        return Float.intBitsToFloat(readInt(array, offset, littleEndian));
    }

    /**
     * Read a long value from the input byte array at specified position.
     */
    public static double readDouble(byte[] array, int offset, boolean littleEndian)
    {
        return Double.longBitsToDouble(readLong(array, offset, littleEndian));
    }

    /**
     * Write a byte to the output byte array at specified position.
     */
    public static void writeByte(byte[] array, int offset, byte value)
    {
        array[offset] = value;
    }

    /**
     * Write a short to the output byte array at specified position.
     */
    public static void writeShort(byte[] array, int offset, short value, boolean littleEndian)
    {
        if (littleEndian)
        {
            array[offset + 0] = (byte) (value >> 0);
            array[offset + 1] = (byte) (value >> 8);
        }
        else
        {
            array[offset + 0] = (byte) (value >> 8);
            array[offset + 1] = (byte) (value >> 0);
        }
    }

    /**
     * Write a int to the output byte array at specified position.
     */
    public static void writeInt(byte[] array, int offset, int value, boolean littleEndian)
    {
        if (littleEndian)
        {
            array[offset + 0] = (byte) (value >> 0);
            array[offset + 1] = (byte) (value >> 8);
            array[offset + 2] = (byte) (value >> 16);
            array[offset + 3] = (byte) (value >> 24);
        }
        else
        {
            array[offset + 0] = (byte) (value >> 24);
            array[offset + 1] = (byte) (value >> 16);
            array[offset + 2] = (byte) (value >> 8);
            array[offset + 3] = (byte) (value >> 0);
        }
    }

    /**
     * Write a long to the output byte array at specified position.
     */
    public static void writeLong(byte[] array, int offset, long value, boolean littleEndian)
    {
        int v;

        if (littleEndian)
        {
            v = (int) (value >> 0);
            array[offset + 0] = (byte) (v >> 0);
            array[offset + 1] = (byte) (v >> 8);
            array[offset + 2] = (byte) (v >> 16);
            array[offset + 3] = (byte) (v >> 24);
            v = (int) (value >> 32);
            array[offset + 4] = (byte) (v >> 0);
            array[offset + 5] = (byte) (v >> 8);
            array[offset + 6] = (byte) (v >> 16);
            array[offset + 7] = (byte) (v >> 24);
        }
        else
        {
            v = (int) (value >> 32);
            array[offset + 0] = (byte) (v >> 24);
            array[offset + 1] = (byte) (v >> 16);
            array[offset + 2] = (byte) (v >> 8);
            array[offset + 3] = (byte) (v >> 0);
            v = (int) (value >> 0);
            array[offset + 4] = (byte) (v >> 24);
            array[offset + 5] = (byte) (v >> 16);
            array[offset + 6] = (byte) (v >> 8);
            array[offset + 7] = (byte) (v >> 0);
        }
    }

    /**
     * Write a float to the output byte array at specified position.
     */
    public static void writeFloat(byte[] array, int offset, float value, boolean littleEndian)
    {
        writeInt(array, offset, Float.floatToRawIntBits(value), littleEndian);
    }

    /**
     * Write a double to the output byte array at specified position.
     */
    public static void writeDouble(byte[] array, int offset, double value, boolean littleEndian)
    {
        writeLong(array, offset, Double.doubleToRawLongBits(value), littleEndian);
    }

    /**
     * Bit transform and return the 'in' byte array in the specified data type array
     * 
     * @param in
     *        input array
     * @param inOffset
     *        position where we start read data from
     * @param out
     *        output array which is used to receive result (and so define wanted type)
     * @param outOffset
     *        position where we start to write data to
     * @param length
     *        number of bytes to compute (-1 means we will use the maximum possible)
     */
    public static byte[] byteArrayToByteArray(byte[] in, int inOffset, byte[] out, int outOffset, int length)
    {
        final int len = getCopyLengthInBytes(in, inOffset, out, outOffset, length);
        final byte[] result = Array1DUtil.allocIfNull(out, outOffset + len);

        // simple copy
        System.arraycopy(in, inOffset, result, outOffset, len);

        return result;
    }

    /**
     * Bit transform and return the 'in' byte array in the specified data type array
     * 
     * @param in
     *        input array
     * @param out
     *        output array which is used to receive result (and so define wanted type)
     */
    public static byte[] byteArrayToByteArray(byte[] in, byte[] out)
    {
        return byteArrayToByteArray(in, 0, out, 0, -1);
    }

    /**
     * Bit transform and return the 'in' byte array in the specified data type array
     * 
     * @param in
     *        input array
     */
    public static byte[] byteArrayToByteArray(byte[] in)
    {
        return byteArrayToByteArray(in, 0, null, 0, -1);
    }

    /**
     * Bit transform and return the 'in' byte array in the specified data type array
     * 
     * @param in
     *        input array
     * @param inOffset
     *        position where we start read data from
     * @param out
     *        output array which is used to receive result (and so define wanted type)
     * @param outOffset
     *        position where we start to write data to
     * @param length
     *        number of bytes to compute (-1 means we will use the maximum possible)
     * @param little
     *        little endian order
     */
    public static short[] byteArrayToShortArray(byte[] in, int inOffset, short[] out, int outOffset, int length,
            boolean little)
    {
        final int len = getCopyLengthInBytes(in, inOffset, out, outOffset, length) / 2;
        final short[] result = Array1DUtil.allocIfNull(out, outOffset + len);

        int inOff = inOffset;
        int outOff = outOffset;

        for (int i = 0; i < len; i++)
        {
            result[outOff++] = readShort(in, inOff, little);
            inOff += 2;
        }

        return result;
    }

    /**
     * Bit transform and return the 'in' byte array in the specified data type array
     * 
     * @param in
     *        input array
     * @param out
     *        output array which is used to receive result (and so define wanted type)
     * @param little
     *        little endian order
     */
    public static short[] byteArrayToShortArray(byte[] in, short[] out, boolean little)
    {
        return byteArrayToShortArray(in, 0, out, 0, -1, little);
    }

    /**
     * Bit transform and return the 'in' byte array in the specified data type array
     * 
     * @param in
     *        input array
     * @param little
     *        little endian order
     */
    public static short[] byteArrayToShortArray(byte[] in, boolean little)
    {
        return byteArrayToShortArray(in, 0, null, 0, -1, little);
    }

    /**
     * Bit transform and return the 'in' byte array in the specified data type array
     * 
     * @param in
     *        input array
     * @param inOffset
     *        position where we start read data from
     * @param out
     *        output array which is used to receive result (and so define wanted type)
     * @param outOffset
     *        position where we start to write data to
     * @param length
     *        number of bytes to compute (-1 means we will use the maximum possible)
     * @param little
     *        little endian order
     */
    public static int[] byteArrayToIntArray(byte[] in, int inOffset, int[] out, int outOffset, int length,
            boolean little)
    {
        final int len = getCopyLengthInBytes(in, inOffset, out, outOffset, length) / 4;
        final int[] result = Array1DUtil.allocIfNull(out, outOffset + len);

        int inOff = inOffset;
        int outOff = outOffset;

        for (int i = 0; i < len; i++)
        {
            result[outOff++] = readInt(in, inOff, little);
            inOff += 4;
        }

        return result;
    }

    /**
     * Bit transform and return the 'in' byte array in the specified data type array
     * 
     * @param in
     *        input array
     * @param out
     *        output array which is used to receive result (and so define wanted type)
     * @param little
     *        little endian order
     */
    public static int[] byteArrayToIntArray(byte[] in, int[] out, boolean little)
    {
        return byteArrayToIntArray(in, 0, out, 0, -1, little);
    }

    /**
     * Bit transform and return the 'in' byte array in the specified data type array
     * 
     * @param in
     *        input array
     * @param little
     *        little endian order
     */
    public static int[] byteArrayToIntArray(byte[] in, boolean little)
    {
        return byteArrayToIntArray(in, 0, null, 0, -1, little);
    }

    /**
     * Bit transform and return the 'in' byte array in the specified data type array
     * 
     * @param in
     *        input array
     * @param inOffset
     *        position where we start read data from
     * @param out
     *        output array which is used to receive result (and so define wanted type)
     * @param outOffset
     *        position where we start to write data to
     * @param length
     *        number of bytes to compute (-1 means we will use the maximum possible)
     * @param little
     *        little endian order
     */
    public static long[] byteArrayToLongArray(byte[] in, int inOffset, long[] out, int outOffset, int length,
            boolean little)
    {
        final int len = getCopyLengthInBytes(in, inOffset, out, outOffset, length) / 8;
        final long[] result = Array1DUtil.allocIfNull(out, outOffset + len);

        int inOff = inOffset;
        int outOff = outOffset;

        for (int i = 0; i < len; i++)
        {
            result[outOff++] = readLong(in, inOff, little);
            inOff += 8;
        }

        return result;
    }

    /**
     * Bit transform and return the 'in' byte array in the specified data type array
     * 
     * @param in
     *        input array
     * @param out
     *        output array which is used to receive result (and so define wanted type)
     * @param little
     *        little endian order
     */
    public static long[] byteArrayToLongArray(byte[] in, long[] out, boolean little)
    {
        return byteArrayToLongArray(in, 0, out, 0, -1, little);
    }

    /**
     * Bit transform and return the 'in' byte array in the specified data type array
     * 
     * @param in
     *        input array
     * @param little
     *        little endian order
     */
    public static long[] byteArrayToLongArray(byte[] in, boolean little)
    {
        return byteArrayToLongArray(in, 0, null, 0, -1, little);
    }

    /**
     * Bit transform and return the 'in' byte array in 'out' float array
     * 
     * @param in
     *        input array
     * @param inOffset
     *        position where we start read data from
     * @param out
     *        output array which is used to receive result (and so define wanted type)
     * @param outOffset
     *        position where we start to write data to
     * @param length
     *        number of bytes to compute (-1 means we will use the maximum possible)
     * @param little
     *        little endian order
     */
    public static float[] byteArrayToFloatArray(byte[] in, int inOffset, float[] out, int outOffset, int length,
            boolean little)
    {
        final int len = getCopyLengthInBytes(in, inOffset, out, outOffset, length) / 4;
        final float[] result = Array1DUtil.allocIfNull(out, outOffset + len);

        int inOff = inOffset;
        int outOff = outOffset;

        for (int i = 0; i < len; i++)
        {
            result[outOff++] = readFloat(in, inOff, little);
            inOff += 4;
        }

        return result;
    }

    /**
     * Bit transform and return the 'in' byte array in the specified data type array
     * 
     * @param in
     *        input array
     * @param out
     *        output array which is used to receive result (and so define wanted type)
     * @param little
     *        little endian order
     */
    public static float[] byteArrayToFloatArray(byte[] in, float[] out, boolean little)
    {
        return byteArrayToFloatArray(in, 0, out, 0, -1, little);
    }

    /**
     * Bit transform and return the 'in' byte array in the specified data type array
     * 
     * @param in
     *        input array
     * @param little
     *        little endian order
     */
    public static float[] byteArrayToFloatArray(byte[] in, boolean little)
    {
        return byteArrayToFloatArray(in, 0, null, 0, -1, little);
    }

    /**
     * Bit transform and return the 'in' byte array in the specified data type array
     * 
     * @param in
     *        input array
     * @param inOffset
     *        position where we start read data from
     * @param out
     *        output array which is used to receive result (and so define wanted type)
     * @param outOffset
     *        position where we start to write data to
     * @param length
     *        number of bytes to compute (-1 means we will use the maximum possible)
     * @param little
     *        little endian order
     */
    public static double[] byteArrayToDoubleArray(byte[] in, int inOffset, double[] out, int outOffset, int length,
            boolean little)
    {
        final int len = getCopyLengthInBytes(in, inOffset, out, outOffset, length) / 8;
        final double[] result = Array1DUtil.allocIfNull(out, outOffset + len);

        int inOff = inOffset;
        int outOff = outOffset;

        for (int i = 0; i < len; i++)
        {
            result[outOff++] = readDouble(in, inOff, little);
            inOff += 8;
        }

        return result;
    }

    /**
     * Bit transform and return the 'in' byte array in the specified data type array
     * 
     * @param in
     *        input array
     * @param out
     *        output array which is used to receive result (and so define wanted type)
     * @param little
     *        little endian order
     */
    public static double[] byteArrayToDoubleArray(byte[] in, double[] out, boolean little)
    {
        return byteArrayToDoubleArray(in, 0, out, 0, -1, little);
    }

    /**
     * Bit transform and return the 'in' byte array in the specified data type array
     * 
     * @param in
     *        input array
     * @param little
     *        little endian order
     */
    public static double[] byteArrayToDoubleArray(byte[] in, boolean little)
    {
        return byteArrayToDoubleArray(in, 0, null, 0, -1, little);
    }

    /**
     * Bit transform and return the 'in' short array as byte array
     * 
     * @param in
     *        input array
     * @param inOffset
     *        position where we start read data from
     * @param out
     *        output byte array which is used to receive result
     * @param outOffset
     *        position where we start to write data to
     * @param length
     *        number of <b>bytes</b> to compute (-1 means we will use the maximum possible)
     * @param little
     *        little endian order
     */
    public static byte[] shortArrayToByteArray(short[] in, int inOffset, byte[] out, int outOffset, int length,
            boolean little)
    {
        final int len = getCopyLengthInBytes(in, inOffset, out, outOffset, length);
        final byte[] result = Array1DUtil.allocIfNull(out, outOffset + len);

        int inOff = inOffset;
        int outOff = outOffset;

        for (int i = 0; i < len / 2; i++)
        {
            writeShort(result, outOff, in[inOff++], little);
            outOff += 2;
        }

        return result;
    }

    /**
     * Bit transform and return the 'in' int array as byte array
     * 
     * @param in
     *        input array
     * @param inOffset
     *        position where we start read data from
     * @param out
     *        output byte array which is used to receive result
     * @param outOffset
     *        position where we start to write data to
     * @param length
     *        number of <b>bytes</b> to compute (-1 means we will use the maximum possible)
     * @param little
     *        little endian order
     */
    public static byte[] intArrayToByteArray(int[] in, int inOffset, byte[] out, int outOffset, int length,
            boolean little)
    {
        final int len = getCopyLengthInBytes(in, inOffset, out, outOffset, length);
        final byte[] result = Array1DUtil.allocIfNull(out, outOffset + len);

        int inOff = inOffset;
        int outOff = outOffset;

        for (int i = 0; i < len / 4; i++)
        {
            writeInt(result, outOff, in[inOff++], little);
            outOff += 4;
        }

        return result;
    }

    /**
     * Bit transform and return the 'in' long array as byte array
     * 
     * @param in
     *        input array
     * @param inOffset
     *        position where we start read data from
     * @param out
     *        output byte array which is used to receive result
     * @param outOffset
     *        position where we start to write data to
     * @param length
     *        number of <b>bytes</b> to compute (-1 means we will use the maximum possible)
     * @param little
     *        little endian order
     */
    public static byte[] longArrayToByteArray(long[] in, int inOffset, byte[] out, int outOffset, int length,
            boolean little)
    {
        final int len = getCopyLengthInBytes(in, inOffset, out, outOffset, length);
        final byte[] result = Array1DUtil.allocIfNull(out, outOffset + len);

        int inOff = inOffset;
        int outOff = outOffset;

        for (int i = 0; i < len / 8; i++)
        {
            writeLong(result, outOff, in[inOff++], little);
            outOff += 8;
        }

        return result;
    }

    /**
     * Bit transform and return the 'in' float array as byte array
     * 
     * @param in
     *        input array
     * @param inOffset
     *        position where we start read data from
     * @param out
     *        output byte array which is used to receive result
     * @param outOffset
     *        position where we start to write data to
     * @param length
     *        number of <b>bytes</b> to compute (-1 means we will use the maximum possible)
     * @param little
     *        little endian order
     */
    public static byte[] floatArrayToByteArray(float[] in, int inOffset, byte[] out, int outOffset, int length,
            boolean little)
    {
        final int len = getCopyLengthInBytes(in, inOffset, out, outOffset, length);
        final byte[] result = Array1DUtil.allocIfNull(out, outOffset + len);

        int inOff = inOffset;
        int outOff = outOffset;

        for (int i = 0; i < len / 4; i++)
        {
            writeFloat(result, outOff, in[inOff++], little);
            outOff += 4;
        }

        return result;
    }

    /**
     * Bit transform and return the 'in' double array as byte array
     * 
     * @param in
     *        input array
     * @param inOffset
     *        position where we start read data from
     * @param out
     *        output byte array which is used to receive result
     * @param outOffset
     *        position where we start to write data to
     * @param length
     *        number of <b>bytes</b> to compute (-1 means we will use the maximum possible)
     * @param little
     *        little endian order
     */
    public static byte[] doubleArrayToByteArray(double[] in, int inOffset, byte[] out, int outOffset, int length,
            boolean little)
    {
        final int len = getCopyLengthInBytes(in, inOffset, out, outOffset, length);
        final byte[] result = Array1DUtil.allocIfNull(out, outOffset + len);

        int inOff = inOffset;
        int outOff = outOffset;

        for (int i = 0; i < len / 8; i++)
        {
            writeDouble(result, outOff, in[inOff++], little);
            outOff += 8;
        }

        return result;
    }

    /**
     * Bit transform and return the 'in' byte array in the specified 'out' data type array
     * 
     * @param in
     *        input array
     * @param inOffset
     *        position where we start read data from
     * @param out
     *        output array which is used to receive result (and so define wanted type)
     * @param outOffset
     *        position where we start to write data to
     * @param length
     *        number of bytes to compute (-1 means we will use the maximum possible)
     * @param little
     *        little endian order
     */
    public static Object byteArrayTo(byte[] in, int inOffset, Object out, int outOffset, int length, boolean little)
    {
        if (out == null)
            return null;

        switch (ArrayUtil.getDataType(out).getJavaType())
        {
            case BYTE:
                return byteArrayToByteArray(in, inOffset, (byte[]) out, outOffset, length);
            case SHORT:
                return byteArrayToShortArray(in, inOffset, (short[]) out, outOffset, length, little);
            case INT:
                return byteArrayToIntArray(in, inOffset, (int[]) out, outOffset, length, little);
            case LONG:
                return byteArrayToLongArray(in, inOffset, (long[]) out, outOffset, length, little);
            case FLOAT:
                return byteArrayToFloatArray(in, inOffset, (float[]) out, outOffset, length, little);
            case DOUBLE:
                return byteArrayToDoubleArray(in, inOffset, (double[]) out, outOffset, length, little);
            default:
                return null;
        }
    }

    /**
     * Bit transform and return the 'in' byte array in the specified 'out' data type array
     * 
     * @param in
     *        input array
     * @param out
     *        output array which is used to receive result (and so define wanted type)
     * @param little
     *        little endian order
     */
    public static Object byteArrayTo(byte[] in, Object out, boolean little)
    {
        return byteArrayTo(in, 0, out, 0, -1, little);
    }

    /**
     * Bit transform and return the 'in' byte array in the specified data type array
     * 
     * @param in
     *        input array
     * @param inOffset
     *        position where we start read data from
     * @param outDataType
     *        wanted output array data type
     * @param outOffset
     *        position where we start to write data to
     * @param length
     *        number of bytes to compute (-1 means we will use the maximum possible)
     * @param little
     *        little endian order
     */
    public static Object byteArrayTo(byte[] in, int inOffset, DataType outDataType, int outOffset, int length,
            boolean little)
    {
        switch (outDataType.getJavaType())
        {
            case BYTE:
                return byteArrayToByteArray(in, inOffset, null, outOffset, length);
            case SHORT:
                return byteArrayToShortArray(in, inOffset, null, outOffset, length, little);
            case INT:
                return byteArrayToIntArray(in, inOffset, null, outOffset, length, little);
            case LONG:
                return byteArrayToLongArray(in, inOffset, null, outOffset, length, little);
            case FLOAT:
                return byteArrayToFloatArray(in, inOffset, null, outOffset, length, little);
            case DOUBLE:
                return byteArrayToDoubleArray(in, inOffset, null, outOffset, length, little);
            default:
                return null;
        }
    }

    /**
     * Bit transform and return the 'in' byte array in the specified data type array
     * 
     * @param in
     *        input array
     * @param inOffset
     *        position where we start read data from
     * @param outDataType
     *        wanted output array data type
     * @param length
     *        number of bytes to compute (-1 means we will use the maximum possible)
     * @param little
     *        little endian order
     */
    public static Object byteArrayTo(byte[] in, int inOffset, DataType outDataType, int length, boolean little)
    {
        return byteArrayTo(in, inOffset, outDataType, 0, length, little);
    }

    /**
     * Bit transform and return the 'in' byte array in the specified data type array
     * 
     * @param in
     *        input array
     * @param outDataType
     *        wanted output array data type
     * @param little
     *        little endian order
     */
    public static Object byteArrayTo(byte[] in, DataType outDataType, boolean little)
    {
        return byteArrayTo(in, 0, outDataType, 0, -1, little);
    }

    /**
     * @deprecated use {@link #byteArrayTo(byte[], int , DataType , int , boolean )} instead
     */
    @Deprecated
    public static Object byteArrayTo(byte[] in, int inOffset, int outDataType, int length, boolean little)
    {
        return byteArrayTo(in, inOffset, DataType.getDataType(outDataType), 0, length, little);
    }

    /**
     * @deprecated use {@link #byteArrayTo(byte[], DataType , boolean )} instead
     */
    @Deprecated
    public static Object byteArrayTo(byte[] in, int outDataType, boolean little)
    {
        return byteArrayTo(in, 0, DataType.getDataType(outDataType), 0, -1, little);
    }

    /**
     * Bit transform and return the 'in' array as byte array
     * 
     * @param in
     *        input array (define input type)
     * @param inOffset
     *        position where we start read data from
     * @param out
     *        output byte array which is used to receive result
     * @param outOffset
     *        position where we start to write data to
     * @param length
     *        number of bytes to compute (-1 means we will use the maximum possible)
     * @param little
     *        little endian order
     */
    public static byte[] toByteArray(Object in, int inOffset, byte[] out, int outOffset, int length, boolean little)
    {
        switch (ArrayUtil.getDataType(in))
        {
            case BYTE:
                return byteArrayToByteArray((byte[]) in, inOffset, out, outOffset, length);
            case SHORT:
                return shortArrayToByteArray((short[]) in, inOffset, out, outOffset, length, little);
            case INT:
                return intArrayToByteArray((int[]) in, inOffset, out, outOffset, length, little);
            case LONG:
                return longArrayToByteArray((long[]) in, inOffset, out, outOffset, length, little);
            case FLOAT:
                return floatArrayToByteArray((float[]) in, inOffset, out, outOffset, length, little);
            case DOUBLE:
                return doubleArrayToByteArray((double[]) in, inOffset, out, outOffset, length, little);
            default:
                return out;
        }
    }

    /**
     * Bit transform and return the 'in' array as byte array
     * 
     * @param in
     *        input array (define input type)
     * @param inOffset
     *        position where we start read data from
     * @param out
     *        output byte array which is used to receive result
     * @param outOffset
     *        position where we start to write data to
     * @param little
     *        little endian order
     */
    public static byte[] toByteArray(Object in, int inOffset, byte[] out, int outOffset, boolean little)
    {
        return toByteArray(in, inOffset, out, outOffset, -1, little);
    }

    /**
     * Bit transform and return the 'in' array as byte array
     * 
     * @param in
     *        input array (define input type)
     * @param inOffset
     *        position where we start read data from
     * @param length
     *        number of <br>
     *        bytes</b> to compute (-1 means we will use the maximum possible)
     * @param little
     *        little endian order
     */
    public static byte[] toByteArray(Object in, int inOffset, int length, boolean little)
    {
        return toByteArray(in, inOffset, null, 0, length, little);
    }

    /**
     * Bit transform and return the 'in' array as byte array
     * 
     * @param in
     *        input array (define input type)
     * @param out
     *        output byte array which is used to receive result
     * @param little
     *        little endian order
     */
    public static byte[] toByteArray(Object in, byte[] out, boolean little)
    {
        return toByteArray(in, 0, out, 0, -1, little);
    }

    /**
     * Bit transform and return the 'in' array as byte array
     * 
     * @param in
     *        input array (define input type)
     * @param little
     *        little endian order
     */
    public static byte[] toByteArray(Object in, boolean little)
    {
        return toByteArray(in, 0, null, 0, -1, little);
    }
}