/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.ocelotds.core.services;

import org.ocelotds.marshalling.ArgumentServices;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.ArrayType;
import com.fasterxml.jackson.databind.type.CollectionType;
import com.fasterxml.jackson.databind.type.MapType;
import com.fasterxml.jackson.databind.type.SimpleType;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Map;
import javax.inject.Inject;
import org.ocelotds.Constants;
import org.ocelotds.annotations.OcelotLogger;
import org.ocelotds.marshalling.exceptions.JsonMarshallerException;
import org.ocelotds.marshalling.JsonMarshallerServices;
import org.ocelotds.marshalling.IJsonMarshaller;
import org.ocelotds.marshalling.annotations.JsonMarshallerType;
import org.ocelotds.marshalling.annotations.JsonUnmarshaller;
import org.ocelotds.marshalling.exceptions.JsonUnmarshallingException;
import org.slf4j.Logger;

/**
 *
 * @author hhfrancois
 */
public class ArgumentConvertor implements IArgumentConvertor {

	@Inject
	@OcelotLogger
	private Logger logger;

	@Inject
	ObjectMapper objectMapper;

	@Inject
	ArgumentServices argumentServices;

	@Inject
	JsonMarshallerServices jsonMarshallerServices;
	
	/**
	 * Convert json to Java
	 *
	 * @param jsonArg
	 * @param paramType
	 * @param parameterAnnotations
	 * @return
	 * @throws org.ocelotds.marshalling.exceptions.JsonUnmarshallingException
	 * @throws org.ocelotds.marshalling.exceptions.JsonMarshallerException
	 */
	@Override
	public Object convertJsonToJava(String jsonArg, Type paramType, Annotation[] parameterAnnotations) throws JsonUnmarshallingException, JsonMarshallerException {
		if ("null".equals(jsonArg)) {
			return null;
		}
		JsonUnmarshaller juma = getJsonUnmarshallerAnnotation(parameterAnnotations);
		if (null != juma) {
			Object result = getResult(jsonArg, jsonMarshallerServices.getIJsonMarshallerInstance(juma.value()), juma.type());
			argumentServices.checkType(result, paramType);
			return result;
		} else {
			return convertArgument(jsonArg, paramType);
		}
	}

	Object getResult(String jsonArg, IJsonMarshaller ijm, JsonMarshallerType type) throws JsonUnmarshallingException {
		Object result;
		switch (type) {
			case LIST:
				result = argumentServices.getJavaResultFromSpecificUnmarshallerIterable(jsonArg, ijm);
				break;
			case MAP:
				result = argumentServices.getJavaResultFromSpecificUnmarshallerMap(jsonArg, ijm);
				break;
			default:
				result = ijm.toJava(jsonArg);
		}
		return result;
	}

	/**
	 * return the JsonUnmarshaller annotation
	 *
	 * @param annotations
	 * @return
	 */
	JsonUnmarshaller getJsonUnmarshallerAnnotation(Annotation[] annotations) {
		for (Annotation annotation : annotations) {
			if (JsonUnmarshaller.class.isInstance(annotation)) {
				return (JsonUnmarshaller) annotation;
			}
		}
		return null;
	}

	/**
	 * If argument is annotated with JsonUnmarshaller annotation, get the JsonUnmarshaller class
	 *
	 * @param annotations
	 * @param paramType
	 * @return
	 */
	Class<? extends IJsonMarshaller> getMarshallerAnnotation(Annotation[] annotations) {
		JsonUnmarshaller ju = getJsonUnmarshallerAnnotation(annotations);
		return (ju != null) ? ju.value() : null;
	}

	/**
	 * try to convert json argument in java type
	 *
	 * @param arg
	 * @param paramType
	 * @return
	 * @throws IllegalArgumentException
	 */
	Object convertArgument(String arg, Type paramType) throws IllegalArgumentException {
		Object result = null;
		if (null == arg || "null".equals(arg)) {
			return result;
		}
		logger.debug("Try to convert {} : param = {} : {}", new Object[]{arg, paramType, paramType.getClass()});
		try { // GenericArrayType, ParameterizedType, TypeVariable<D>, WildcardType, Class
			if (ParameterizedType.class.isInstance(paramType)) {
				JavaType javaType = getJavaType(paramType);
				logger.debug("Try to convert '{}' to JavaType : '{}'", arg, paramType);
				result = getObjectMapper().readValue(arg, javaType);
				logger.debug("Conversion of '{}' to '{}' : OK", arg, paramType);
			} else if (Class.class.isInstance(paramType)) {
				Class cls = (Class) paramType;
				logger.debug("Try to convert '{}' to Class '{}'", arg, paramType);
				checkStringArgument(cls, arg);
				result = getObjectMapper().readValue(arg, cls);
				logger.debug("Conversion of '{}' to '{}' : OK", arg, paramType);
			} else { // GenericArrayType, TypeVariable<D>, WildcardType
				logger.warn("Conversion of '{}' to '{}' not yet supported", arg, paramType);
			}
		} catch (IOException ex) {
			logger.debug("Conversion of '{}' to '{}' failed", arg, paramType);
			throw new IllegalArgumentException(paramType.toString());
		}
		return result;
	}

	/**
	 * check if class and argument are string
	 *
	 * @param cls
	 * @param arg
	 * @throws IOException
	 */
	void checkStringArgument(Class cls, String arg) throws IOException {
		if (arg.startsWith(Constants.QUOTE)) { // ca ressemble à une string
			if (!cls.equals(String.class)) { // et on veut pas une string
				throw new IOException();
			}
		} else // ca ressemble pas à une string
		 if (cls.equals(String.class)) { // mais on veut une string
				throw new IOException();
			}
	}

	private JavaType getJavaType(Type type) {
		Class clazz;
		logger.debug("Computing type of {} - {}", type.getClass(), type.toString());
		if (type instanceof ParameterizedType) {
			clazz = (Class) ((ParameterizedType) type).getRawType();
		} else {
			clazz = (Class) type;
		}
		JavaType javaType;
		Type actualType;
		if (Collection.class.isAssignableFrom(clazz)) {
			ParameterizedType pt = (ParameterizedType) type;
			actualType = pt.getActualTypeArguments()[0];
			JavaType t1 = getJavaType(actualType);
			javaType = CollectionType.construct(Collection.class, t1);
		} else if (clazz.isArray()) {
			Class t = clazz.getComponentType();
			JavaType t1 = getJavaType(t);
			javaType = ArrayType.construct(t1, null, null);
		} else if (Map.class.isAssignableFrom(clazz)) {
			ParameterizedType pt = (ParameterizedType) type;
			actualType = pt.getActualTypeArguments()[0];
			JavaType t1 = getJavaType(actualType);
			actualType = pt.getActualTypeArguments()[1];
			JavaType t2 = getJavaType(actualType);
			javaType = MapType.construct(Map.class, t1, t2);
		} else {
			javaType = SimpleType.construct(clazz);
		}
		return javaType;
	}

	ObjectMapper getObjectMapper() {
		return objectMapper;
	}

}

@@ -998,65 +998,8 @@
 n;%0D%0A
-import org.ocelotds.marshalling.JsonMarshallerServices;%0D%0A
 impo
@@ -1522,70 +1522,8 @@
 %0D%0A%0D%0A
-%09@Inject%0D%0A%09JsonMarshallerServices jsonMarshallerServices;%0D%0A%09%0D%0A
 %09/**
@@ -2172,22 +2172,16 @@
 rg, 
-jsonMarshaller
+argument
 Serv

move getIJsonMarshallerInstance in argumentServices