package org.onetwo.boot.core.web.view;

import java.io.IOException;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Pattern;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.onetwo.boot.core.json.ObjectMapperProvider;
import org.onetwo.common.jackson.JsonMapper;
import org.onetwo.common.web.utils.WebHolder;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.converter.json.MappingJacksonValue;

/**
 * @author wayshall
 * <br/>
 */
public class ExtJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter implements InitializingBean {
	private Set<String> jsonpParameterNames = new LinkedHashSet<String>(Arrays.asList("jsonp", "callback"));
	private static final Pattern CALLBACK_PARAM_PATTERN = Pattern.compile("[0-9A-Za-z_\\.]*");
	
	private Jackson2ObjectMapperBuilder mapperBuilder;
	@Autowired(required=false)
	private ObjectMapperProvider objectMapperProvider;

	public ExtJackson2HttpMessageConverter() {
		super(JsonMapper.ignoreNull().getObjectMapper());
		setSupportedMediaTypes(Arrays.asList(MediaType.APPLICATION_JSON, 
				MediaType.APPLICATION_JSON_UTF8
//				MediaType.TEXT_PLAIN
				));
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		if(mapperBuilder!=null){
			setObjectMapper(mapperBuilder.build());
		}
		if(objectMapperProvider!=null){
			setObjectMapper(objectMapperProvider.createObjectMapper());
		}
	}
	

	
	@Override
	protected void writeInternal(Object object, Type type, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
//		outputMessage.getHeaders().setAcceptCharset(Arrays.asList(StandardCharsets.UTF_8));
		Optional<HttpServletRequest> requestOpt = WebHolder.getRequest();
		if(requestOpt.isPresent()){
			object = filterAndWrapModel(object, requestOpt.get());
		}
		super.writeInternal(object, type, outputMessage);
	}

	public void setMapperBuilder(Jackson2ObjectMapperBuilder mapperBuilder) {
		this.mapperBuilder = mapperBuilder;
	}

	public void setObjectMapperProvider(ObjectMapperProvider objectMapperProvider) {
		this.objectMapperProvider = objectMapperProvider;
	}

	protected Object filterAndWrapModel(Object value, HttpServletRequest request) {
		String jsonpParameterValue = getJsonpParameterValue(request);
		if (jsonpParameterValue != null) {
			if (value instanceof MappingJacksonValue) {
				((MappingJacksonValue) value).setJsonpFunction(jsonpParameterValue);
			}
			else {
				MappingJacksonValue container = new MappingJacksonValue(value);
				container.setJsonpFunction(jsonpParameterValue);
				value = container;
			}
		}
		return value;
	}
	
	public void setJsonpParameterNames(Set<String> jsonpParameterNames) {
		this.jsonpParameterNames = jsonpParameterNames;
	}

	private String getJsonpParameterValue(HttpServletRequest request) {
		if (this.jsonpParameterNames != null) {
			for (String name : this.jsonpParameterNames) {
				String value = request.getParameter(name);
				if (StringUtils.isEmpty(value) || !isValidJsonpQueryParam(value)) {
					continue;
				}
				return value;
			}
		}
		return null;
	}
	
	protected boolean isValidJsonpQueryParam(String value) {
		return CALLBACK_PARAM_PATTERN.matcher(value).matches();
	}
	
}

@@ -2342,16 +2342,31 @@
 (object,
+ outputMessage,
  request
@@ -2373,24 +2373,24 @@
 Opt.get());%0A
-
 %09%09%7D%0A%09%09super.
@@ -2742,16 +2742,49 @@
 t value,
+ HttpOutputMessage outputMessage,
  HttpSer
@@ -3186,21 +3186,119 @@
 tainer;%0A
-
 %09%09%09%7D%0A
+%09%09%09outputMessage.getHeaders().setContentType(MediaType.parseMediaType(%22application/javascript%22));%0A
 %09%09%7D%0A%09%09re

change content-type for jsonp