package speiger.src.coreengine.utils.collections.collection;

public class BitArray {
	long[] data;
	int size;
	int bits;
	long maxValue;
	
	public BitArray(int size, int bits) {
		this.size = size;
		this.bits = bits;
		maxValue = (1L << bits) - 1L;
		data = new long[arraySize(size * bits)];
	}
	
	public BitArray(int size, int bits, long[] data) {
		this.size = size;
		this.bits = bits;
		maxValue = (1L << bits) - 1L;
		int arraySize = arraySize(size * bits);
		if(data == null || data.length != arraySize) {
			this.data = new long[arraySize];
			if(this.data.length < arraySize) System.arraycopy(data, 0, this.data, 0, Math.min(this.data.length, arraySize));
		}
		else this.data = data;
	}
	
	private int arraySize(int entries) {
		return entries % 64 == 0 ? entries / 64 : (entries / 64) + 1;
	}
	
	public void set(int index, long value) {
		if(index < 0 || index >= size) throw new IllegalStateException("Out ouf Bounds");
		int arrayIndex = (index * bits) / 64;
		int bitIndex = (index * bits) % 64;
		data[arrayIndex] = data[arrayIndex] & ~(maxValue << bitIndex) | (value & maxValue) << bitIndex;
		int left = 64 - bitIndex;
		if(left < bits) {
			int leftover = bits - left;
			data[arrayIndex + 1] = data[arrayIndex + 1] >>> leftover << leftover | (value & maxValue) >> left;
		}
	}
	
	public void clear(int index) {
		set(index, 0L);
	}
	
	public long get(int index) {
		if(index < 0 || index >= size) throw new IllegalStateException("Out ouf Bounds");
		int arrayIndex = (index * bits) / 64;
		int bitIndex = (index * bits) % 64;
		return (64 - bitIndex < bits ? (data[arrayIndex] >>> bitIndex | data[arrayIndex + 1] << (64 - bitIndex)) : (data[arrayIndex] >>> bitIndex)) & maxValue;
	}
	
	public int getInt(int index) {
		return (int)get(index);
	}
	
	public int swapInt(int index, int newValue) {
		int value = getInt(index);
		if(newValue != value) set(index, newValue);
		return value;
	}
	
	public long swap(int index, long newValue) {
		long value = get(index);
		if(newValue != value) set(index, newValue);
		return value;
	}
	
	public int size() {
		return size;
	}
	
	public int bits() {
		return bits;
	}
	
	public long[] getData() { return data; }
}