/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package salidasautomaticas.util;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Types;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.json.JSONArray;
import org.json.JSONObject;

/**
 *
 * @author sistem19user
 */
public class EstandarDAO {

    private static final Logger log = LogManager.getLogger(EstandarDAO.class);

    private static EstandarDAO instance;

    private EstandarDAO() {
    }

    public static EstandarDAO getInstance() {
        if (instance == null) {
            instance = new EstandarDAO();
        }
        return instance;
    }

    public JSONObject listar(Connection cn, String query, JSONArray... parametros) throws Exception {
        JSONObject obj = new JSONObject();
        JSONArray jsonArray = consultar(cn, query, parametros);

        int length = jsonArray.length();

        obj.put("data", jsonArray);
        obj.put("recordsFiltered", length);
        obj.put("recordsTotal", length);
        obj.put("draw", "1");

        return obj;
    }

    /**
     * Este metodo ejecuta una sentencia sql de consulta(solamente selects) en
     * la base de datos y devuelve como resultado en un JSONArray los registros
     * obtenidos
     *
     *
     * @param cn objeto que representa la conexion a la base de datos
     * @param query sentencia sql que se desea ejecutar en la base de datos
     * @param parametros parametros que vamos a pasar al query, *es opcional
     * @return Devuelve como resultado los registros obtenidos por el query en
     * un objeto JSONArray
     * @throws Exception
     */
    public JSONArray consultar(Connection cn, String query, JSONArray... parametros) throws Exception {
        //objeto que almacena todos las filas obtenidas por el query
        JSONArray jsonArray = new JSONArray();
        try {
            if (cn == null) {
                jsonArray = null;
            } else {
                PreparedStatement ps = cn.prepareStatement(query);

                //validamos si existen parametros
                if (parametros != null && parametros.length > 0) {
                    JSONArray params = parametros[0];
                    int cont = 1;

                    //Recorremos la lista de parametros y lo seteamos en el preparedstatement
                    for (Object parametro : params) {
                        setPreparedStatement(ps, cont, parametro);
                        cont++;
                    }
                }
                log.trace("query [SELECT] =" + ps);

                ResultSet rs = ps.executeQuery();
                ResultSetMetaData rm = rs.getMetaData();
                int numCols = rm.getColumnCount();

                while (rs.next()) {
                    JSONObject obj = new JSONObject();
                    for (int i = 1; i <= numCols; i++) {
                        castColumn(rs, rm, i, obj);
                    }
                    jsonArray.put(obj);
                }

//                rs.close();
//                cn.close();
            }
        } catch (Exception ex) {
            log.error("error", ex);
            throw ex;
        }
        return jsonArray;
    }

    /**
     * Este metodo nos permite ejecutar una sentencia sql en la base de datos y
     * devuelve como resultado en un JSONObject las filas afectadas por el
     * query.
     *
     * @param cn objeto que representa la conexion a la base de datos
     * @param query sentencia sql que se desea ejecutar en la base de datos
     * @param parametros parametros que vamos a pasar al query, *es opcional
     * @return Devuelve la cantidad de filas afectadas por el query en un
     * JSONObject.
     * @throws Exception
     */
    public JSONObject ejecutar(Connection cn, String query, JSONArray... parametros) throws Exception {
        JSONObject obj = new JSONObject();
        try {
            if (cn == null) {
                obj.put("msg", 0);
            } else {
                PreparedStatement ps = cn.prepareStatement(query);
                if (parametros != null && parametros.length > 0) {
                    JSONArray params = parametros[0];
                    int cont = 1;
                    for (Object parametro : params) {
                        setPreparedStatement(ps, cont, parametro);
                        cont++;
                    }
                }

                log.trace("query [ExecuteUpdate]= " + ps);
                int filas = ps.executeUpdate();
                if (filas > 0) {
                    obj.put("msg", filas);
                } else {
                    obj.put("msg", 0);
                }
            }
        } catch (Exception ex) {
            log.error("error", ex);
            throw ex;
        }
        return obj;
    }

    /**
     * Setea en el prepared statement el valor del parametro segun su tipo de
     * dato.
     *
     * @param ps representa el objeto PreparedStatement
     * @param index indica la posicion del parametro en la consulta sql
     * @param p parametro de la consulta sql
     * @throws SQLException
     */
    private void setPreparedStatement(PreparedStatement ps, int index, Object p) throws SQLException {
        if (p instanceof Integer) {
            ps.setInt(index, (int) p);
        } else if (p instanceof String) {
            ps.setString(index, p.toString());
        } else if (p instanceof Double) {
            ps.setDouble(index, (double) p);
        } else if (p instanceof Boolean) {
            ps.setBoolean(index, (boolean) p);
        }
    }

    /**
     * Obtiene el valor de una columna de una tabla y lo guarda en el objeto
     * JSONObject con el tipo de dato que le corresponde.
     *
     * @param rs Objeto ResultSet para obtener el valor de una columna de una
     * tabla
     * @param rsmd Objeto ResultSetMetaData nos permite obtener el nombre y tipo
     * de columna
     * @param columnIndex Posicion de la columna en la sentencia sql
     * @param obj Representa a un registro de la base de datos
     * @throws SQLException
     */
    private void castColumn(ResultSet rs, ResultSetMetaData rsmd, int columnIndex, JSONObject obj) throws SQLException {
        //obtenemos el tipo de dato de la columna de la tabla
        int type = rsmd.getColumnType(columnIndex);

        //evaluamos el tipo
        switch (type) {
            case Types.VARCHAR:
            case Types.CHAR:
                obj.put(rsmd.getColumnName(columnIndex), rs.getString(columnIndex));
                break;
            case Types.INTEGER:
                obj.put(rsmd.getColumnName(columnIndex), rs.getInt(columnIndex));
                break;
            case Types.BIT:
                obj.put(rsmd.getColumnName(columnIndex), rs.getBoolean(columnIndex));
                break;
            default:
                obj.put(rsmd.getColumnName(columnIndex), rs.getString(columnIndex));
        }
    }
}