package it.mice.voila.runtime.springmvc.controller;

import it.mice.voila.runtime.security.SecurityUtils;
import it.mice.voila.runtime.springmvc.bean.PageStatus;
import it.mice.voila.runtime.springmvc.editors.BlobEditor;
import it.mice.voila.runtime.springmvc.editors.ClobEditor;
import it.mice.voila.runtime.springmvc.editors.CustomBigDecimalEditor;
import it.mice.voila.runtime.springmvc.editors.CustomTimestampEditor;
import it.mice.voila.runtime.springmvc.editors.GeometryEditor;
import it.mice.voila.runtime.springmvc.editors.ObjectEditor;
import it.mice.voila.runtime.springmvc.editors.ObjectToStringConverter;
import it.mice.voila.runtime.springmvc.editors.PageStatusEditor;
import it.mice.voila.runtime.springmvc.flashmap.FlashMap;
import it.mice.voila.runtime.springmvc.form.JfFormBean;
import it.mice.voila.runtime.springmvc.interceptors.ControllerBasePathExposingInterceptor;
import it.mice.voila.runtime.springmvc.interceptors.PathStackMantainerInterceptor;
import it.mice.voila.runtime.springmvc.util.SpringMvcUtil;
import it.mice.voila.runtime.springmvc.validation.GroupAwareSpringValidatorAdapter;
import it.mice.voila.runtime.util.UserMessage;
import it.mice.voila.runtime.util.UserMessages;
import it.mice.voila.runtime.validator.BindingErrorProcessor;
import it.mice.voila.runtime.validator.RuntimeValidationProviderResolver;
import it.mice.voila.runtime.validator.ValidatorFactoryBean;

import java.text.SimpleDateFormat;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.validation.Validation;

import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;
import org.displaytag.tags.TableTagParameters;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.beans.propertyeditors.CustomNumberEditor;
import org.springframework.beans.propertyeditors.StringTrimmerEditor;
import org.springframework.context.MessageSourceResolvable;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.ServletRequestDataBinder;
import org.springframework.web.bind.annotation.InitBinder;

import com.vividsolutions.jts.geom.Geometry;

public class DefaultController {
	public static final String REDIRECT_SPECIAL_REFRESH = "redirect:special.refresh";
	public static final String JUMP_TO = "jumpTo";
	public static final String OBJECT_ID = "objectId";
	public static final String FORM_ID = "formId";
	public static final String UNIQUE_OBJECT_ID = "uniqueObjectId";
	private static final SimpleDateFormat ITALY_DATE_FORMAT = new SimpleDateFormat("dd/MM/yyyy");
	private static final SimpleDateFormat ITALY_DATE_TIME_FORMAT = new SimpleDateFormat("dd/MM/yyyy HH:mm");
	private static final SimpleDateFormat US_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
	private static final SimpleDateFormat[] BIND_DATE_FORMATTERS = new SimpleDateFormat[]{ITALY_DATE_TIME_FORMAT, US_DATE_FORMAT, ITALY_DATE_FORMAT};
	private static final ObjectToStringConverter OBJECT_TO_STRING_CONVERTER = new ObjectToStringConverter();

	@Autowired
	private ValidatorFactoryBean theValidatorFactoryBean;
	@Autowired
	private BindingErrorProcessor theBindingErrorProcessor;

//	private GroupAwareSpringValidatorAdapter validator = new GroupAwareSpringValidatorAdapter(Validation.buildDefaultValidatorFactory().getValidator());
	private GroupAwareSpringValidatorAdapter validator = new GroupAwareSpringValidatorAdapter(Validation.byDefaultProvider().providerResolver(new RuntimeValidationProviderResolver()).configure().buildValidatorFactory().getValidator());
	
	public ValidatorFactoryBean getTheValidatorFactoryBean() {
		return theValidatorFactoryBean;
	}

	public void setTheValidatorFactoryBean(
			ValidatorFactoryBean theValidatorFactoryBean) {
		this.theValidatorFactoryBean = theValidatorFactoryBean;
	}

	public BindingErrorProcessor getTheBindingErrorProcessor() {
		return theBindingErrorProcessor;
	}

	public void setTheBindingErrorProcessor(
			BindingErrorProcessor theBindingErrorProcessor) {
		this.theBindingErrorProcessor = theBindingErrorProcessor;
	}

	@InitBinder
	public void initBinder(HttpServletRequest request, ServletRequestDataBinder binder)
			throws Exception {
		binder.setValidator(theValidatorFactoryBean);
		binder.setBindingErrorProcessor(theBindingErrorProcessor);
		
		binder.registerCustomEditor(String.class, new StringTrimmerEditor(true));
		binder.registerCustomEditor(java.sql.Timestamp.class, new CustomTimestampEditor(BIND_DATE_FORMATTERS, ITALY_DATE_TIME_FORMAT, true)); 
		binder.registerCustomEditor(java.util.Date.class, new CustomDateEditor(ITALY_DATE_FORMAT, true)); 
		binder.registerCustomEditor(java.math.BigDecimal.class, new CustomBigDecimalEditor(true)); 
		binder.registerCustomEditor(PageStatus.class, new PageStatusEditor()); 
		binder.registerCustomEditor(java.sql.Blob.class, new BlobEditor()); 
		binder.registerCustomEditor(java.sql.Clob.class, new ClobEditor()); 
		binder.registerCustomEditor(Geometry.class, new GeometryEditor(true));

		//FIXME Replace following two lines with the next commented ones
		binder.registerCustomEditor(java.lang.Object.class, new ObjectEditor()); 
		binder.registerCustomEditor(java.lang.Object.class, "criteriaMap", new ObjectEditor());
//		binder.registerCustomEditor(java.lang.Object.class, new NullIfEmptyObjectEditor()); 
//		binder.registerCustomEditor(java.lang.Object.class, "criteriaMap", new NullIfEmptyObjectEditor());
		
		//Aggiunti Editors
		binder.registerCustomEditor(java.math.BigInteger.class, new CustomNumberEditor(java.math.BigInteger.class, true));
		binder.registerCustomEditor(Long.class, new CustomNumberEditor(Long.class, true));
		binder.registerCustomEditor(Float.class, new CustomNumberEditor(Float.class, true));
		binder.registerCustomEditor(Integer.class, new CustomNumberEditor(Integer.class, true));
		binder.registerCustomEditor(Short.class, new CustomNumberEditor(Short.class, true));
		binder.registerCustomEditor(Byte.class, new CustomNumberEditor(Byte.class, true));
		
		//((ConverterRegistry)binder.getConversionService()).addConverter(OBJECT_TO_STRING_CONVERTER);
	}

	public GroupAwareSpringValidatorAdapter getValidator() {
		return validator;
	}

	protected String getOrderByFromDisplaytag(String displayTagTableName) {
		String orderByToken = "d-" + displayTagTableName + "-n";
		return getRequest().getParameter(orderByToken);
	}

	protected HttpServletRequest getRequest() {
		return SpringMvcUtil.getRequest();
	}

	protected HttpSession getSession() {
		return SpringMvcUtil.getSession();
	}
	
	protected int getFirstResult(int pageSize, String displayTagTableName) {
		String pageNumberToken = "d-" + displayTagTableName + "-p";
		int firstResult = 0;
		if (getRequest().getParameter(pageNumberToken) != null) {
			int pageNumber = Integer.parseInt(getRequest().getParameter(pageNumberToken));
			firstResult = (pageNumber - 1) * pageSize;
		}
		return firstResult;
	}
	
	protected boolean isExportListRequested() {
        return (getRequest().getParameter(TableTagParameters.PARAMETER_EXPORTING) != null);
	}
	
	protected boolean isPostRequest() {
		return getRequest().getMethod().equalsIgnoreCase("POST");
	}

	protected UserDetails getUserDetails() {
		return SecurityUtils.getCurrentUserDetail();
	}

	protected String getUserId() {
		UserDetails userDetails = getUserDetails();
		if (userDetails != null) {
			return userDetails.getUsername();
		} else {
			return null;
		}
	}

	protected String finalizeModelAndView(String viewName, Model model) {
		return finalizeModelAndView(viewName, model, null, null);
	}
	
	protected String finalizeModelAndView(String viewName, Model model, JfFormBean form) {
		return finalizeModelAndView(viewName, model, form, null);
	}
	
	protected String finalizeModelAndView(String viewName, Model model, Object id) {
		return finalizeModelAndView(viewName, model, null, id);
	}
	
	protected String finalizeModelAndView(String viewName, Model model, JfFormBean form, Object id) {
		model.addAttribute(OBJECT_ID, id);
		if (viewName != null) {
			getSession().setAttribute(SpringMvcUtil.LAST_VIEW_NAME, viewName);
		}
		return viewName;
	}

	protected void addMessage(String key, String severity, String fieldPath, Object... params) {
		UserMessage msg = new UserMessage(key, params);
		msg.setSeverity(severity);
		msg.setFieldPath(fieldPath);
		FlashMap.getMessages().add(msg);
	}
	
	protected void addUserMessages(UserMessages userMessages) {
		FlashMap.getMessages().addAll(userMessages);
	}
	
	protected void addUserMessage(UserMessage userMessage) {
		FlashMap.getMessages().add(userMessage);
	}
	
	protected void addInfoMessage(String key, Object... params) {
		addMessage(key, UserMessage.SEVERITY_INFO, null, params);
	}
	
	protected void addWarningMessage(String key, Object... params) {
		addMessage(key, UserMessage.SEVERITY_WARNING, null, params);
	}
	
	protected void addErrorMessage(String key, Object... params) {
		addMessage(key, UserMessage.SEVERITY_ERROR, null, params);
	}
	
	protected void addFieldError(String key, String fieldPath, Object... params) {
		addMessage(key, UserMessage.SEVERITY_ERROR, fieldPath, params);
	}

	protected boolean hasErrors() {
		return !FlashMap.getMessages().isEmpty();
	}
	
	protected void doNotSaveCurrentTransactionIntoStack() {
		SpringMvcUtil.getRequest().setAttribute(PathStackMantainerInterceptor.NO_STACK_PARAM_NAME, "true");
	}

	protected String getQueryString() {
		return "?" + getRequest().getQueryString();
	}
	
	protected String redirectTo(String url, JfFormBean form) {
		return redirectTo(url);
	}
	
	protected String redirectTo(String url) {
		String result = url;
		if (!url.startsWith("/")) {
			String baseControllerUrl = (String)getRequest().getAttribute(ControllerBasePathExposingInterceptor.CONTROLLER_BASE_URL);
			if (baseControllerUrl != null) {
				result = baseControllerUrl + url;
			} else {
				result = StringUtils.defaultString(getRequest().getServletPath()) + StringUtils.defaultString(getRequest().getPathInfo());
				result = result.substring(0, result.lastIndexOf("/") + 1) + url;
			}
		}
		result = appendJumpTo(result);
		return "redirect:" + result;
	}

	/**
	 * @param result
	 * @return
	 */
	private String appendJumpTo(String result) {
		String jumpTo = getRequest().getParameter(JUMP_TO);
		if (!StringUtils.isEmpty(jumpTo)) {
        	String prefix = (result.indexOf("?") > -1 ? "&amp;" : "?");
        	result = result + prefix + JUMP_TO + "=" + jumpTo;
		}
		return result;
	}
	
	protected String buildRedirectSpecialRefresh() {
		return appendJumpTo(REDIRECT_SPECIAL_REFRESH);
	}
	
	protected String buildAjaxResponseViewName(Model model, JfFormBean form) {
		String viewName = (String)getSession().getAttribute(SpringMvcUtil.LAST_VIEW_NAME);
		doNotSaveCurrentTransactionIntoStack();
		return finalizeModelAndView(viewName, model, form);
	}

    /**
     * Check if value has invalid charachetrs and raise a binding errors if any
     * @param errorResult - the current error binder result
     * @param value - the value to be checked
     * @param entityName the entity name
     * @param objectName the property id
     */
    protected void addErrorIfHasInvalidValue(BindingResult errorResult, String value, String objectName) {
        if (StringUtils.isNotEmpty(value) && !StringEscapeUtils.escapeXml(value).equals(value)) {
            errorResult.addError(new ObjectError(objectName, new String[]{"typeMismatch"}, new Object[]{new DefaultMessageSourceResolvable("label." + objectName)}, "Valore non ammesso per il campo"));
        }
    }
}
