/**
 * 
 */
package it.mice.voila.runtime.web.restws;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import it.mice.voila.runtime.entity.EntityGeneric;
import it.mice.voila.runtime.exception.BusinessException;
import it.mice.voila.runtime.security.SecurityUtils;
import it.mice.voila.runtime.util.UserMessage;

/**
 *
 */
public class GenericJsonController {
	private static Log logger = LogFactory.getLog(GenericJsonController.class);
	/**
	 * MESSAGE_GENERIC_NOT_FOUND
	 */
	protected static final String MESSAGE_GENERIC_NOT_FOUND = "message.genericNotFound";

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

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

	protected ModelMap getJsonResult(Object dataObject) {
		ModelMap map = new ModelMap();
		map.addAttribute("data", dataObject);
		return map;
	}

	protected ModelMap getJsonException(Exception ex, HttpServletResponse response) {
		return getJsonException(ex, HttpStatus.INTERNAL_SERVER_ERROR, response);
	}

	protected ModelMap getJsonException(Exception ex, HttpStatus status, HttpServletResponse response) {
		return getJsonException(ex, HttpStatus.BAD_REQUEST, HttpStatus.BAD_REQUEST.value() + "", response);
	}

	protected ModelMap getJsonException(Exception ex, HttpStatus status, String errorKey,
			HttpServletResponse response) {
		logger.warn(ex);
		ModelMap map = new ModelMap();
		map.addAttribute("error", new JsonCallError(status.value(), errorKey, ex.getMessage(), ex.getClass().getName()));
		response.setStatus(status.value());
		return map;
	}

	@ExceptionHandler(BusinessException.class)
	public @ResponseBody ModelMap handleNotAuthenticatedException(BusinessException ex, HttpServletRequest request,
			HttpServletResponse response) throws IOException {

		if (logger.isInfoEnabled()) {
			logger.info("", ex);
		}
		UserMessage msg = ex.getUserMessages().iterator().next();
		return getJsonException(ex, HttpStatus.BAD_REQUEST, msg.getMessageKey(), response);
	}

	@ExceptionHandler(BadCredentialsException.class)
	public @ResponseBody ModelMap handleNotAuthenticatedException(BadCredentialsException ex,
			HttpServletRequest request, HttpServletResponse response) throws IOException {
		return getJsonException(ex, HttpStatus.UNAUTHORIZED, response);
	}

	@ExceptionHandler(Exception.class)
	public @ResponseBody ModelMap handleNotAuthenticatedException(Exception ex, HttpServletRequest request,
			HttpServletResponse response) throws IOException {

		logger.error("", ex);
		return getJsonException(ex, response);
	}

	protected String escapeNonAscii(String str) {
		StringBuilder retStr = new StringBuilder();
		for (int i = 0; i < str.length(); i++) {
			int cp = Character.codePointAt(str, i);
			int charCount = Character.charCount(cp);
			if (charCount > 1) {
				i += charCount - 1; // 2.
				if (i >= str.length()) {
					throw new IllegalArgumentException("truncated unexpectedly");
				}
			}
			if (cp < 128) {
				retStr.appendCodePoint(cp);
			} else {
				retStr.append(String.format("\\u%x", cp));
			}
		}
		return retStr.toString();
	}

	/**
	 * createCriteriaMapForUser.
	 * 
	 * @param username the username.
	 * @return the result.
	 */
	protected Map<String, Object> createCriteriaMapForUser(String username) {
		Map<String, Object> criteria = new HashMap<String, Object>();
		criteria.put("preferitiForUsername", username);
		return criteria;
	}

	public static abstract class FindByFilterGenericRequest<E extends EntityGeneric> {
		private E filterFrom;
		private E filterTo;

		private Map<String, Object> criteriaMap = new HashMap<String, Object>();

		private Integer firstResultRow;
		private Integer searchResultPageSize;
		private String orderBy;

		public E getFilterFrom() {
			return filterFrom;
		}

		public void setFilterFrom(E filterFrom) {
			this.filterFrom = filterFrom;
		}

		public E getFilterTo() {
			return filterTo;
		}

		public void setFilterTo(E filterTo) {
			this.filterTo = filterTo;
		}

		public Map<String, Object> getCriteriaMap() {
			return criteriaMap;
		}

		public void setCriteriaMap(Map<String, Object> criteriaMap) {
			this.criteriaMap = criteriaMap;
		}

		public Integer getSearchResultPageSize() {
			return searchResultPageSize;
		}

		public void setSearchResultPageSize(Integer searchResultPageSize) {
			this.searchResultPageSize = searchResultPageSize;
		}

		public Integer getFirstResultRow() {
			return firstResultRow;
		}

		public void setFirstResultRow(Integer firstResultRow) {
			this.firstResultRow = firstResultRow;
		}

		public String getOrderBy() {
			return orderBy;
		}

		public void setOrderBy(String orderBy) {
			this.orderBy = orderBy;
		}
	}
}
