package net.glowstone.util;

import static com.google.common.base.Preconditions.checkArgument;

import java.util.Arrays;

/**
 * An array of nibbles (4-bit values) stored efficiently as a byte array of half the size.
 *
 * <p>The even indices are stored in the least significant nibble and the odd indices in the most significant bits. For example, [1 5 8 15] is stored as [0x51 0xf8].
 */
public final class NibbleArray {

    private final byte[] data;

    /**
     * Construct a new NibbleArray with the given size in nibbles.
     *
     * @param size The number of nibbles in the array.
     * @throws IllegalArgumentException If size is not positive and even.
     */
    public NibbleArray(int size) {
        this(size, (byte) 0);
    }

    /**
     * Construct a new NibbleArray with the given size in nibble, filled with the specified nibble value.
     *
     * @param size The number of nibbles in the array.
     * @param value The value to fill the array with.
     * @throws IllegalArgumentException If size is not positive and even.
     */
    public NibbleArray(int size, byte value) {
        checkArgument(size > 0 && size % 2 == 0, "size must be positive even number, not " + size);
        data = new byte[size / 2];
        if (value != 0) {
            fill(value);
        }
    }

    /**
     * Construct a new NibbleArray using the given underlying bytes. No copy is created.
     *
     * @param data The raw data to use.
     */
    public NibbleArray(byte... data) {
        this.data = data;
    }

    /**
     * Get the size in nibbles.
     *
     * @return The size in nibbles.
     */
    public int size() {
        return 2 * data.length;
    }

    /**
     * Get the size in bytes, one-half the size in nibbles.
     *
     * @return The size in bytes.
     */
    public int byteSize() {
        return data.length;
    }

    /**
     * Get the nibble at the given index.
     *
     * @param index The nibble index.
     * @return The value of the nibble at that index.
     */
    public byte get(int index) {
        byte val = data[index / 2];
        if (index % 2 == 0) {
            return (byte) (val & 0x0f);
        } else {
            return (byte) ((val & 0xf0) >> 4);
        }
    }

    /**
     * Set the nibble at the given index to the given value.
     *
     * @param index The nibble index.
     * @param value The new value to store.
     */
    public void set(int index, byte value) {
        value &= 0xf;
        int half = index / 2;
        byte previous = data[half];
        if (index % 2 == 0) {
            data[half] = (byte) (previous & 0xf0 | value);
        } else {
            data[half] = (byte) (previous & 0x0f | value << 4);
        }
    }

    /**
     * Fill the nibble array with the specified value.
     *
     * @param value The value nibble to fill with.
     */
    public void fill(byte value) {
        value &= 0xf;
        Arrays.fill(data, (byte) (value << 4 | value));
    }

    /**
     * Get the raw bytes of this nibble array. Modifying the returned array will modify the internal representation of this nibble array.
     *
     * @return The raw bytes.
     */
    public byte[] getRawData() {
        return data;
    }

    /**
     * Copies into the raw bytes of this nibble array from the given source.
     *
     * @param source The array to copy from.
     * @throws IllegalArgumentException If source is not the correct length.
     */
    public void setRawData(byte... source) {
        checkArgument(source.length == data.length, "expected byte array of length " + data.length + ", not " + source.length);
        System.arraycopy(source, 0, data, 0, source.length);
    }

    /**
     * Take a snapshot of this NibbleArray which will not reflect changes.
     *
     * @return The snapshot NibbleArray.
     */
    public NibbleArray snapshot() {
        return new NibbleArray(data.clone());
    }
}

@@ -310,16 +310,19 @@
 the most
+%0A *
  signifi
@@ -849,16 +849,23 @@
 d nibble
+%0A     *
  value.%0A
@@ -3090,16 +3090,23 @@
 internal
+%0A     *
  represe
@@ -3530,16 +3530,33 @@
 rgument(
+%0A                
 source.l
@@ -3576,16 +3576,32 @@
 .length,
+%0A               
  %22expect

Fix CheckStyle issues in util/NibbleArray.java (#718)