package org.jboss.resteasy.test.resource.param.resource;

import java.lang.annotation.Annotation;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Date;

import javax.ws.rs.ext.ParamConverter;
import javax.ws.rs.ext.ParamConverterProvider;

public class MultiValuedParamConverterProvider implements ParamConverterProvider {

    @SuppressWarnings("unchecked")
    @Override
    public <T> ParamConverter<T> getConverter(Class<T> rawType, Type genericType, Annotation[] annotations) {
        if (MultiValuedParam.class.isAssignableFrom(rawType)) {
            ParamConverter<T> paramConverter = (ParamConverter<T>) getConverter(getType(genericType));
            return (ParamConverter<T>) (paramConverter != null ? new MultiValuedParamConverter(paramConverter) : null);
        } else if (MultiValuedCookieParam.class.isAssignableFrom(rawType)) {
            ParamConverter<T> paramConverter = (ParamConverter<T>) getConverter(getType(genericType));
            return (ParamConverter<T>) (paramConverter != null ? new MultiValuedCookieParamConverter(paramConverter)
                    : null);
        } else if (MultiValuedPathParam.class.isAssignableFrom(rawType)) {
            ParamConverter<T> paramConverter = (ParamConverter<T>) getConverter(getType(genericType));
            return (ParamConverter<T>) (paramConverter != null ? new MultiValuedPathParamConverter(paramConverter)
                    : null);
        } else if (rawType.isArray()) {
            Class<?> componentType = rawType.getComponentType();
            if (genericType instanceof GenericArrayType) {
                Type genericComponentType = ((GenericArrayType) genericType).getGenericComponentType();
                if (ParamWrapper.class.isAssignableFrom(componentType)) {
                    ParamConverter<Object> paramConverter = (ParamConverter<Object>) getConverter(
                            getType(genericComponentType));
                    return (ParamConverter<T>) (paramConverter != null ? new ParamWrapperArrayConverter(paramConverter) : null);
                } else if (CookieParamWrapper.class.isAssignableFrom(componentType)) {
                    ParamConverter<Object> paramConverter = (ParamConverter<Object>) getConverter(
                            getType(genericComponentType));
                    return (ParamConverter<T>) (paramConverter != null ? new CookieParamWrapperArrayConverter(paramConverter)
                            : null);
                } else if (PathParamWrapper.class.isAssignableFrom(componentType)) {
                    ParamConverter<Object> paramConverter = (ParamConverter<Object>) getConverter(
                            getType(genericComponentType));
                    return (ParamConverter<T>) (paramConverter != null ? new PathParamWrapperArrayConverter(paramConverter)
                            : null);
                }
            }
        }
        return (ParamConverter<T>) getConverter(rawType);
    }

    private static Class<?> getType(Type genericType) {
        ParameterizedType parameterizedType = (ParameterizedType) genericType;
        Type type = parameterizedType.getActualTypeArguments()[0];
        return (type instanceof Class) ? (Class<?>) type : null;
    }

    private static ParamConverter<?> getConverter(Class<?> rawType) {
        if (Date.class.isAssignableFrom(rawType)) {
            return new DateParamConverter();
        }
        return null;
    }

}
