/*
 * @author Ranides Atterwim <ranides@gmail.com>
 * @copyright Ranides Atterwim
 * @license WTFPL
 * @url http://ranides.net/projects/assira
 */
package net.ranides.assira.collection;

import java.lang.reflect.Array;
import java.util.ListIterator;
import net.ranides.assira.generic.PrivilegedActions;

/**
 *
 * @author Ranides Atterwim <ranides@gmail.com>
 */
public final class ByteArray {
    
    private static final sun.misc.Unsafe API = PrivilegedActions.$getStaticField("sun.misc.Unsafe", "theUnsafe");
    
    private final Object array;
    private final long base;
    private final int size;
    
    public ByteArray(Object array) {
        if (!array.getClass().isArray()) {
            throw new IllegalArgumentException(array.getClass() + " is not an array");
        }
        Class<?> type = array.getClass();
        this.array = array;
        this.base = API.arrayBaseOffset(type);
        this.size = API.arrayIndexScale(type) * Array.getLength(array);
    }
    
    public byte get(int index) {
        return API.getByte(array, base+index);
    }
    
    public void set(int index, byte value) {
        API.putByte(array, base+index, value);
    }
    
    public int size() {
        return size;
    }
    
    public ByteIterator iterator() {
        return new ByteIterator(this, 0);
    }
    
    public ByteIterator listIterator(int index) {
        return new ByteIterator(this, index);
    }
    
    public byte[] toArray() {
        return toArray(0, size);
    }
    
    public byte[] toArray(int begin, int end) {
        return toArray(null, begin, end);
    }
    
    public byte[] toArray(byte[] target, int begin, int end) {
        int count = end - begin;
        long offset = base + begin;
        byte[] out = null!=target && target.length >= count ? target : new byte[count];
        
        for(int i=0; i<count; i++, offset++) {
            out[i] = API.getByte(array, offset);
        }
        return out;
    }
    
    public final static class ByteIterator implements ListIterator<Byte> {
        
        public static final String NSE = "Operation is not supported by ByteArray.";
        
        private final Object array;
        private final long base;
        private final long max;
        private long index;
        private long last;

        public ByteIterator(ByteArray that, int index) {
            this.array = that.array;
            this.base = that.base;
            this.max = that.size - 1;
            this.index = index;
        }
        
        @Override
        public boolean hasNext() {
            return index <= max;
        }

        @Override
        public Byte next() {
            return nextByte();
        }
        
        public byte nextByte() {
            last = base+(index++);
            return API.getByte(array, last);
        }
        
        public byte previousByte() {
            last = base+(--index);
            return API.getByte(array, last);
        }
        
        public void setByte(byte value) {
            API.putByte(array, last, value);
        }

        @Override
        public void remove() {
            throw new UnsupportedOperationException(NSE);
        }

        @Override
        public boolean hasPrevious() {
            return index > 0;
        }

        @Override
        public Byte previous() {
            return previousByte();
        }

        @Override
        public int nextIndex() {
            return (int)(index + 1);
        }

        @Override
        public int previousIndex() {
            return (int)(index - 1);
        }

        @Override
        public void set(Byte value) {
            setByte(value);
        }

        @Override
        public void add(Byte value) {
            throw new UnsupportedOperationException(NSE);
        }
        
    }
    
}
