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

import it.mice.voila.runtime.entity.Entity;
import it.mice.voila.runtime.entity.EntityGeneric;
import it.mice.voila.runtime.exception.RuntimeBusinessException;
import it.mice.voila.runtime.springmvc.bean.PageStatus;
import it.mice.voila.runtime.springmvc.form.JfFormBean;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.SocketException;
import java.sql.Blob;
import java.util.Collection;
import java.util.List;

import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pojomatic.diff.Difference;
import org.pojomatic.diff.Differences;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.web.multipart.MultipartFile;

public class ManageController<F extends JfFormBean> extends DefaultFormController<F> {
	/**
	 * Default logger.
	 */
	private static Log logger = LogFactory.getLog(ManageController.class);
	/**
	 * METHOD_ID
	 */
	protected static final String METHOD_ID_PARAM = "{id}";

	/**
	 * METHOD_CLONE
	 */
	protected static final String METHOD_CLONE = "clone/";
	/**
	 * METHOD_CLONE_ID
	 */
	protected static final String METHOD_CLONE_ID = METHOD_CLONE + METHOD_ID_PARAM;

	/**
	 * METHOD_PRINT
	 */
	protected static final String METHOD_PRINT = "print/";
	/**
	 * METHOD_PRINT_FORMAT
	 */
	protected static final String METHOD_PRINT_FORMAT = METHOD_PRINT + "{format}";
	/**
	 * METHOD_PRINT_ID_FORMAT
	 */
	protected static final String METHOD_PRINT_ID_FORMAT = METHOD_PRINT + METHOD_ID_PARAM + "/{format}";

	/**
	 * METHOD_DELETE
	 */
	protected static final String METHOD_DELETE = "delete";
	/**
	 * METHOD_DELETE_ID
	 */
	protected static final String METHOD_DELETE_ID = METHOD_DELETE + "/" + METHOD_ID_PARAM;
	/**
	 * METHOD_SAVE_NEW_ELEMENT
	 */
	protected static final String METHOD_SAVE_NEW_ELEMENT = "saveNewElement";
	/**
	 * METHOD_SAVE_ELEMENT
	 */
	protected static final String METHOD_SAVE_ELEMENT = "saveElement";
	/**
	 * METHOD_EDIT_ID
	 */
	protected static final String METHOD_EDIT = "edit/";
	/**
	 * METHOD_EDIT_ID
	 */
	protected static final String METHOD_EDIT_ID = METHOD_EDIT + METHOD_ID_PARAM;
	/**
	 * METHOD_VIEW_ID
	 */
	protected static final String METHOD_VIEW = "view/";
	/**
	 * METHOD_VIEW_ID
	 */
	protected static final String METHOD_VIEW_ID = METHOD_VIEW + METHOD_ID_PARAM;
	/**
	 * METHOD_NEW
	 */
	protected static final String METHOD_NEW = "new";
	/**
	 * METHOD_SEARCH
	 */
	protected static final String METHOD_SEARCH = "search";
	/**
	 * METHOD_INIT
	 */
	protected static final String METHOD_INIT = "init";
	/**
	 * METHOD_NEXT_PAGE
	 */
	protected static final String METHOD_NEXT_PAGE = "nextPage";
	/**
	 * METHOD_NEXT_PAGE
	 */
	protected static final String METHOD_PREV_PAGE = "prevPage";
	/**
	 * METHOD_DOWNLOAD_ID
	 */
	protected static final String METHOD_DOWNLOAD = "download/";
	/**
	 * METHOD_DOWNLOAD_ID
	 */
	protected static final String METHOD_DOWNLOAD_ID = METHOD_DOWNLOAD + METHOD_ID_PARAM;
	/**
	 * METHOD_EXECUTE_ID_FORMID
	 */
	protected static final String METHOD_EXECUTE = "execute/";
	/**
	 * METHOD_EXECUTE_ID_FORMID
	 */
	protected static final String METHOD_EXECUTE_ID_FORMID = METHOD_EXECUTE + METHOD_ID_PARAM + "/{formId}";
	/**
	 * SPECIAL_END_FLOW
	 */
	protected static final String SPECIAL_END_FLOW = "special.endFlow";
	/**
	 * MESSAGE_GENERIC_NOT_FOUND
	 */
	protected static final String MESSAGE_GENERIC_NOT_FOUND = "message.genericNotFound";
	/**
	 * MESSAGE_GENERIC_DELETED
	 */
	protected static final String MESSAGE_GENERIC_DELETED = "message.genericDeleted";
	/**
	 * MESSAGE_GENERIC_INSERT
	 */
	protected static final String MESSAGE_GENERIC_INSERT = "message.genericInsert";
	/**
	 * MESSAGE_GENERIC_UPDATE
	 */
	protected static final String MESSAGE_GENERIC_UPDATE = "message.genericUpdate";

	/**
	 * Nome dell'entity gestita.
	 **/
	@Autowired(required=false)
	private Resource imagesFolderBean;

	protected int getSearchResultFirstResult(JfFormBean form, String displayTagTableName) {
		int pageSize = getSearchResultPageSize(form, displayTagTableName);
		return getFirstResult(pageSize, displayTagTableName);
	}

	protected int getSearchResultPageSize(JfFormBean form, String displayTagTableName) {
		if (isExportListRequested()) {
			return form.getListExportMaxRows();
		}
		return form.getSearchResultPageSize();
	}

	protected String viewOrEdit(F form) {
		if (form.getPageStatus().isReadOnly()) {
			return "view";
		} else {
			return "edit";
		}
	}

	protected String buildRedirect(F form, String objectKey) {
		if (form.getPageStatus().equals(PageStatus.READ_PAGE_STATUS)) {
			return redirectTo("view/" + objectKey);
		}
		if (form.getPageStatus().equals(PageStatus.EDIT_PAGE_STATUS)) {
			return redirectTo("edit/" + objectKey);
		}
		if (form.getPageStatus().equals(PageStatus.DELETE_PAGE_STATUS)) {
			return redirectTo("delete/" + objectKey);
		}
		if (form.getPageStatus().equals(PageStatus.NEW_PAGE_STATUS)) {
			return redirectTo("new");
		}
		logger.warn("Invalid page status: " + form.getPageStatus() + " given to the buildRedirect method. Returning null...");
		return null;
	}

	@SuppressWarnings("unchecked")
	protected void setEntityToChecked(Entity entity, Collection<? extends Entity> entityList) {
		List<Entity> existingEntityAsList = (List<Entity>)entityList;
		Entity existingEntity = (Entity)existingEntityAsList.get(existingEntityAsList.indexOf(entity));
		existingEntity.setChecked(true);
	}

	/**
	 * manageStandardImages.
	 * @param form
	 * @param theCustomer
	 */
	protected void manageStandardImages(JfFormBean form, EntityGeneric entity) {
		if (imagesFolderBean != null) {
			manageStandardImage(form.getIconImage(), entity, "icon", form.isIconImageDeleteFlag());
			manageStandardImage(form.getBannerImage(), entity, "banner", form.isBannerImageDeleteFlag());
		}
	}

	/**
	 * manageStandardImage.
	 * @param image
	 * @param entity
	 * @param fieldName
	 * @param deleteIfEmpty
	 */
	protected void manageStandardImage(MultipartFile image, EntityGeneric entity, String fieldName, boolean deleteIfEmpty) {
		try {
			File imageFile = new File(imagesFolderBean.getFile(), entity.getImageName(fieldName));
			if (image != null && image.getSize() > 0) {
				IOUtils.copy(image.getInputStream(), new FileOutputStream(imageFile));
			} else if (deleteIfEmpty) {
				FileUtils.deleteQuietly(imageFile);
			}
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

    /**
     * isSocketException.
     * @param e
     * @return
     */
    protected boolean isSocketException(Exception e) {
        return e instanceof SocketException || e.getCause() instanceof SocketException;
    }

    /**
     * 
     * finalizeDownloadToResponse.
     * @param content the blob content to flush
     * @param fileName the file name
     * @param mimeType the mime type
     * @param response the http response to write into
     */
    protected void finalizeDownloadToResponse(Blob content, String fileName, String mimeType, HttpServletResponse response) {
        response.reset();
        response.setContentType(mimeType);
        response.setHeader("Content-disposition",
                "attachment; filename=\"" + fileName + "\"");
        OutputStream sos;
        try {
            sos = response.getOutputStream();
            IOUtils.copy(content.getBinaryStream(), sos);
        } catch (Exception e) {
            if (isSocketException(e)) {
                logger.warn("Il client ha abbandonato il download di un allegato.");
            } else {
                throw new RuntimeBusinessException(e);
            }
        }
    }

    /**
     * hasDifference.
     * @param differences
     * @param string
     * @return
     */
    protected boolean hasDifference(Differences differences, String propertyName) {
        for (Difference diff : differences.differences()) {
            if (diff.propertyName().equals(propertyName)) {
                return true;
            }
        }
        return false;
    }

}
