package org.kohsuke.args4j.spi;

import org.kohsuke.args4j.CmdLineParser;
import org.kohsuke.args4j.IllegalAnnotationError;

import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Array;
import java.lang.reflect.Field;

/**
 * {@link Setter} that allows multiple values to be stored into one array field.
 *
 * <p>
 * Because of the {@link CmdLineParser} abstractions of allowing incremental parsing of options,
 * this implementation creates a whole new array each time a new value is found.
 *
 * This is also why we don't support a setter method that takes list/array as arguments.
 *
 * @author Kohsuke Kawaguchi
 */
final class ArrayFieldSetter implements Setter {
    private final Object bean;
    private final Field f;

    public ArrayFieldSetter(Object bean, Field f) {
        this.bean = bean;
        this.f = f;

        if(!f.getType().isArray())
            throw new IllegalAnnotationError(Messages.ILLEGAL_FIELD_SIGNATURE.format(f.getType()));
    }

    public FieldSetter asFieldSetter() {
        return new FieldSetter(bean,f);
    }

    public AnnotatedElement asAnnotatedElement() {
        return f;
    }

    public boolean isMultiValued() {
    	return true;
    }

    public Class getType() {
        return f.getType().getComponentType();
    }

    public void addValue(Object value) {
        try {
            doAddValue(bean, value);
        } catch (IllegalAccessException _) {
            // try again
            f.setAccessible(true);
            try {
                doAddValue(bean,value);
            } catch (IllegalAccessException e) {
                throw new IllegalAccessError(e.getMessage());
            }
        }
    }

    private void doAddValue(Object bean, Object value) throws IllegalAccessException {
        Object ary = f.get(bean);
        if (ary == null) {
                ary = Array.newInstance(getType(), 1);
                Array.set(ary, 0, value);
        } else {
            int len = Array.getLength(ary);
            Object newAry = Array.newInstance(ary.getClass().getComponentType(), len +1);
                System.arraycopy(ary, 0, newAry, 0, len);
                Array.set(newAry, len, value);
            ary = newAry;
        }

        f.set(bean, ary);
    }
}

