/*
 *
 */
package com.fp.bpmlib.task.client;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.kie.api.task.model.Comment;
import org.kie.api.task.model.Status;
import org.kie.api.task.model.Task;
import org.kie.api.task.model.TaskData;
import org.mvel2.PropertyAccessException;

import com.fp.base.persistence.util.helper.MessageManager;
import com.fp.bpm.query.QueryProcessor;
import com.fp.bpmlib.BPMException;
import com.fp.bpmlib.ProcessUtil;
import com.fp.common.messages.ELEval;
import com.fp.dto.Request;
import com.fp.dto.Response;
import com.fp.dto.data.TransactionDTO;
import com.fp.dto.query.QueryRequest;
import com.fp.persistence.commondb.PersistenceHelper;
import com.fp.persistence.commondb.data.ThreadFacade;
import com.fp.persistence.pbpm.gene.TbpmFieldTransactionMaping;
import com.fp.persistence.pbpm.gene.TbpmFieldTransactionOrigin;
import com.fp.persistence.pbpm.gene.TbpmRules;
import com.fp.persistence.pbpm.gene.TbpmRulesKey;
import com.fp.persistence.pgeneral.trans.TgeneTransaction;
import com.fp.persistence.pgeneral.trans.TgeneTransactionKey;
import com.fp.simple.dto.Responses;
import com.fp.simple.dto.TaskInfo;

// TODO: Auto-generated Javadoc
/**
 * Class TaskUtil encargada del manejo de Tareas.
 * 
 * @author Jvaca
 */
public class TaskUtil {

    /** Constante BPM_STATUS. */
    public static final String BPM_STATUS = "BPMStatus";

    /** Constante BPM_DATA. */
    public static final String BPM_DATA = "BPMData";

    /** Constante BPM_OBS. */
    public static final String BPM_OBS = "BPMObs";

    /** Constante BPM_TID. */
    public static final String BPM_TID = "TID";

    /** El valor de task. */
    private Task task;

    /** El valor de task data. */
    private TaskData taskData;

    /** El valor de htc. */
    private final HumanTaskClient htc;

    /** El valor de tid. */
    private long tid;

    /** El valor de taskname. */
    private long taskname;

    /** El valor de input. */
    private Map<String, Object> input = null;

    /**
     * Crea una nueva instancia de TaskUtil.
     * 
     * @throws Exception la exception
     */
    private TaskUtil() throws Exception {
        htc = new HumanTaskClient();
    }

    /**
     * Crea una nueva instancia de TaskUtil.
     * 
     * @throws Exception la exception
     */
    public TaskUtil(Task task, HumanTaskClient htc) throws Exception {
        this.htc = htc;
        this.task = task;
        this.tid = this.task.getId();
        taskData = task.getTaskData();
    }

    /**
     * Entrega el valor de task.
     * 
     * @return Valor de task
     */
    public Task getTask() {
        return task;
    }

    /**
     * Entrega el valor de taskData.
     * 
     * @return Valor de taskData
     */
    public TaskData getTaskData() {
        return taskData;
    }

    /**
     * Entrega el valor de tid.
     * 
     * @return Valor de tid
     */
    public long getTid() {
        return tid;
    }

    /**
     * Entrega el valor de: taskname
     * 
     * @return long
     */
    public long getTaskname() {
        return taskname;
    }

    /**
     * Fija el valor de: taskname
     * 
     * @param taskname
     */
    public void setTaskname(long taskname) {
        this.taskname = taskname;
    }

    /**
     * Crea una nueva instancia de task útil.
     * 
     * @param pTId the t id
     * @throws Exception la exception
     */
    public TaskUtil(long pTId) throws Exception {
        this();
        tid = pTId;
        task = htc.getTaskById(pTId);
        taskData = task.getTaskData();
    }

    /**
     * Close.
     * 
     * @throws Exception la exception
     */
    public void close() throws Exception {
        htc.close();
    }

    /**
     * Obtiene el valor de task info.
     * 
     * @return Valor de task info
     * @throws Exception la exception
     */
    public TaskInfo getTaskInfo() throws Exception {
        Map<String, Object> m = this.getActualData();
        Object obj = m.get(ProcessUtil.TASK_METADATA);
        return (TaskInfo) obj;
    }

    /**
     * Obtiene el valor del juego de variables de MAIA.
     * 
     * @return Valor de las variables MAIA
     * @throws Exception la exception
     */
    @SuppressWarnings("unchecked")
    public Map<String, Object> getMaia() throws Exception {
        Map<String, Object> m = this.getActualData();
        return (Map<String, Object>) m.get(ProcessUtil.DATA);
    }

    /**
     * Obtiene el Request.
     * 
     * @return Request
     * @throws Exception la exception
     */
    public Request getRequest() throws Exception {
        Map<String, Object> maia = this.getMaia();
        return (Request) maia.get(ProcessUtil.REQUEST);
    }

    /**
     * Obtiene el valor de actual data.
     * 
     * @return Valor de actual data
     * @throws Exception la exception
     */
    public Map<String, Object> getActualData() throws Exception {
        if (input != null) {
            return input;
        }
        if (taskData.getStatus() == Status.Completed) {
            input = htc.getOutputData(task);
        } else {
            input = htc.getInputData(task);
        }
        return input;
    }

    /**
     * Obtiene el valor de status.
     * 
     * @return Valor de status
     */
    public Status getStatus() {
        return taskData.getStatus();
    }

    /**
     * Obtiene el valor de work item id.
     * 
     * @return Valor de work item id
     */
    public long getWorkItemId() {
        return taskData.getWorkItemId();
    }

    /**
     * Obtiene el valor de comments.
     * 
     * @return Valor de comments
     * @throws Exception la exception
     */
    public List<Comment> getComments() throws Exception {
        return taskData.getComments();
    }

    /**
     * Complete.
     * 
     * @param pJournalId the journal id
     * @param data the data
     * @param pResponse the response
     * @param pObs the obs
     * @throws Exception la exception
     */
    public String complete(String pJournalId, Map<String, Object> data, Responses pResponse, String pObs) throws Exception {
        try {
            return htc.completeTask(tid, pJournalId, data, pResponse.getCode(), pObs.toUpperCase());
        } catch (Exception e) {
            throw new BPMException("BPM502", String.format("NO SE HA PODIDO COMPLETAR LA TAREA {0}: %s", e.toString()), e, tid);
        }
    }

    /**
     * Start.
     * 
     * @throws Exception la exception
     */
    public void start() throws Exception {
        try {
            htc.startTask(tid);
        } catch (Exception e) {
            throw new BPMException("BPM501",String.format("NO SE HA PODIDO INICIAR LA TAREA {0} %s ", e.toString()) , e, tid);
        }
    }

    /**
     * Claim.
     * 
     * @param pUser the user
     * @throws Exception la exception
     */
    public void claim(String pUser) throws Exception {
        try {
            htc.claimTask(tid, pUser);
        } catch (Exception e) {
            throw new BPMException("BPM500", String.format("NO SE HA PODIDO RESERVAR LA TAREA {0} PARA EL USUARIO {1} %s", e.toString()), e, tid, pUser);
        }
    }

    /**
     * Comment.
     * 
     * @param pUser the user
     * @param pComment the comment
     * @throws Exception la exception
     */
    public void comment(String pUser, String pComment) throws Exception {
        try {
            htc.addComment(tid, pUser, pComment.toUpperCase());
        } catch (Exception e) {
            throw new BPMException("BPM504", "NO SE HA PODIDO MODIFICAR LA TAREA {0} ", e, tid);
        }
    }

    /**
     * Reasign.
     * 
     * @param pUser the user
     * @throws Exception la exception
     */
    public void delegate(String pUser) throws Exception {
        try {
            htc.delegateTask(tid, taskData.getActualOwner().getId(), pUser);
        } catch (Exception e) {
            throw new BPMException("BPM504", "NO SE HA PODIDO MODIFICAR LA TAREA {0} ", e, tid);
        }
    }

    /**
     * Obtiene el valor de subject.
     * 
     * @return Valor de subject
     * @throws Exception la exception
     */
    public String getSubject() throws Exception {
        String subject = "";
        Request req = this.getRequest();
        if (req.getSubject() == null) {
            req.setSubject(MessageManager.getMessage(req));
        }
        subject = req.getSubject();
        return subject;
    }

    /**
     * Obtiene la descripcion de la regla.
     * 
     * @return descripcion de la regla
     * @throws Exception la exception
     */
    public String getRuleDescription() throws Exception {
        Request req = this.getRequest();
        TaskInfo info = this.getTaskInfo();
        TbpmRulesKey rk = new TbpmRulesKey(info.getRuleCode(), req.getCompany());
        TbpmRules r = TbpmRules.find(PersistenceHelper.getEntityManager(), rk);
        if (r != null) {
            return " " + r.getDescription();
        }
        return "";
    }

    /**
     * Obtiene el valor de transaction.
     * 
     * @return Valor de transaction
     * @throws Exception la exception
     */
    public TransactionDTO getTransaction() throws Exception {
        TaskInfo info = this.getTaskInfo();
        return new TransactionDTO(info.getModule(), info.getTransaction(), info.getVersion());
    }

    /**
     * Obtiene el valor de detail.
     * 
     * @param pLanguage the language
     * @return Valor de detail
     * @throws Exception la exception
     */
    public String getDetail(String pLanguage) throws Exception {
        TaskInfo info = this.getTaskInfo();
        Integer messagecode = info.getMessageCode();
        if (messagecode == null) {
            TbpmRulesKey k = new TbpmRulesKey(info.getRuleCode(), ThreadFacade.getSessionData().getCompany());
            TbpmRules r = TbpmRules.find(PersistenceHelper.getEntityManager(), k);
            messagecode = r.getMessagecode();
        }
        com.fp.bpmlib.messages.MessageManager msg = new com.fp.bpmlib.messages.MessageManager(messagecode, pLanguage, null);
        msg.setValue(TaskUtil.BPM_DATA, this.getMaia());
        msg.setValue("request", this.getRequest());
        msg.setValue(ProcessUtil.TASK_METADATA, info);
        return msg.getMessage();
    }

    /**
     * Obtiene el valor de origin data.
     * 
     * @return Valor de origin data
     * @throws Exception la exception
     */
    public Map<String, Object> getOriginData() throws Exception {
        Map<String, Object> data = new HashMap<String, Object>();
        Request rq = this.getRequest();
        List<TbpmFieldTransactionOrigin> l = TbpmFieldTransactionOrigin.find(PersistenceHelper.getEntityManager(), rq.getTransactionModule(),
                rq.getTransactionCode(), rq.getTransactionVersion());
        for (TbpmFieldTransactionOrigin o : l) {
            try {
                data.put(o.getPk().getFieldname(), ELEval.eval(rq, o.getExpresionlanguage()));
            } catch (PropertyAccessException e) {
                continue;
            }
        }
        return data;
    }

    /**
     * Obtiene el valor de destiny meta data.
     * 
     * @return Valor de destiny meta data
     * @throws Exception la exception
     */
    public Map<String, String> getDestinyMetaData() throws Exception {
        Map<String, String> data = new HashMap<String, String>();
        TransactionDTO trn = this.getTransaction();
        List<TbpmFieldTransactionMaping> l = TbpmFieldTransactionMaping.find(PersistenceHelper.getEntityManager(), trn.getModule(),
                trn.getTransaction(), trn.getVersion());
        for (TbpmFieldTransactionMaping o : l) {
            data.put(o.getFieldscreen(), o.getPk().getFieldname());
        }
        return data;
    }

    /**
     * Obtiene el valor de destiny data.
     * 
     * @return Valor de destiny data
     * @throws Exception la exception
     */
    public Map<String, Object> getDestinyData() throws Exception {
        Map<String, Object> data = new HashMap<String, Object>();
        Map<String, Object> origin = this.getOriginData();
        Map<String, String> meta = this.getDestinyMetaData();
        for (Entry<String, String> e : meta.entrySet()) {
            data.put(e.getKey(), origin.get(e.getValue()));
        }
        TransactionDTO trn = this.getTransaction();
        TgeneTransactionKey tKey = new TgeneTransactionKey(trn.getModule(), trn.getTransaction(), trn.getVersion());
        TgeneTransaction trnData = TgeneTransaction.find(PersistenceHelper.getEntityManager(), tKey);
        if (trnData != null && trnData.getQuerycode() != null) {
            QueryProcessor qp = new QueryProcessor();
            QueryRequest qr = new QueryRequest();
            qr.setResponse(new Response(Response.RESPONSE_OK, "OK"));
            qr.setTransactionModule(trn.getModule());
            qr.setTransactionCode(trn.getTransaction());
            qr.setTransactionVersion(trn.getVersion());
            qr.setTransactionModule(trn.getModule());
            for (Entry<String, Object> e : data.entrySet()) {
                qr.put(e.getKey(), e.getValue());
            }
            qr.addAddtionalInfo("queryalias", trnData.getQuerycode());
            qr.addAddtionalInfo("querytype", "P");
            qp.setQueryRequest(qr);
            qp.processQueryByAlias();

            for (Entry<String, Object> e : qr.getResponse().entrySet()) {
                data.put(e.getKey(), e.getValue());
            }
        }
        return data;
    }

    /**
     * Entrega el map que almacena datos utilizados en el bpm.
     * 
     * @param request Request a obtener el map de datos utilizados en el jbpm.
     * @return Map<String, Object>
     * @throws Exception la exception
     */
    public static Map<String, Object> getBpmData(Request request) throws Exception {
        @SuppressWarnings("unchecked")
        Map<String, Object> mdata = (Map<String, Object>) request.get(TaskUtil.BPM_DATA);
        if (mdata == null) {
            mdata = new HashMap<String, Object>();
            request.put(TaskUtil.BPM_DATA, mdata);
        }
        return mdata;
    }
    
    
    
}