package com.fp.frontend.controller;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.faces.bean.ManagedProperty;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;

import org.primefaces.context.RequestContext;

import com.fp.common.helper.BeanManager;
import com.fp.dto.AbstractDataTransport;
import com.fp.dto.Request;
import com.fp.dto.Response;
import com.fp.dto.query.DtoQuery;
import com.fp.dto.save.DtoSave;
import com.fp.firmas.rules.common.CertificateUtils;
import com.fp.frontend.controller.alfresco.AlfrescoController;
import com.fp.frontend.controller.security.LoginController;
import com.fp.frontend.helper.CallerHelper;
import com.fp.frontend.helper.MessageHelper;
import com.fp.frontend.utility.MsgGeneral;

/**
 * Clase principal para los controladores
 *
 * @author jvaca
 * @author amerchan Cambio atributos, metodos genericos
 *
 * @param <T>
 */
public abstract class AbstractController<T extends AbstractDataTransport>
        implements Serializable {

    private static final long serialVersionUID = 1154897874514657854L;

    /**
     * Registro a ser usado cuando se crea uno nuevo o para edicion
     */
    protected T record;

    /**
     * Lista de registros a presentar en una tabla de mantenimiento
     */
    protected List<T> lrecord;

    /**
     * Identificador para obtener la lista de registros del mapa de resultados
     * del core
     */
    protected String beanalias;

    /**
     * Tipo de dato de la entidad que maneja el controlador
     */
    protected Class<T> classType;

    // Valores posibles Q(query), P(previous), N(next)
    private String queryType;

    /**
     * Numero de pagina o registro desde el cual se va a ejecutar la consulta.
     */
    protected Integer page = 0;

    /**
     * Numero de registros por pagina a consultar.
     */
    protected Integer recperpage = 10;

    /**
     * Crease utilitaria, para ejecutar una accion en el core, ejecmpo consulta,
     * mantenimiento.
     */
    protected CallerHelper callerhelper;

    /**
     * Lista de registros eliminados associates a una tabla o entitybean.
     */
    private final List<AbstractDataTransport> ldeleted = new ArrayList<>();

    /**
     * Lista de registros nuevos asociados a una tabla o entitybean.
     */
    private final List<AbstractDataTransport> lnew = new ArrayList<>();

    /**
     * Lista de registros modificados asociados a una tabla o entitybean.
     */
    private final List<AbstractDataTransport> lupdated = new ArrayList<>();

    /**
     * Map que contiene los criterios de busqueda de una tabla.
     */
    private Map<String, String> mfilters = new HashMap<String, String>();

    /**
     * Map que contiene campos extras como descripciones.
     */
    private Map<String, Object> mfilelds = new HashMap<String, Object>();

    /**
     * Numero de decimales a manejar en las pantallas.
     */
    private Integer decimal = 2;

    /**
     * Formato de numeros a utilizar en los outputtext.
     */
    private String numberformat = "#,##,##0.000000";

    /**
     * Variable que indica si se ejecuta el autoquery, Ej: en el lov de personas
     * deberia ser falso.
     */
    protected boolean forceautoquery = true;

    /**
     * Variable que indica si la pagina es del BPM y ademas si tiene parametros
     * para pegar en la pagina
     */
    boolean bpmPage = false;

    /**
     * Para indicar que el registro se va a visualizar.
     */
    private boolean showRow;

    /**
     * Para indicar que se va a insertar un nuevo registro.
     */
    private boolean newRow;

    /**
     * Id de la tarea de jbpm a ejecutar.
     */
    protected String tid;

    /**
     * Codigo de estatus de bpm A aprobano, D negado
     */
    protected String bpmDataStatus;

    /**
     * Cometario de ejecucion de una tarea.
     */
    protected String bpmDataComment;
    /**
     * Atributo para la contrasena de la firma
     */
    private String signaturePass;
    /**
     * Atributo para el comentario de la firma
     */
    private String signatureComments;
    /*
	 * Atributo para el mensaje de la solicitud
     */
    private String message;
    /**
     * Referencia al controlador de session
     */
    @ManagedProperty(value = "#{loginController}")
    private LoginController loginController;

    /**
     * Constructor del controlador
     *
     * @param type Tipo de clase
     * @throws Exception
     */
    protected AbstractController(Class<T> type) throws Exception {
        this.classType = type;
        if (this.callerhelper == null) {
            this.callerhelper = new CallerHelper(
                    FacesContext.getCurrentInstance());
        }
    }

    /**
     * Metodo que verifica si la pagina es de autoconsulta y si debe pegar
     * valores del BPM
     */
    public void startQuery() {
        try {
            this.bpmPage = this.pasteBpmScreenParameters();
            if (!this.bpmPage && this.forceautoquery
                    && this.callerhelper.isautoquery()) {
                this.query();
            }
        } catch (Exception e) {
            MessageHelper.setMessageError(e);
        }
    }

    /**
     * Metodo que verifica si la pagina es de BPM, pega los valores en los
     * filtros y realiza la consulta de la p&acute;gina
     *
     * @return
     * @throws Exception
     */
    public boolean pasteBpmScreenParameters() throws Exception {
        this.getLoginController().setRenderBpmData(false);
        String bpmParamsJson = this.getRequestParameter("bpmParams");
        if (bpmParamsJson == null) {
            return false;
        }
        bpmParamsJson = bpmParamsJson.substring(1, bpmParamsJson.length() - 1);
        if (!bpmParamsJson.isEmpty()) {
            String[] bpmParams = bpmParamsJson.split(",");
            for (String item : bpmParams) {
                String[] param = item.split("=");
                this.manageBpmParameter(param);
            }
            return true;
        }
        return false;
    }

    private void manageBpmParameter(String[] param) throws Exception {
        String key = param[0].trim();
        String value = param[1];
        if (key.equals("null")) {
            return;
        }
        if (key.equals("TID")) {
            tid = param[1];
            return;
        }
        if (key.equals("KIND")) {
            if (value.compareTo("OK_NO") == 0) {
                this.getLoginController().setRenderBpmData(true);
            } else if (value.compareTo("OK") == 0) {
                this.bpmDataStatus = "A";
            }
            return;
        }

        this.mfilters.put(param[0].trim(), param[1]);
    }

    /**
     * Funcion que verifica que exista al menos un filtro de busqueda
     *
     * @return boolean
     */
    public boolean existAtLeastOneFilterValue() {
        Set<String> keys = this.mfilters.keySet();
        for (String key : keys) {
            String val = this.mfilters.get(key);
            if (!key.equals("pk.dateto")) {
                if ((val != null) && !val.isEmpty()) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * Metodo que agrega la fecha hasta (dateto) al mapa de filtros
     *
     * @throws Exception
     */
    public void addFilterDateto() throws Exception {
        this.mfilters.put("pk.dateto", "2999-12-31");
    }

    /**
     * Crea una instancia de una entidad T y marca el registro como nuevo.
     *
     * @throws Exception
     */
    @SuppressWarnings("unchecked")
    public void create() throws Exception {
        this.record = (T) Class.forName(this.classType.getName()).newInstance();
        BeanManager.setBeanAttributeValue(this.record, "isnew", true);
        try {
            BeanManager.setBeanAttributeValue(this.record, "pk",
                    Class.forName(this.classType.getName() + "Key")
                    .newInstance());
        } catch (Exception e) {
        }
    }

    /**
     * Metodo a ser sobreescrito en los controladores. En este m&eacute;todo a
     * sobrescribir se debe colocar la l&oacute;gica para invocar al core y
     * guardar los registros.
     */
    public void save() {
    }

    ;

	/**
	 * Adiciona datos de un registro a ser eliminado de la base de datos.
	 * 
	 * @param bean
	 *            Objeto a eliminar.
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
    public void remove() throws Exception {
        if (this.record == null) {
            return;
        }
        this.removefromlista(this.lrecord, this.record);
        if (!this.record.isnew) {
            this.ldeleted.add(this.record);
        }
        this.removefromlista((List<T>) this.lnew, this.record);
        this.removefromlista((List<T>) this.lupdated, this.record);
    }

    /**
     * Elimina un registro de la lista comparando el rowkey del registro.
     *
     * @param ldata Lista de registros a buscar y remover un objeto.
     * @param abstractbean Bean a remover de la lista.
     * @throws Exception
     */
    private void removefromlista(List<T> ldata, T abstractbean)
            throws Exception {
        int size = ldata.size();
        for (int i = 0; i < size; i++) {
            if (ldata.get(i).rowkey() == abstractbean.rowkey()) {
                ldata.remove(i);
                break;
            }
        }
    }

    /**
     * Adiciona un registro a la lista de registros que se va a insertar en la
     * base de datos.
     *
     * @param bean Abjeto a insertar.
     * @throws Exception
     */
    protected void addNew(AbstractDataTransport bean) throws Exception {
        if (!this.lnew.contains(bean)) {
            this.lnew.add(bean);
        }
    }

    /**
     * Adiciona un registro a la lista de registros que se va a actualizar en la
     * base de datos.
     *
     * @param bean
     * @throws Exception
     */
    protected void addUpdated(AbstractDataTransport bean) throws Exception {
        if (!this.lupdated.contains(bean)) {
            this.lupdated.add(bean);
        }
    }

    /**
     * Actualiza datos de registros nuevos o modifiados, si el registro es nuevo
     * se adiciona a lista de nuevos, si es actualizado se adiciona a la lista
     * de actualizados.
     *
     * @throws Exception
     */
    @SuppressWarnings("unchecked")
    public void update() throws Exception {
        AbstractDataTransport bean = this.record;
        if (bean == null) {
            return;
        }
        if (bean.isnew) {
            if (!this.exist(bean, (List<AbstractDataTransport>) this.lrecord)) {
                this.addNew(bean);
                this.lrecord.add((T) bean);
            }
        } else {
            this.addUpdated(bean);
        }
    }

    /**
     *
     * @param bean
     * @param ldata
     * @return
     * @throws Exception
     */
    private boolean exist(AbstractDataTransport bean,
            List<AbstractDataTransport> ldata) throws Exception {
        for (AbstractDataTransport obj : ldata) {
            if (obj.rowkey() == bean.rowkey()) {
                return true;
            }
        }
        return false;
    }

    /**
     * Metodo que tiene que sobreescribir las clases que trabajen con el core.
     * En este metodo se debe implementar la consulta de las las tablas que se
     * requieran
     */
    protected abstract void querydatabase();

    /**
     * Ejecuta consulta de datos en la base para la tabla.
     *
     * @throws Exception
     */
    public void query() throws Exception {
        this.queryType = "Q";
        if (this.validateQuery()) {
            this.page = 0;
            this.querydatabase();
        }
    }

    private boolean validateQuery() {
        if (this.lupdated.isEmpty() && this.lnew.isEmpty()
                && this.ldeleted.isEmpty()) {
            return true;
        } else {
            RequestContext.getCurrentInstance().execute(
                    "PF('queryConfirmDialog').show()");
        }
        return false;
    }

    public void forcedQuery() throws Exception {
        this.lupdated.clear();
        this.lnew.clear();
        this.ldeleted.clear();
        this.lrecord.clear();
        if (this.queryType == null) {
            this.queryType = "default"; // valor dummy para que no de null
            // pointer exception en el switch
        }
        switch (this.queryType) {
            case "P":
                this.previous();
                break;
            case "N":
                this.next();
                break;
            default:
                this.query();
                break;
        }
    }

    /**
     * Ejecuta consulta de la sigueinte pagina.
     *
     * @throws Exception
     */
    public void next() throws Exception {
        this.queryType = "N";
        if (this.validateQuery()) {
            this.page = ((this.page > 0) && (this.lrecord.size() == 0)) ? this.page
                    : this.page + this.recperpage;
            this.querydatabase();
        }
    }

    /**
     * Ejecuta consulta de la pagina anterior.
     *
     * @throws Exception
     */
    public void previous() throws Exception {
        this.queryType = "P";
        if (this.validateQuery()) {
            this.page = this.page - this.recperpage;
            this.page = this.page < 0 ? 0 : this.page;
            this.querydatabase();
        }
    }

    /**
     * Entrega el dto de save, con los registros a insertar, eliminar, modificar
     * para una tabla.
     *
     * @param beanname Nombre del bean.
     * @return
     * @throws Exception
     */
    public DtoSave getDtoSave() throws Exception {
        DtoSave dto = new DtoSave();
        dto.setBeanname(this.classType.getName());
        for (AbstractDataTransport bean : this.lnew) {
            // para que siempre fije el valor del rowkey.
            bean.rowkey();
            bean.getModifiedData().put("isnew", "1");
        }
        dto.addNew(this.lnew);
        dto.addDeleted(this.ldeleted);
        // Quita el bean original de los modificados y marca el bean para
        // actualizacion.
        for (AbstractDataTransport mod : this.lupdated) {
            if (mod.get("ORIGINALBEAN") == null) {
                continue;
            }
            mod.isupdated = false;
            AbstractDataTransport boriginal = (AbstractDataTransport) mod
                    .get("ORIGINALBEAN");
            HashMap<String, Object> modificados = BeanManager.getModified(
                    boriginal, mod);
            if (!modificados.isEmpty()) {
                mod.isupdated = true;
                // solo si existe cambios se adiciona a la lista de modificado.
                dto.getLupdated().add(modificados);
            }
        }
        // adiciona campos sueltos.
        return dto;
    }

    /**
     * Entrega el dto de save, con los registros a insertar, eliminar, modificar
     * para una tabla.
     *
     * @param isForm V si es formulario.
     * @return dto
     * @throws Exception
     */
    public DtoSave getDtoSave(boolean isForm) throws Exception {
        DtoSave dto = this.getDtoSave();
        dto.setIsForm(isForm);
        return dto;
    }

    /**
     * Metodo que retorna un objeto de tipo DtoQuery que sirve para realizar
     * consultas
     *
     * @param isMultirecord
     * @return
     * @throws Exception
     */
    public DtoQuery getDtoQuery(boolean isMultirecord) throws Exception {
        DtoQuery dto = new DtoQuery(this.classType.getName(), this.page,
                this.recperpage, isMultirecord, this.getMfilters());
        return dto;
    }

    /**
     * Metodo usado despues de grabar exitosamente los datos en la base
     *
     * @param response
     * @throws Exception
     */
    public void postCommit(Response response) throws Exception {
        this.postCommitGeneric(response, this.beanalias);
    }

    /**
     * Si el commit se relaliza con exito, suma 1 al campo optlock para poder
     * hacer un nuevo mantenimiento del registro. Completa las claves primarias
     * de los registros nuevos
     *
     * @throws Exception
     */
    @SuppressWarnings("unchecked")
    protected void postCommitGeneric(Response response, String alias)
            throws Exception {
        for (AbstractDataTransport bean : this.lupdated) {
            this.postcommitbean(bean);
        }
        // marca el registro como no nuevo va antes de actualizar pk.
        for (AbstractDataTransport bean : this.lnew) {
            bean.isnew = false;
            this.postcommitbean(bean);
        }
        // Actualiza pk de los registros nuevos.
        if (response.get(alias) != null) {
            List<Map<String, Object>> lresp = (List<Map<String, Object>>) response
                    .get(alias);
            for (Map<String, Object> m : lresp) {
                this.updatePkOnRecords(m);
            }
        }
        // Encera registros nuevos.
        this.lnew.clear();
        this.ldeleted.clear();
        this.lupdated.clear();
    }

    /**
     * Metodo que maneja la version de registros de una entidad
     *
     * @param bean
     * @throws Exception
     */
    private void postcommitbean(AbstractDataTransport bean) throws Exception {
        Integer optlock = BeanManager.getRecordversion(bean);
        if (optlock != null) {
            if (bean.isupdated) {
                BeanManager.setBeanAttributeValue(bean, "recordversion",
                        optlock + 1);
            } else {
                BeanManager.setBeanAttributeValue(bean, "recordversion",
                        optlock);
            }
        }
        this.postQuery(bean);
    }

    /**
     * Metodo que actualiza los pk's de los registros nuevos
     *
     * @param m
     * @throws Exception
     */
    private void updatePkOnRecords(Map<String, Object> m) throws Exception {
        String id = m.get("rowkey").toString();
        AbstractDataTransport bean = this.getNewBeanById(Integer.valueOf(id));
        if (bean == null) {
            return;
        }
        Set<String> s = m.keySet();
        for (String key : s) {
            if (key.equals("rowkey")) {
                continue;
            }
            BeanManager.setBeanAttributeValue(bean, key.replace("pk_", "pk."),
                    m.get(key));
        }
    }

    /**
     * Metodo que obtiene una entidad de la lista de registros nuevos por id
     *
     * @param id
     * @return
     * @throws Exception
     */
    private AbstractDataTransport getNewBeanById(Integer id) throws Exception {
        AbstractDataTransport bean = null;
        for (AbstractDataTransport obj : this.lnew) {
            if (obj.rowkey() == id) {
                bean = obj;
            }
        }
        return bean;
    }

    /**
     * Metodo que clona una lista de entidades
     *
     * @param lbeans
     * @throws Exception
     */
    @SuppressWarnings("rawtypes")
    public void postQuery(List lbeans) throws Exception {
        for (Object bean : lbeans) {
            if (bean instanceof AbstractDataTransport) {
                this.postQuery((AbstractDataTransport) bean);
            }
        }
    }

    /**
     * Metodo que clona una entidad y lo coloca en un mapa dentro de la misma
     * para poder dar mantenimiento
     *
     * @param bean
     * @throws Exception
     */
    public void postQuery(AbstractDataTransport bean) throws Exception {
        if (bean == null) {
            return;
        }
        bean.isupdated = false; // Se cambia a true cuando arma el mensaje que
        // se envia al core.
        AbstractDataTransport boriginal = (AbstractDataTransport) bean
                .cloneMe();
        bean.put("ORIGINALBEAN", boriginal);
    }

    /**
     * Metodo que limplia las listas de objetos
     */
    public void clearAll() {
        this.mfilelds.clear();
        this.mfilters.clear();
        this.lupdated.clear();
        this.lnew.clear();
        this.ldeleted.clear();
        if (lrecord != null) {
            this.lrecord.clear();
        }
    }

    /**
     * Metodo que obtiene el valor de un parametro enviado desde la p&acute;gina
     *
     * @param key
     * @return
     * @throws Exception
     */
    public String getRequestParameter(String key) throws Exception {
        return FacesContext.getCurrentInstance().getExternalContext()
                .getRequestParameterMap().get(key);
    }

    /**
     * Metodo que retorna un mapa de parametros enviados desde una pagina
     *
     * @return
     * @throws Exception
     */
    public Map<String, String> getRequestParameterMap() throws Exception {
        return FacesContext.getCurrentInstance().getExternalContext()
                .getRequestParameterMap();
    }

    /**
     * Metodo usado para descargar archivos
     *
     * @param fileByte
     * @param contentType
     * @param extension
     */
    public void downloadFile(byte[] fileByte, String contentType, String extension) {
        try {
            int read = 0;
            byte[] bytes = new byte[1024];

            InputStream input = new ByteArrayInputStream(fileByte);
            FacesContext context = FacesContext.getCurrentInstance();
            ExternalContext external = context.getExternalContext();
            HttpServletResponse response = (HttpServletResponse) external
                    .getResponse();
            response.setContentType(contentType);
            response.setHeader("Content-Disposition",
                    "attachment; filename=fileDownload." + extension);
            ServletOutputStream out = response.getOutputStream();

            while ((read = input.read(bytes)) != -1) {
                out.write(bytes, 0, read);
            }
            out.flush();
            out.close();
            context.responseComplete();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    /**
     * Entrega el valor de: recperpage
     *
     * @return Integer
     */
    public Integer getRecperpage() {
        return this.recperpage;
    }

    /**
     * Fija el valor de: recperpage
     *
     * @param Valor a fijar en el atributo.
     */
    public void setRecperpage(Integer recperpage) {
        this.recperpage = recperpage;
    }

    /**
     * Entrega el valor de: callerhelper
     *
     * @return CallerHelper
     */
    public CallerHelper getCallerhelper() {
        return this.callerhelper;
    }

    /**
     * Fija el valor de: callerhelper
     *
     * @param Valor a fijar en el atributo.
     */
    public void setCallerhelper(CallerHelper callerhelper) {
        this.callerhelper = callerhelper;
    }

    /**
     * Entrega el valor de: mfilters
     *
     * @return Map<String,String>
     */
    public Map<String, String> getMfilters() {
        return this.mfilters;
    }

    /**
     * Fija el valor de: mfilters
     *
     * @param Valor a fijar en el atributo.
     */
    public void setMfilters(Map<String, String> mfilters) {
        this.mfilters = mfilters;
    }

    public void addFilter(String field, String value) {
        this.mfilters.put(field, value);
    }

    /**
     * Entrega el valor de: mfilelds
     *
     * @return Map<String,Object>
     */
    public Map<String, Object> getMfilelds() {
        return this.mfilelds;
    }

    /**
     * Fija el valor de: mfilelds
     *
     * @param Valor a fijar en el atributo.
     */
    public void setMfilelds(Map<String, Object> mfilelds) {
        this.mfilelds = mfilelds;
    }

    /**
     * Entrega el valor de: decimal
     *
     * @return Integer
     */
    public Integer getDecimal() {
        return this.decimal;
    }

    /**
     * Fija el valor de: decimal
     *
     * @param Valor a fijar en el atributo.
     */
    public void setDecimal(Integer decimal) {
        this.decimal = decimal;
    }

    /**
     * Entrega el valor de: numberformat
     *
     * @return String
     */
    public String getNumberformat() {
        return this.numberformat;
    }

    /**
     * Fija el valor de: numberformat
     *
     * @param Valor a fijar en el atributo.
     */
    public void setNumberformat(String numberformat) {
        this.numberformat = numberformat;
    }

    /**
     * Entrega el valor de: forceautoquery
     *
     * @return boolean
     */
    public boolean isForceautoquery() {
        return this.forceautoquery;
    }

    /**
     * Fija el valor de: forceautoquery
     *
     * @param Valor a fijar en el atributo.
     */
    public void setForceautoquery(boolean forceautoquery) {
        this.forceautoquery = forceautoquery;
    }

    /**
     * Entrega el valor de: bpmPage
     *
     * @return boolean
     */
    public boolean isBpmPage() {
        return this.bpmPage;
    }

    /**
     * Fija el valor de: bpmPage
     *
     * @param bpmPage Valor a fijar en el atributo
     */
    public void setBpmPage(boolean bpmPage) {
        this.bpmPage = bpmPage;
    }

    /**
     * Entrega el valor de: record
     *
     * @return T
     */
    public T getRecord() {
        return this.record;
    }

    /**
     * Fija el valor de: record
     *
     * @param Valor a fijar en el atributo
     */
    public void setRecord(T record) {
        this.record = record;
    }

    /**
     * Entrega el valor de: lrecord
     *
     * @return List<T>
     */
    public List<T> getLrecord() {
        return this.lrecord;
    }

    /**
     * Fija el valor de: lrecord
     *
     * @param Valor a fijar en el atributo
     */
    public void setLrecord(List<T> lrecord) {
        this.lrecord = lrecord;
    }

    /**
     * Entrega el valor de: beanalias
     *
     * @return String
     */
    public String getBeanalias() {
        return this.beanalias;
    }

    /**
     * Entrega el valor de: beanalias
     *
     * @return String
     */
    public void setBeanalias(String beanalias) {
        this.beanalias = beanalias;
    }

    /**
     * Entrega el valor de: showRow
     *
     * @return boolean
     */
    public boolean isShowRow() {
        return this.showRow;
    }

    /**
     * Fija el valor de: showRow
     *
     * @param Valor a fijar en el atributo.
     */
    public void setShowRow(boolean showRow) {
        this.showRow = showRow;
    }

    public boolean isNewRow() {
        return this.newRow;
    }

    public void setNewRow(boolean newRow) {
        this.newRow = newRow;
    }

    public void addField(String field, Object value) {
        this.mfilelds.put(field, value);
    }

    public Object getFieldvalue(String field) {
        return this.mfilelds.get(field);
    }

    /**
     * Entrega el valor de: loginController
     *
     * @return LoginController
     */
    public LoginController getLoginController() {
        return this.loginController;
    }

    /**
     * Fija el valor de: loginController
     *
     * @param Valor a fijar en el atributo.
     */
    public void setLoginController(LoginController loginController) {
        this.loginController = loginController;
    }

    /**
     * Entrega el valor de: bpmDataStatus
     *
     * @return String
     */
    public String getBpmDataStatus() {
        return bpmDataStatus;
    }

    /**
     * Fija el valor de: bpmDataStatus
     *
     * @param bpmDataStatus Valor a fijar en el atributo.
     */
    public void setBpmDataStatus(String bpmDataStatus) {
        this.bpmDataStatus = bpmDataStatus;
    }

    /**
     * Entrega el valor de: bpmDataComment
     *
     * @return String
     */
    public String getBpmDataComment() {
        return bpmDataComment;
    }

    /**
     * Fija el valor de: bpmDataComment
     *
     * @param bpmDataComment Valor a fijar en el atributo.
     */
    public void setBpmDataComment(String bpmDataComment) {
        this.bpmDataComment = bpmDataComment;
    }

    /**
     * Metodo que se encarga de encerar datos de consultas, mantenimeintos y
     * datos adicionales de un request, se utliza para iniciar flujos de un bpm.
     *
     * @param request Objeto a limpiar informacion.
     * @throws Exception
     */
    public void cleanRequest(Request request) throws Exception {
        request.modifiedData().clear();
        if (request.getSaveTables() != null) {
            request.getSaveTables().clear();
        }
        if (request.getQueryTables() != null) {
            request.getQueryTables().clear();
        }

    }

    /**
     * Entrega el password del firmante
     *
     * @return signaturePass
     */
    public String getSignaturePass() {
        return signaturePass;
    }

    /**
     * Fija el password del firmante
     *
     * @param signaturePass
     */
    public void setSignaturePass(String signaturePass) {
        this.signaturePass = signaturePass;
    }

    /**
     * Entrega el motivo del firmado
     *
     * @return
     */
    public String getSignatureComments() {
        return signatureComments;
    }

    /**
     * Fija el motivo del firmado
     *
     * @param signatureComments
     */
    public void setSignatureComments(String signatureComments) {
        this.signatureComments = signatureComments;
    }

    /**
     * Entrega el valor del mensaje
     *
     * @return message
     */
    public String getMessage() {
        return message;
    }

    /**
     * Fija el valor del mensaje
     *
     * @param message
     */
    public void setMessage(String message) {
        this.message = message;
    }

    /**
     * Metodo que comprueba las firmas de un documento
     *
     * @param codSol
     * @param codEmp
     * @param nomTipo
     */
    public boolean checkSignature(String codSol, String codEmp, String nomTipo) {
        int count = 0;
        InputStream originalDocTemplate = AlfrescoController.getDocumentByCode(codSol, nomTipo, this.getLoginController().getRequest().getCompany().toString());
        if (originalDocTemplate == null) {
            return false;
        }
        List<String> signs = CertificateUtils.obtainNameSigns(originalDocTemplate);
        if (signs != null && signs.size() > 0) {
            for (String sign : signs) {
                if (sign.equalsIgnoreCase(codEmp)) {
                    ++count;
                }
            }
            if (count == 0) {
                return true;
            }
        }
        return false;
    }

    /**
     * Metodo para el firmado electronico de documentos
     *
     * @param codSol - Codigo de la solicitud
     * @param codEmp - Codigo del empleado
     * @param nomTipo - Tipo de documento (solicitud, informe)
     */
    /*public void signatureDocument(String codSol, String codEmp, String nomTipo,
			int company, String usuario) {
		
		String pathStoreCertificates= ParametersController.find(EnumParametros.PATH_CERTIFICADO_BCE.getCodigo(),(company+"")).getTextvalue();
		String nomCert = CertificadosController.find(usuario).getPk().getIdcertificado();
		
		
		String path =pathStoreCertificates+"/"+nomCert;

		InputStream originalDocTemplate = AlfrescoController.getDocumentByCode(codSol, nomTipo, this.getLoginController().getRequest().getCompany().toString());

		List<String> signs = CertificateUtils
				.obtainNameSigns(originalDocTemplate);
		float llx = 50;
		float lly = 210;
		float urx = 305;
		float ury = 277;
		Rectangle rectangle = new Rectangle(llx, lly, urx, ury);

		if (signs.size() > 0) {
			for (String sign : signs) {
				if (sign.equalsIgnoreCase(codEmp)) {
					MessageHelper.setMessageWarn("msg_firmaExistente");
					return;
				}
			}

		}

		InputStream originalDoc = AlfrescoController.getDocumentByCode(codSol, nomTipo, this.getLoginController().getRequest().getCompany().toString());

		byte[] signedDoc = null;
		try {
			signedDoc = CertificateUtils.sign(originalDoc, usuario,
					this.signaturePass, company, this.signatureComments,
					this.signatureComments, rectangle, 1, codEmp,
					path);

			if (signedDoc != null) {
				InputStream uploadDoc = new ByteArrayInputStream(signedDoc);

				AlfrescoController.uploadSignedDocument(uploadDoc, codSol,
						nomTipo);

			}
			originalDocTemplate.close();
			originalDoc.close();
		} catch (Exception e1) {

			e1.printStackTrace();
		}

	}*/
    /**
     * Metodo para el firmado electronico de documentos; Aprobaciones
     *
     * @param codSol - Codigo de la solicitud
     * @param codEmp - Codigo del empleado
     * @param nomTipo - Tipo de documento (solicitud, informe)
     */
    /*public void signatureDocumentAPR(String codSol, String codEmp, String nomTipo,
			int company, String usuario) {

		InputStream originalDocTemplate = AlfrescoController.getDocumentByCode(codSol, nomTipo, this.getLoginController().getRequest().getCompany().toString());
		TfirmCertificado certificado = CertificadosController.find(usuario);
		if(certificado == null){
			MessageHelper.setMessageError("msg_certificateNotParam");
			return;
		}
		String nomCert = certificado.getPk().getIdcertificado();
		String pathStoreCertificates= ParametersController.find(EnumParametros.PATH_CERTIFICADO_BCE.getCodigo(),(company+"")).getTextvalue();
		
		String path =pathStoreCertificates+"/"+nomCert;

		List<String> signs = CertificateUtils
				.obtainNameSigns(originalDocTemplate);
		float llx = 50;
		float lly = 210;
		float urx = 305;
		float ury = 277;
		Rectangle rectangle = new Rectangle(llx, lly, urx, ury);

		if (signs.size() > 0) {
			for (String sign : signs) {
				if (sign.equalsIgnoreCase(codEmp)) {
					MessageHelper.setMessageWarn("msg_firmaExistente");
					return;
				}
			}
			
			InputStream originalDoc = AlfrescoController.getDocumentByCode(codSol, nomTipo, this.getLoginController().getRequest().getCompany().toString());

			byte[] signedDoc = null;
			try {
				signedDoc = CertificateUtils.sign(originalDoc, usuario,
						this.signaturePass, company, this.signatureComments,
						this.signatureComments, rectangle, 1, codEmp,
						path);

				if (signedDoc != null) {
					InputStream uploadDoc = new ByteArrayInputStream(signedDoc);

					AlfrescoController.uploadSignedDocument(uploadDoc, codSol,
							nomTipo);

				}
				originalDocTemplate.close();
				originalDoc.close();
			} catch (Exception e1) {

				e1.printStackTrace();
			}

		}
		else
		{
			MessageHelper.setMessageWarn("msg_sinFirma");
			return;	
		}

	}*/
    /**
     * Metodo que comprueba las firmas de un documento
     *
     * @param codSol
     * @param codEmp
     * @param nomTipo
     */
    public void signature(String codSol, String nomTipo, String tipo) {
        int count = 0;
        InputStream originalDocTemplate = AlfrescoController.getDocumentByCode(codSol, nomTipo, this.getLoginController().getRequest().getCompany().toString());
        if (originalDocTemplate == null) {
            this.message = MsgGeneral.getProperty("msg_solicitudeNotSign");
            return;
        }
        List<String> signs = CertificateUtils.obtainNameSigns(originalDocTemplate);
        if (signs != null && !signs.isEmpty()) {
            this.message = tipo.equals("S") ? MsgGeneral.getProperty("msg_solicitudeSign") : MsgGeneral.getProperty("msg_informSign");
        }
        this.message = tipo.equals("S") ? MsgGeneral.getProperty("msg_solicitudeNotSign") : MsgGeneral.getProperty("msg_informNotSign");
    }

    /**
     * M&eacute;todo para el inicio de flujo
     */
    public void iniciarFlujo() {
        try {

            Request request = callerhelper.getRequest();
            this.cleanRequest(request);

            request.modifiedData().put("csolicitud", "123456");
            request.modifiedData().put("isnew", "Y");

            Response resp = callerhelper.executeSave(request);
            if (resp.getResponseCode().compareTo(Response.RESPONSE_OK) == 0) {
                MessageHelper.setMessageInfo(resp);
            } else {
                MessageHelper.setMessageError(resp);
            }
        } catch (Throwable e) {
            MessageHelper.setMessageError(e);
        }
    }

    /**
     * M&eacute;todo para el inicio de flujo
     */
    public void completarTarea() {
        try {
            Request request = callerhelper.getRequest();
            // limpiar request para finalizar la tarea.
            this.cleanRequest(request);
            request.modifiedData().put("TID", tid);
            request.modifiedData().put("BPMStatus", bpmDataStatus == null ? "A" : bpmDataStatus); // A,D,R
            request.modifiedData().put("BPMObs", bpmDataComment);
            //request.modifiedData().put("validate", "Y");
            Response resp = callerhelper.executeSave(request);
            if (resp.getResponseCode().compareTo(Response.RESPONSE_OK) == 0) {
                MessageHelper.setMessageInfo(resp);
                RequestContext.getCurrentInstance().execute("Maia.refreshusertasks()");
            } else {
                MessageHelper.setMessageError(resp);
            }
        } catch (Throwable e) {
            MessageHelper.setMessageError(e);
        }
    }
}
