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

import it.mice.voila.runtime.springmvc.bean.PageStatus;
import it.mice.voila.runtime.springmvc.editors.CustomBigDecimalEditor;
import it.mice.voila.runtime.springmvc.editors.CustomTimestampEditor;
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.PathStackMantainerInterceptor;
import it.mice.voila.runtime.springmvc.util.portlet.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.ValidatorFactoryBean;
import javax.validation.Validation;

import java.lang.reflect.ParameterizedType;
import java.text.SimpleDateFormat;

import javax.portlet.PortletPreferences;
import javax.portlet.PortletRequest;
import javax.portlet.PortletSession;

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.StringTrimmerEditor;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.portlet.bind.PortletRequestDataBinder;

public class DefaultController<F> {

	public static final String OBJECT_ID = "objectId";
	private Class<F> persistentFormClass;
	private static final SimpleDateFormat ITALY_DATE_FORMAT = new SimpleDateFormat("dd/MM/yyyy");
	private static final SimpleDateFormat US_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
	private static final SimpleDateFormat[] BIND_DATE_FORMATTERS = new SimpleDateFormat[]{US_DATE_FORMAT, ITALY_DATE_FORMAT};

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

	private GroupAwareSpringValidatorAdapter validator = new GroupAwareSpringValidatorAdapter(Validation.buildDefaultValidatorFactory().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;
	}

	@SuppressWarnings("unchecked")
	public DefaultController() {
		try {
			this.persistentFormClass = (Class<F>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
		} catch (Exception e) {
			Class dc = (Class) getClass().getGenericSuperclass();
			this.persistentFormClass = (Class<F>) ((ParameterizedType) dc.getGenericSuperclass()).getActualTypeArguments()[0];
		}
	}
	
	public Class<F> getPersistentFormClass() {
		return persistentFormClass;
	}

	

	@InitBinder
	public void initBinder(PortletRequestDataBinder binder, PortletPreferences preferences) {
		
		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_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()); 
		
	}
	
	public GroupAwareSpringValidatorAdapter getValidator() {
		return validator;
	}

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

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

	protected PortletSession 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() {
		if (SecurityContextHolder.getContext().getAuthentication() != null) {
			return (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
		} else {
			return null;
		}
	}

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

	protected F getForm() {
		return getFormInternal(false);
	}

	@SuppressWarnings("unchecked")
	protected F getFormInternal(boolean resetForm) {
		F form = resetForm ? null : (F) getSession().getAttribute(getFormName());
		if (form == null) {
			try {
				form = persistentFormClass.newInstance();
//				ServletRequestDataBinder srdb = new ServletRequestDataBinder(form);
//				srdb.bind(getRequest());
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
			saveFormIntoScope(getFormName(), form);
		}
		return form;
	}
	
	protected void saveFormIntoScope(String formName, F form) {
		getSession().setAttribute(formName, form);
	}

	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 (form != null) {
			model.addAttribute(getFormName(), form);
		} else {
			model.addAttribute(getFormName(), getForm());
		}
		if (viewName != null) {
			getSession().setAttribute(SpringMvcUtil.LAST_VIEW_NAME, viewName);
		}
		return viewName;
	}

	protected String getFormName() {
		return StringUtils.uncapitalize(getPersistentFormClass().getSimpleName());
	}

	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 void doNotSaveCurrentTransactionIntoStack() {
		SpringMvcUtil.getRequest().setAttribute(PathStackMantainerInterceptor.NO_STACK_PARAM_NAME, "true");
	}

//	protected String getQueryString() {
//		return "?" + getRequest().getQueryString();
//	}
	
//	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().getContextPath()) + StringUtils.defaultString(getRequest().getPortalContext().getPortalInfo());
//				result = result.substring(0, result.lastIndexOf("/") + 1) + url;
//			}
//		}
//		return "redirect:" + result;
//	}
}
