package com.fp.bpm;

import java.math.BigDecimal;
import java.net.InetAddress;
import java.util.List;

import com.fp.base.persistence.util.helper.MessageManager;
import com.fp.common.helper.BeanManager;
import com.fp.common.logger.APPLogger;
import com.fp.dto.Request;
import com.fp.dto.Response;
import com.fp.dto.data.SaveData;
import com.fp.dto.helper.FileHelper;
import com.fp.persistence.commondb.NestedSave;
import com.fp.persistence.commondb.db.DataHelper;
import com.fp.persistence.commondb.helper.FormatDates;
import com.fp.persistence.pgeneral.date.TgeneAccountingDateBranch;
import com.fp.persistence.pgeneral.log.TgeneTransactionLog;
import com.fp.persistence.pgeneral.log.TgeneTransactionMessage;
import com.fp.persistence.pgeneral.trans.TgeneTransaction;

/**
 * @author jorge
 *
 */
public abstract class ActionExecutor {

    /**
     * Request de entrada de una transaccion
     */
    protected Request request;

    /**
     * Objeto que contiene el log de una transaccion.
     */
    protected TgeneTransactionLog log = null;

    public abstract String execute(String pRequest, List<FileHelper> fileItem) throws Exception;

    protected TgeneTransactionLog requestToLog() throws Exception {
        InetAddress.getLocalHost().getAddress();
        this.log = new TgeneTransactionLog();
        this.log.setCompanycode(this.request.getCompany());
        this.request.setJournalId(this.request.getJournalId());
        // Fija parametros al objeto
        this.log.setPk(this.request.getJournalId());
        this.log.setTransactionmodule(this.request.getTransactionModule());
        this.log.setTransactioncode(this.request.getTransactionCode());
        this.log.setTransactionversion(this.request.getTransactionVersion());
        // Fecha contable
        if (this.request.getCompany() != null) {
            TgeneAccountingDateBranch d = DataHelper.getInstance().getTgeneAccountingDateBranch(this.request.getCompany(), 0);
            this.log.setAccountingdate(d.getAccountingdate());
            this.request.put("accountingdate", d.getAccountingdate());
            this.request.put("workingdate", d.getWorkingdate());
        }
        if ((this.request.getTransactionModule() != null) && (this.request.getTransactionCode() != null)
                && (this.request.getTransactionVersion() != null)) {
            // Almacena en el request los datos generales de la definicion de una transaccion.
            this.request.mbeans.put("tgenetransaction",DataHelper.getInstance().getTgeneTransaction(
                    this.request.getTransactionModule(), this.request.getTransactionCode(),this.request.getTransactionVersion()));
        }
        this.log.setServer(this.getIp());
        this.log.setRealdate(FormatDates.getInstance().getDataBaseTimestamp());
        this.log.setBranchcode(this.request.getBranchCode());
        this.log.setOfficecode(this.request.getOfficeCode());
        this.log.setTerminalcode(this.request.getTerminalCode());
        this.log.setUsercode(this.request.getUser());
        this.log.setBrowser(this.request.getBrowser());
        this.log.setOperativsystem(this.request.getOperativesystem());
        this.request.mbeans.put("tgenetransactionlog", this.log);
        return this.log;
    }

    /**
     * Metodo que graba el log de transacciones cuando el resultado de ejecucion
     * de la transaccion es ok.
     *
     * @param pTime Tiempo de ejecucion de la transaccion.
     * @param pMethod
     * @throws Exception
     */
    public void saveLog(BigDecimal pTime, String pMethod) throws Exception {
        if (!this.isInsert()) {
            return;
        }
        this.log.setDuration(pTime);
        this.log.setResultcode(Response.RESPONSE_OK);
        this.log.setUsermessage("OK");
        this.log.setMethod(this.getMethod(pMethod));
        this.setAditionaldata();
        new NestedSave(this.log.getCompanycode(), this.log).start();
    }

    /**
     *
     * @throws Exception
     */
    private void setAditionaldata() throws Exception {
        this.log.setPersoncode(this.request.getLogperson());
        this.setPerson();
        this.log.setSolicitudenumber(this.request.getLogsolicitude() == null ? this.request.get("csolicitud") == null ? this.request.getLogsolicitude() : this.request.get("csolicitud").toString() : this.request.getLogsolicitude());
        this.setSolicitudnumber();
        this.log.setAccount(this.request.getLogaccount());
        this.setAccount();
        if (this.request.mbeans.get("tgenetransaction") == null) {
            return;
        }
        Integer messagecode = ((TgeneTransaction) this.request.mbeans.get("tgenetransaction")).getMessagecode();
        if ((this.log.getAditionaldata() == null) && (messagecode != null)) {
            String msg = MessageManager.getMessage(this.request);
            if (msg.length() > 299) {
                this.log.setAditionaldata(msg.substring(0, 298));
            } else {
                this.log.setAditionaldata(msg);
            }
            if (this.request.getSubject() == null) {
                this.request.setSubject(msg);
            }
        }

    }

    private void setPerson() throws Exception {
        if (this.log.getPersoncode() == null) {
            try {
                BeanManager.setBeanAttributeValue(this.log, "personcode",
                        SaveData.getPersoncode() == null ? null : Integer.valueOf(SaveData.getPersoncode()));
            } catch (Exception e) {
            }
        }
    }

    private void setAccount() throws Exception {
        if (this.log.getAccount() == null) {
            try {
                BeanManager.setBeanAttributeValue(this.log, "account", SaveData.getAccount());
            } catch (Exception e) {
                // TODO: handle exception
            }
        }
    }

    private void setSolicitudnumber() throws Exception {
        if (this.log.getSolicitudenumber() == null) {
            try {
                BeanManager.setBeanAttributeValue(this.log, "solicitudenumber", SaveData.getSolicitudenumber());
            } catch (Exception e) {
                // TODO: handle exception
            }
        }

    }

    /**
     * Metodo que almacena el log de auditoria, sin que este forme parte del
     * tiempo de la transaccion.
     *
     * @param pTime Tiempo de ejecucion de la transaccion.
     * @param pResultCode Codigo de resultado.
     * @param pMessage Messaje de usuario.
     * @throws Exception
     */
    public void saveLogWithCommit(BigDecimal pTime, String pMethod, String pResultCode, String pMessage, String indata, String outdata)
            throws Exception {
        if (!this.isInsert()) {
            return;
        }

        this.log.setDuration(pTime);
        this.log.setResultcode(pResultCode.length() > 20 ? pResultCode.substring(0, 19) : pResultCode);
        this.log.setUsermessage(pMessage.length() > 200 ? pMessage.substring(0, 199) : pMessage);
        this.log.setMethod(this.getMethod(pMethod));
        // Mensajes de entrada y salida
        TgeneTransactionMessage m = new TgeneTransactionMessage();
        m.setPk(this.request.getJournalId());
        m.setDatain(indata);
        m.setDataout(outdata);
        if (indata != null) {
            new NestedSave(this.log.getCompanycode(), this.log, m).start();
        } else {
            new NestedSave(this.log.getCompanycode(), this.log).start();
        }

    }

    /**
     * Verifica si almacena o no el log de auditoria.
     *
     * @return boolean
     * @throws Exception
     */
    private boolean isInsert() throws Exception {
        if ((this.log == null) || (this.log.getTransactionmodule() == null) || (this.log.getTransactioncode() == null)) {
            return false;
        }
        return true;
    }

    /**
     * Entrega las iniciales del metodo a almacenar en la tabla de log de
     * transacciones.
     *
     * @param pMethod Nombre del metodo a ejecutar.
     * @return String
     * @throws Exception
     */
    private String getMethod(String pMethod) throws Exception {
        if (pMethod.compareTo("save") == 0) {
            return "S";
        }
        if (pMethod.compareTo("query") == 0) {
            return "Q";
        }
        if (pMethod.compareTo("logon") == 0) {
            return "L";
        }
        return "";
    }

    /**
     * Entrega la ip del servidor de aplicaciones.
     *
     * @return String
     * @throws Exception
     */
    private String getIp() throws Exception {
        byte[] ip = InetAddress.getLocalHost().getAddress();

        String sIPAddress = "";

        for (int x = 0; x < ip.length; x++) {
            if (x > 0) {
                // A todos los numeros les anteponemos
                // un punto menos al primero
                sIPAddress += ".";
            }
            // Jugamos con los bytes y cambiamos el bit del signo
            sIPAddress += ip[x] & 255;
        }
        return sIPAddress;
    }

    /**
     * Entrega el valor de: request
     *
     * @return Request
     */
    public Request getRequest() {
        return this.request;
    }

    /**
     * Fija el modulo, transaccion y version de origen.
     *
     * @throws Exception
     */
    public void fillOriginalTransaction() throws Exception {
        if (this.request != null) {
            this.request.setOriginalTransactionModule(this.request.getTransactionModule());
            this.request.setOriginalTransactionCode(this.request.getTransactionCode());
            this.request.setOriginalTransactionVersion(this.request.getTransactionVersion());
        }
    }
}
