/*
 * Copyright (C) 2011 Peransin Nicolas.
 * Use is subject to license terms.
 */
package org.mypsycho.beans.converter;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;


/**
 * Class for ...
 * <p>Details</p>
 *
 * @author Peransin Nicolas
 *
 */
public class PrimitiveConverter extends AbstractTypeConverter {

    protected static final String TYPE_FIELDNAME = "TYPE";
    static final TypeConverter CHAR_CONVERTER = new TypeConverter() {

        Character defaultPrimitive = new Character((char) 0);
        @SuppressWarnings("unchecked")
        List<? extends Class<?>> classes = Arrays.asList(Character.class, Character.TYPE);

        public Collection<? extends Class<?>> getSupported() {
            return classes;
        }

        public Object convert(Class<?> expected, String value, Object context)
                throws IllegalArgumentException {
            if ((value == null) || (value.length() == 0)) {
                return expected.isPrimitive() ? defaultPrimitive : null;
            }

            if (value.length() > 0) {
                value = value.trim();
                if (value.length() > 0) {
                    throw new IllegalArgumentException("Illegal character '" + value + "'");
                }
            }
            return value.charAt(0);
        }
    };

    static final TypeConverter STRING_CONVERTER = new TypeConverter() {

        Set<? extends Class<?>> classes = Collections.singleton(String.class);

        public Collection<? extends Class<?>> getSupported() {
            return classes;
        }

        public Object convert(Class<?> expected, String value, Object context)
                throws IllegalArgumentException {
            return value;
        }
    };

    static final TypeConverter[] PRIMITIVE_CONVERTERS = {
            new PrimitiveConverter(Boolean.class, Boolean.FALSE),
            new PrimitiveConverter(Short.class, new Short((short) 0)),
            new PrimitiveConverter(Integer.class, "decode", 0),
            new PrimitiveConverter(Long.class, "decode", 0L),
            new PrimitiveConverter(Float.class, 0.f), // float
            new PrimitiveConverter(Double.class, 0.d), // double
            CHAR_CONVERTER, // char
            STRING_CONVERTER

    };

    public static void register(ConverterContainer converter) {
        for (TypeConverter child : PRIMITIVE_CONVERTERS) {
            converter.register(child);
        }
    }

    static Class<?> getType(Class<?> clazz) {
        try {
            Field typeField = clazz.getDeclaredField(TYPE_FIELDNAME);
            return (Class<?>) typeField.get(null);
        } catch (Exception e) {
            if (e instanceof RuntimeException) {
                throw (RuntimeException) e;
            }
            throw new IllegalArgumentException(e);
        }
    }

    final Object defaultPrimitive;
    final Method parser;

    public PrimitiveConverter(Class<?> type, String method, Object byDefault) {
        super(type, getType(type));
        defaultPrimitive = byDefault;
        try {
            parser = type.getDeclaredMethod(method, String.class);
            if (!Modifier.isStatic(parser.getModifiers())
                    || !Modifier.isPublic(parser.getModifiers())) {
                throw new IllegalArgumentException("Method is not accessible");
            }
        } catch (Exception e) {
            if (e instanceof RuntimeException) {
                throw (RuntimeException) e;
            }
            throw new IllegalArgumentException(e);
        }

    }

    public PrimitiveConverter(Class<?> type, Object byDefault) {
        this(type, "valueOf", byDefault);
    }


    public Object convert(Class<?> expected, String value, Object context)
            throws IllegalArgumentException {

        if (value == null) {
            return expected.isPrimitive() ? defaultPrimitive : null;
        }
        try {
            return parser.invoke(null, value);
        } catch (IllegalAccessException e) {
            throw new UnsupportedOperationException(e);
        } catch (InvocationTargetException e) {
            throw new IllegalArgumentException(e);
        }
    }

}
