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

import java.lang.reflect.Field;
import net.ranides.assira.generic.Wrapper;
import net.ranides.assira.reflection.util.ReflectUtils;
import net.ranides.assira.text.LexicalCast;

/**
 *
 * @author Ranides Atterwim <ranides@gmail.com>
 */
public final class GenericField<T> {
    
    
    private final Field field;

    public GenericField(Field field) {
        this.field = ReflectUtils.access(field);
    }
    
    /**
     * Returns value of referenced object
     * @return
     */
    public T get(Object that) {
        try {
            return (T)field.get(that);
        } catch (IllegalArgumentException | IllegalAccessException ex) {
            throw new InspectException(ex);
        }
    }

    /**
     * Change value of referenced object
     * @param value
     */
    public void set(Object that, T value) {
        try {
            field.set(that, value);
        } catch (IllegalArgumentException | IllegalAccessException ex) {
            throw new InspectException(ex);
        }
    }

    /**
     * Changes value, automatically casting passed argument, if necessary
     * @param value 
     */
    public void put(Object that, Object value) {
        try {
            field.set(that, LexicalCast.cast(value, type()));
        } catch (IllegalArgumentException | IllegalAccessException ex) {
            throw new InspectException(ex);
        }
    }

    public GenericClass type() {
        return TypeFactory.construct(field.getGenericType());
    }

    public Wrapper<T> bind(Object that) {
        return new FWrapper<>(field, that);
    }

    public Field reflective() {
        return field;
    }
    
    private static final class FWrapper<T> extends Wrapper<T> {
        
        private final Field field;
        private final Object that;
        

        FWrapper(Field field, Object that) {
            this.that = that;
            this.field = field;
        }
        
        @Override
        public T get() {
            try {
                return (T)field.get(that);
            } catch (ReflectiveOperationException ex) {
                throw new InspectException(ex);
            }
        }

        @Override
        public void set(T value) {
            try {
                field.set(that, value);
            } catch (ReflectiveOperationException ex) {
                throw new InspectException(ex);
            }
        }

        @Override
        public void put(Object value) {
            try {
                field.set(that, LexicalCast.cast(value, field.getType()));
            } catch (ReflectiveOperationException ex) {
                throw new InspectException(ex);
            }
        }

        @Override
        public GenericClass type() {
            return TypeFactory.construct(field.getType());
        }
        
    }
    
}
