package de.uni_hamburg.fs;

import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;


/**
 * Represents a feature for handling indexed collections in Java objects.
 * This class provides functionality to get and set values in indexed structures
 * through reflection using get, count, and add methods.
 */
class IndexFeature extends JavaFeature {
    /**
     * Logger instance for this class to handle error messages and debugging information.
     */
    public static final org.apache.log4j.Logger LOGGER =
        org.apache.log4j.Logger.getLogger(IndexFeature.class);
    private Method _getCount;
    private Method _get;
    private Method _add;
    private Class<?> _returnType;

    /**
     * Constructs an IndexFeature with the specified reflection methods.
     *
     * @param getCount the method to get the count of elements
     * @param get      the method to get an element at a specific index
     * @param add      the method to add an element (can be null if read-only)
     */
    IndexFeature(Method getCount, Method get, Method add) {
        this._getCount = getCount;
        this._get = get;
        this._add = add;
        _returnType = Array.newInstance(get.getReturnType(), 0).getClass();
    }

    @Override
    Class<?> getJavaClass() {
        return _returnType;
    }

    @Override
    Object getObjectValue(Object javaObject) {
        Throwable e = null;
        try {
            int count = ((Integer) _getCount.invoke(javaObject, JavaConcept.NOPARAM)).intValue();
            Object[] result = (Object[]) Array.newInstance(_get.getReturnType(), count);
            for (int i = 0; i < count; ++i) {
                result[i] = _get.invoke(javaObject, new Object[] { Integer.valueOf(i) });
            }
            return result;
        } catch (IllegalAccessException e1) {
            e = e1;
            // should not happen
        } catch (IllegalArgumentException e2) {
            e = e2;
            // should not happen
        } catch (InvocationTargetException e3) {
            e = e3.getTargetException();
        }
        LOGGER.error("Could not get index values of Object " + javaObject + ":");
        LOGGER.error(e.getMessage(), e);
        //throw new RuntimeException("Exception during feature extraction:");
        return null;
    }

    @Override
    boolean canSet() {
        return _add != null;
    }

    @Override
    void setObjectValue(Object javaObject, Object value) {
        if (_add == null) {
            // no add method
            String msg = "Cannot set attribute of objects of " + javaObject.getClass().getName()
                + ": No add method for\n" + _get;
            LOGGER.error(msg);
            throw new RuntimeException(msg);
        }
        Throwable e = null;
        try {
            Object[] valarray = (Object[]) value;
            for (int i = 0; i < valarray.length; ++i) {
                _add.invoke(javaObject, new Object[] { valarray[i] });
            }
            return;
        } catch (IllegalAccessException e1) {
            e = e1;
            // should not happen
        } catch (IllegalArgumentException e2) {
            e = e2;
            // should not happen
        } catch (InvocationTargetException e3) {
            e = e3.getTargetException();
        } catch (Throwable t) {
            e = t;
        }
        LOGGER.error(e.getMessage(), e);
        throw new RuntimeException("Exception during feature setting:");
    }
}