/*
 * 
 */
package com.fp.persistence.commondb.db;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.persistence.EntityManager;
import javax.persistence.Query;

import com.fp.common.logger.APPLogger;
import com.fp.persistence.commondb.PersistenceHelper;
import com.fp.persistence.commondb.PersistenceManager;

/**
 * Class CloneData encargada de copiar los datos de una tabla a otra tabla empleando una sola Sentencia SQL
 * (Insert-select)
 * 
 * @author gfiallos
 */
public class CloneData {

    /** Tabla Origen */
    private String table;

    /** Tabla Destino */
    private String destiny;

    /** El valor de fields. */
    private List<String> fields;

    /** El valor de restrictions. */
    private Map<String, Object> restrictions = new HashMap<String, Object>();

    /** El valor de replacements. */
    private Map<String, Object> replacements = new HashMap<String, Object>();

    /** El valor de em. */
    private EntityManager em;

    /**
     * Crea una nueva instancia de clone data.
     * 
     * @param pTable Tabla Origen
     * @throws Exception la exception
     */
    public CloneData(String pTable) throws Exception {
        this(pTable, pTable);
    }

    /**
     * Crea una nueva instancia de clone data.
     * 
     * @param pTable Tabla Origen
     * @param pDestiny Tabla Destino
     * @throws Exception la exception
     */
    public CloneData(String pTable, String pDestiny) throws Exception {
        this.destiny = pDestiny;
        this.table = pTable;
        this.prepare();
    }

    /**
     * Crea una nueva instancia de clone data.
     * 
     * @param pTable Tabla Origen
     * @param pRestrictions the restrictions
     * @throws Exception la exception
     */
    public CloneData(String pTable, Map<String, Object> pRestrictions) throws Exception {
        this(pTable);
        this.restrictions = pRestrictions;
    }

    /**
     * Adiciona un restriction.
     * 
     * @param pName the name
     * @param pValue the value
     */
    public void addRestriction(String pName, Object pValue) {
        this.restrictions.put(pName, pValue);
    }

    /**
     * Adiciona un replacement.
     * 
     * @param pName the name
     * @param pValue the value
     */
    public void addReplacement(String pName, Object pValue) {
        this.replacements.put(pName, pValue);
    }

    /**
     * Prepara los campos para la instrucción dada
     * 
     * @throws Exception la exception
     */
    private void prepare() throws Exception {
        this.em = PersistenceHelper.getEntityManager();
        this.fields = PersistenceManager.getInstance().getDataBaseFields(this.destiny);
    }

    /**
     * Prepara la sentencia instert insert.
     * 
     * @return string
     * @throws Exception la exception
     */
    private String prepareInsert() throws Exception {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        boolean first = true;
        for (String name : this.fields) {
            if (first) {
                pw.print("insert into " + this.destiny + " (" + name);
                first = false;
            } else {
                pw.print(" ," + name);
                first = false;
            }
        }
        pw.println(")");
        pw.close();
        return sw.toString();
    }

    /**
     * Prepara el select.
     * 
     * @return string
     * @throws Exception la exception
     */
    private String prepareSelect() throws Exception {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        boolean first = true;
        for (String name : this.fields) {
            if (this.replacements.containsKey(name)) {
                // name = "cast( :r" + name.toLowerCase() + " as varchar(20) )";
                name = "'" + this.replacements.get(name) + "'";
            }
            if (first) {
                pw.print("select " + name);
                first = false;
            } else {
                pw.print(" ," + name);
                first = false;
            }
        }
        pw.println();
        pw.println(" from " + this.table);
        first = true;
        for (Entry<String, Object> val : this.restrictions.entrySet()) {
            if (first) {
                pw.println(" where " + val.getKey() + "=:_" + val.getKey().toLowerCase());
                first = false;
            } else {
                pw.println(" and " + val.getKey() + "=:_" + val.getKey().toLowerCase());
                first = false;
            }
        }
        pw.close();
        return sw.toString();
    }

    /**
     * Executa la copia
     * 
     * @return int
     * @throws Exception la exception
     */
    public int execute() throws Exception {
        PersistenceHelper.flushTransaction();
        String sql = this.prepareInsert() + this.prepareSelect();
        APPLogger.getLogger().debug("Instruccion de Copia " + sql);
        Query q = this.em.createNativeQuery(sql);
        for (Entry<String, Object> val : this.restrictions.entrySet()) {
            APPLogger.getLogger().debug("_" + val.getKey().toLowerCase() + ">>>>" + val.getValue());
            q.setParameter("_" + val.getKey().toLowerCase(), val.getValue());
        }
        // for (Entry<String, Object> val : this.replacements.entrySet()) {
        // q.setParameter("r" + val.getKey().toLowerCase().toLowerCase(), val.getValue());
        // APPLogger.getLogger().debug("r" + val.getKey().toLowerCase() + ">>>>" + val.getValue());
        // }
        return q.executeUpdate();
    }
}
