package com.fp.bpm;

import java.math.BigDecimal;
import java.sql.Date;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

import org.apache.commons.lang.time.StopWatch;

import com.fp.bpm.actions.ActionTypes;
import com.fp.common.exception.APPException;
import com.fp.common.exception.ExceptionHandler;
import com.fp.common.logger.APPLogger;
import com.fp.dto.data.SaveData;
import com.fp.dto.helper.FileHelper;
import com.fp.persistence.commondb.PersistenceHelper;
import com.fp.persistence.commondb.helper.FormatDates;

/**
 * Clase que se encarga de ejecutar acciones o peticiones que llegan desde los distintos canales.
 * 
 * @author Jorge Vaca.
 * @version 2.1
 */
public class EngineExecutor {

    /** Objeto que contiene la instancia de la accion a ejecutar. */
    private ActionExecutor action = null;

    /** Almacena el metod a ejecutar, logon,save,query */
    private String method;

    /** Almacena el string de ejecucion de una transaccion. */
    private String data;

    private JSONObject response;

    /**
     * Metodo que se encarga de la ejecucion de una accion.
     * 
     * @param pData Datos con los que se procesa la transaccion.
     * @param pFileItems Lista de archivos o imagenes a almacenar en la base.
     * @return Object
     * @throws Exception
     */
    public Object process(String pData, List<FileHelper> pFileItems) throws Throwable {
        StopWatch sw = null;
        boolean error = false;
        try {
            sw = new StopWatch();
            sw.start();
            this.filldata(pData);
            this.execute(pFileItems);
            PersistenceHelper.flushTransaction();
            sw.stop();

        } catch (Throwable e) {
            error = true;
            try {
                if (sw != null) {
                    sw.stop();
                }
            } catch (IllegalStateException e1) {
                // o hacer nada el stopwatch ya esta detenido
            }
            this.fillError(e);
            throw e;
        } finally {
            try {
                if (!error) {
                    this.action.saveLog(this.getTime(sw), this.method);
                } else {
                    this.action.saveLogWithCommit(this.getTime(sw), this.method, this.response.getString("messageCode"),
                            this.response.getString("message"), this.data, this.response.toString());
                }
            } catch (Exception e2) {
                APPLogger.getLogger().error("ERROR GRABANDO LOG AUDITORIA \n");
            }
        }
        return this.response;

    }

    private void execute(List<FileHelper> pFileItems) throws Throwable {
        this.action = ActionTypes.getActionTypes(this.method).getInstanceOfAction();
        this.response.put("result", this.action.execute(this.data, pFileItems));
    }

    private void filldata(String pData) throws Throwable {
        this.response = new JSONObject();
        JSONObject rpcObject = JSONObject.fromObject(pData);
        String id = rpcObject.getString("id");
        if (id != null) {
            this.response.put("id", id);
        }
        JSONArray paramsArray = rpcObject.getJSONArray("params");
        List<Object> params = new ArrayList<Object>();
        for (int i = 0; i < paramsArray.size(); i++) {
            params.add(paramsArray.get(i));
        }
        this.method = rpcObject.getString("method");
        this.data = params.get(0).toString();
        // Almacena string de datos de entrada en el thread local, se utiliza para llenar
        SaveData.setOriginalString(this.data);
    }

    /**
     * Obtiene el tiempo de respuesta resultado de la ejecucion de una transaccion.
     * 
     * @param sw Objeto que contiene el tiempo de ejecucion de una transaccion.
     * @return BigDecimal
     * @throws Exception
     */
    private BigDecimal getTime(StopWatch sw) throws Exception {
        Date d = new Date(sw.getTime());
        return new BigDecimal(FormatDates.getInstance().getTimeInSeconds().format(d));
    }

    private void fillError(Throwable e) throws Exception {
        APPLogger.getLogger().error("\nRequest:==>\n" + this.data);
        ExceptionHandler eh = new ExceptionHandler(e, "es");
        String usrmsg = eh.getUserMessage();
        APPLogger.getLogger().error("User Message:==>\n" + usrmsg);
        this.response.put("message", usrmsg);
        this.response.put("messageCode", eh.getCode());
        this.response.put("isAplicationMessage", eh.isAppexception());
        this.response.put("trace", eh.getTechnicalMessage());
        if ((e instanceof APPException) || (e instanceof SQLException)) {
            this.response.put("stackTrace", eh.getTechnicalMessage());
        } else {
            this.response.put("stackTrace", eh.getStackTrace().length() > 1536 ? eh.getStackTrace().substring(0, 1536) : eh.getStackTrace());
            APPLogger.getLogger().error("StackTrace:==>" + this.response.get("stackTrace"));
        }
    }
}
