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

import net.ranides.assira.collection.arrays.NativeArray.*;

/**
 *
 * @author Ranides Atterwim <ranides@gmail.com>
 */
public abstract class NativeArrayAccess {
    
    private final Class<?> component;

    protected NativeArrayAccess(Class<?> component) {
        this.component = component;
    }
    
    public static NativeArrayAccess forType(Class<?> type) {
        if(!type.isArray()) {
            throw new IllegalArgumentException(type + " is not an array");
        }
        return forComponent(type.getComponentType());
    }
    
    @SuppressWarnings({
        "PMD.NPathComplexity",
        "PMD.CyclomaticComplexity"
    })
    public static NativeArrayAccess forComponent(Class<?> component) {
        if(!component.isPrimitive()) {
            return new ObjectArrayAccess(component);
        }
        if(byte.class.equals(component)) {
            return BYTE_ARRAY;
        }
        if(short.class.equals(component)) {
            return SHORT_ARRAY;
        }
        if(int.class.equals(component)) {
            return INTEGER_ARRAY;
        }
        if(long.class.equals(component)) {
            return LONG_ARRAY;
        }
        if(float.class.equals(component)) {
            return FLOAT_ARRAY;
        }
        if(double.class.equals(component)) {
            return DOUBLE_ARRAY;
        }
        if(char.class.equals(component)) {
            return CHARACTER_ARRAY;
        }
        if(boolean.class.equals(component)) {
            return BOOLEAN_ARRAY;
        }
        throw new UnsupportedOperationException("Not supported type: " + component.getName());
    }
    
    public final Class<?> component() {
        return component;
    }
    
    public abstract NativeArray allocate(int size);
    
    public abstract NativeArray bind(Object that);
    
    public abstract int size(Object that);
    
    public abstract void set(Object that, int index, Object value);
    
    public abstract Object get(Object that, int index);
    
    private static final NativeArrayAccess BYTE_ARRAY = new NativeArrayAccess(byte.class){
        
        @Override
        public NativeArray allocate(int size) {
            return new ByteArray(size);
        }
        
        @Override
        public NativeArray bind(Object that) {
            return new ByteArray(that);
        }
        
        @Override
        public int size(Object that) {
            return ((byte[])that).length;
        }

        @Override
        public void set(Object that, int index, Object value) {
            ((byte[])that)[index] = (Byte)value;
        }

        @Override
        public Object get(Object that, int index) {
            return ((byte[])that)[index];
        }
  
    };
    
    private static final NativeArrayAccess SHORT_ARRAY = new NativeArrayAccess(short.class){
        
        @Override
        public NativeArray allocate(int size) {
            return new ShortArray(size);
        }
        
        @Override
        public NativeArray bind(Object that) {
            return new ShortArray(that);
        }
        
        @Override
        public int size(Object that) {
            return ((short[])that).length;
        }

        @Override
        public void set(Object that, int index, Object value) {
            ((short[])that)[index] = (Short)value;
        }

        @Override
        public Object get(Object that, int index) {
            return ((short[])that)[index];
        }
  
    };
    
    
    private static final NativeArrayAccess INTEGER_ARRAY = new NativeArrayAccess(int.class){
        
        @Override
        public NativeArray allocate(int size) {
            return new IntegerArray(size);
        }
        
        @Override
        public NativeArray bind(Object that) {
            return new IntegerArray(that);
        }
        
        @Override
        public int size(Object that) {
            return ((int[])that).length;
        }

        @Override
        public void set(Object that, int index, Object value) {
            ((int[])that)[index] = (Integer)value;
        }

        @Override
        public Object get(Object that, int index) {
            return ((int[])that)[index];
        }
  
    };
    
    private static final NativeArrayAccess LONG_ARRAY = new NativeArrayAccess(long.class){
        
        @Override
        public NativeArray allocate(int size) {
            return new LongArray(size);
        }
        
        @Override
        public NativeArray bind(Object that) {
            return new LongArray(that);
        }
        
        @Override
        public int size(Object that) {
            return ((long[])that).length;
        }

        @Override
        public void set(Object that, int index, Object value) {
            ((long[])that)[index] = (Long)value;
        }

        @Override
        public Object get(Object that, int index) {
            return ((long[])that)[index];
        }
  
    };
    
    private static final NativeArrayAccess FLOAT_ARRAY = new NativeArrayAccess(float.class){
        
        @Override
        public NativeArray allocate(int size) {
            return new FloatArray(size);
        }
        
        @Override
        public NativeArray bind(Object that) {
            return new FloatArray(that);
        }
        
        @Override
        public int size(Object that) {
            return ((float[])that).length;
        }

        @Override
        public void set(Object that, int index, Object value) {
            ((float[])that)[index] = (Float)value;
        }

        @Override
        public Object get(Object that, int index) {
            return ((float[])that)[index];
        }
  
    };
    
    private static final NativeArrayAccess DOUBLE_ARRAY = new NativeArrayAccess(double.class){
        
        @Override
        public NativeArray allocate(int size) {
            return new DoubleArray(size);
        }
        
        @Override
        public NativeArray bind(Object that) {
            return new DoubleArray(that);
        }
        
        @Override
        public int size(Object that) {
            return ((double[])that).length;
        }

        @Override
        public void set(Object that, int index, Object value) {
            ((double[])that)[index] = (Double)value;
        }

        @Override
        public Object get(Object that, int index) {
            return ((double[])that)[index];
        }
  
    };
    
    private static final NativeArrayAccess BOOLEAN_ARRAY = new NativeArrayAccess(boolean.class){
        
        @Override
        public NativeArray allocate(int size) {
            return new BooleanArray(size);
        }
        
        @Override
        public NativeArray bind(Object that) {
            return new BooleanArray(that);
        }
        
        @Override
        public int size(Object that) {
            return ((boolean[])that).length;
        }

        @Override
        public void set(Object that, int index, Object value) {
            ((boolean[])that)[index] = (Boolean)value;
        }

        @Override
        public Object get(Object that, int index) {
            return ((boolean[])that)[index];
        }
  
    };
    
    private static final NativeArrayAccess CHARACTER_ARRAY = new NativeArrayAccess(char.class){
        
        @Override
        public NativeArray allocate(int size) {
            return new CharacterArray(size);
        }
        
        @Override
        public NativeArray bind(Object that) {
            return new CharacterArray(that);
        }
        
        @Override
        public int size(Object that) {
            return ((char[])that).length;
        }

        @Override
        public void set(Object that, int index, Object value) {
            ((char[])that)[index] = (Character)value;
        }

        @Override
        public Object get(Object that, int index) {
            return ((char[])that)[index];
        }
  
    };
    
    private static final class ObjectArrayAccess extends NativeArrayAccess {

        public ObjectArrayAccess(Class<?> component) {
            super(component);
        }
        
        @Override
        public NativeArray allocate(int size) {
            return new ObjectArray(component(), size);
        }
        
        @Override
        public NativeArray bind(Object that) {
            return new ObjectArray(that);
        }
        
        @Override
        public int size(Object that) {
            return ((Object[])that).length;
        }

        @Override
        public void set(Object that, int index, Object value) {
            ((Object[])that)[index] = value;
        }

        @Override
        public Object get(Object that, int index) {
            return ((Object[])that)[index];
        }
  
    };
    
    

}
