package com.ejie.m62fb.dao.registroVPP;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.sql.Blob;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;
import javax.sql.DataSource;
import javax.sql.rowset.serial.SerialBlob;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.support.DataAccessUtils;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.support.AbstractLobCreatingPreparedStatementCallback;
import org.springframework.jdbc.support.lob.DefaultLobHandler;
import org.springframework.jdbc.support.lob.LobCreator;
import org.springframework.jdbc.support.lob.LobHandler;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import com.ejie.m62fb.dao.SQLGenericas;
import com.ejie.m62fb.model.Plantilla;
import com.ejie.m62fb.model.documento.Documento;
import com.ejie.m62fb.model.documento.DocumentoRegVPP;
import com.ejie.m62fb.model.tipos.TipoSiNo;
import com.ejie.x38.dto.JQGridManager;
import com.ejie.x38.dto.JQGridRequestDto;

/**
 * DocumentosDaoImpl
 * 
XXXX
 */

@Repository
@Transactional
public class DocumentosDaoImpl implements DocumentosDao {
	protected static final DocumentoRegVPP UtilsResultSet = null;
	private static final Logger logger = LoggerFactory.getLogger(DocumentosDaoImpl.class);
	private JdbcTemplate jdbcTemplate;
	/*
	 * ROW_MAPPERS
	 */
	private final RowMapper<DocumentoRegVPP> rwMap = new RowMapper<DocumentoRegVPP>() {
		
		@Override
		public DocumentoRegVPP mapRow(ResultSet resultSet, int rowNum)
				throws SQLException {
			DocumentoRegVPP documentoRegVPP = new DocumentoRegVPP();
			documentoRegVPP.setIdTabla(resultSet.getBigDecimal("ID_TABLA"));
			documentoRegVPP.setIdElemRelac(resultSet.getBigDecimal("ID_ELEMENTO_RELACIONADO"));
			Documento documento = new Documento();
			documento.setNumDocumento(resultSet.getBigDecimal("NUMDOCUMENTO"));
			documento.setNomDocumento(resultSet.getString("NOMDOCUMENTO"));
			documento.setPlantilla(new Plantilla(resultSet.getString("DESPLANTCODPLANTILLA"),
					resultSet.getString("DESPLANTDESPLANTILLACAS"),resultSet.getString("DESPLANTDESPLANTILLAEUS")));
			documento.setFecRecibi(resultSet.getDate("FEC_RECIBI"));
			documento.setFecEnvio(resultSet.getDate("FEC_ENVIO"));
			documento.setIndAnexado(new TipoSiNo(resultSet.getString("ANEXCODTABLA"), 
					resultSet.getString("ANEXDESTABLACAS"),resultSet.getString("ANEXDESTABLAEUS")));
			documento.setIndAcuseRecibo(new TipoSiNo(resultSet.getString("ACUSECODTABLA"), 
					resultSet.getString("ACUSEDESTABLACAS"),resultSet.getString("ACUSEDESTABLAEUS")));
			documento.setIndDocuAnulado(new TipoSiNo(resultSet.getString("DOCANULCODTABLA"), 
					resultSet.getString("DOCANULDESTABLACAS"),resultSet.getString("DOCANULDESTABLAEUS")));
			
		//	UtilsResulSet.getAbstractClass(resultSet, documento, DocumentosDaoSql.ALIAS);
			documentoRegVPP.setDocumento(documento);
			return documentoRegVPP;
		}
	};

	/*
	 * ROW_MAPPERS
	 */
	private final RowMapper<Documento> rwMapDescarga = new RowMapper<Documento>() {
		LobHandler lobHandler = new DefaultLobHandler();
		@Override
		public Documento mapRow(ResultSet resultSet, int rowNum)
		throws SQLException {
			Documento documento = new Documento();
			documento.setNumDocumento(resultSet.getBigDecimal("NUMDOCUMENTO"));
			documento.setNomDocumento(resultSet.getString("NOMDOCUMENTO"));
			documento.setPlantilla(new Plantilla(resultSet.getString("DESPLANTCODPLANTILLA"),
					resultSet.getString("DESPLANTDESPLANTILLACAS"),resultSet.getString("DESPLANTDESPLANTILLAEUS")));
			documento.setFecRecibi(resultSet.getDate("FEC_RECIBI"));
			documento.setFecEnvio(resultSet.getDate("FEC_ENVIO"));
			documento.setIndAnexado(new TipoSiNo(resultSet.getString("ANEXCODTABLA"), 
					resultSet.getString("ANEXDESTABLACAS"),resultSet.getString("ANEXDESTABLAEUS")));
			documento.setIndAcuseRecibo(new TipoSiNo(resultSet.getString("ACUSECODTABLA"), 
					resultSet.getString("ACUSEDESTABLACAS"),resultSet.getString("ACUSEDESTABLAEUS")));
			documento.setIndDocuAnulado(new TipoSiNo(resultSet.getString("DOCANULCODTABLA"), 
					resultSet.getString("DOCANULDESTABLACAS"),resultSet.getString("DOCANULDESTABLAEUS")));
			//UtilsResulSet.getAbstractClass(resultSet, documento, DocumentosDaoSql.ALIAS);
			
			//CONTENIDO DEL DOCUMENTO
			byte[] blobBytes = lobHandler.getBlobAsBytes(resultSet, "CONTENIDO");
			documento.setContenido(new SerialBlob(blobBytes));
			
			return documento;
			}
			
	};

	/**
	 * Method use to set the datasource.
	 * 
	 * @param dataSource
	 *            DataSource
	 * @return
	 */
	@Resource
	public void setDataSource(DataSource dataSource) {
		this.jdbcTemplate = new JdbcTemplate(dataSource);
	}
	/**
	 * <p>Devuelve la Insert que corresponde en funci&oacute;n del tipo de documento</p>
	 * @param source
	 * @return
	 */
	private String getInsertRelac(int source){
		StringBuilder sql = new StringBuilder(); 
		switch (source) {
		case DocumentoRegVPP.SOURCE_VIVIENDA:
			sql.append(DocumentosDaoSql.SQL_INSERT_REL_DOC_VIV);
			break;
		case DocumentoRegVPP.SOURCE_ELEMENTO:
			sql.append(DocumentosDaoSql.SQL_INSERT_REL_DOC_ELEM);
			break;
		case DocumentoRegVPP.SOURCE_CONTR_ALQ_VIV:
			sql.append(DocumentosDaoSql.SQL_INSERT_REL_DOC_CONTRATO_ALQ_VIVIENDA);
			break;
		case DocumentoRegVPP.SOURCE_CONTR_ALQ_ELEM:
			sql.append(DocumentosDaoSql.SQL_INSERT_REL_DOC_CONTRATO_ALQ_ELEMENTO);
			break;
		case DocumentoRegVPP.SOURCE_CONTR_ALQ_AD:
			sql.append(DocumentosDaoSql.SQL_INSERT_REL_DOC_CONTRATO_ALQUILERAD);
			break;
		case DocumentoRegVPP.SOURCE_CONTR_COMPRA_VIV:
			sql.append(DocumentosDaoSql.SQL_INSERT_REL_DOC_CONTRATO_VIVIENDA);
			break;	
		default:
			break;
		}
		return sql.toString();
	}
	/**
	 * <p>Devuelve la Select que corresponde en funci&oacute;n del tipo de documento</p>
	 * @param source
	 * @return
	 */
	private String getSelect(int source){
		StringBuilder sql = new StringBuilder(); 
		switch (source) {
		case DocumentoRegVPP.SOURCE_VIVIENDA:
			sql.append(DocumentosDaoSql.SQL_FIND_ALL_VIVIENDA);
			break;
		case DocumentoRegVPP.SOURCE_ELEMENTO:
			sql.append(DocumentosDaoSql.SQL_FIND_ALL_ELEMENTO);
			break;
		case DocumentoRegVPP.SOURCE_CONTR_ALQ_VIV:
			sql.append(DocumentosDaoSql.SQL_FIND_ALL_CONTRATO_ALQ_VIVIENDA);
			break;
		case DocumentoRegVPP.SOURCE_CONTR_ALQ_ELEM:
			sql.append(DocumentosDaoSql.SQL_FIND_ALL_CONTRATO_ALQ_ELEMENTO);
			break;
		case DocumentoRegVPP.SOURCE_CONTR_ALQ_AD:
			sql.append(DocumentosDaoSql.SQL_FIND_ALL_CONTRATO_ALQUILERAD);
			break;
		case DocumentoRegVPP.SOURCE_CONTR_COMPRA_VIV:
			sql.append(DocumentosDaoSql.SQL_FIND_ALL_CONTRATO_VIVIENDA);
			break;	
		default:
			break;
		}
		return sql.toString();
	}
	/**
	 * <p>Devuelve la Select Count(1) que corresponde en funci&oacute;n del tipo de documento</p>
	 * @param source
	 * @return
	 */
	private String getSelectCount(int source){
		StringBuilder sql = new StringBuilder(); 
		switch (source) {
		case DocumentoRegVPP.SOURCE_VIVIENDA:
			sql.append(DocumentosDaoSql.SQL_FIND_ALL_VIVIENDA_COUNT);
			break;
		case DocumentoRegVPP.SOURCE_ELEMENTO:
			sql.append(DocumentosDaoSql.SQL_FIND_ALL_ELEMENTO_COUNT);
			break;
		case DocumentoRegVPP.SOURCE_CONTR_ALQ_VIV:
			sql.append(DocumentosDaoSql.SQL_FIND_ALL_CONTRATO_ALQ_VIVIENDA_COUNT);
			break;
		case DocumentoRegVPP.SOURCE_CONTR_ALQ_ELEM:
			sql.append(DocumentosDaoSql.SQL_FIND_ALL_CONTRATO_ALQ_ELEMENTO_COUNT);
			break;
		case DocumentoRegVPP.SOURCE_CONTR_ALQ_AD:
			sql.append(DocumentosDaoSql.SQL_FIND_ALL_CONTRATO_ALQUILERAD_COUNT);
			break;
		case DocumentoRegVPP.SOURCE_CONTR_COMPRA_VIV:
			sql.append(DocumentosDaoSql.SQL_FIND_ALL_CONTRATO_VIVIENDA_COUNT);
			break;
		default:
			break;
		}
		return sql.toString();
	}
	/**
	 * <p>Devuelve la WHERE que corresponde en funci&oacute;n del tipo de documento</p>
	 * @param source
	 * @return
	 */
	private String getWhere(int source){
		StringBuilder sql = new StringBuilder(); 
		switch (source) {
		case DocumentoRegVPP.SOURCE_VIVIENDA:
			sql.append(DocumentosDaoSql.SQL_WHERE_VIVIENDA).append( " AND "+DocumentosDaoSql.ALIAS_CONTENIDO +".NUM_PARTE = 0" );
			break;
		case DocumentoRegVPP.SOURCE_ELEMENTO:
			sql.append(DocumentosDaoSql.SQL_WHERE_ELEMENTO);
			break;
		case DocumentoRegVPP.SOURCE_CONTR_ALQ_VIV:
			sql.append(DocumentosDaoSql.SQL_WHERE_CONTRATO_ALQ_VIVIENDA);
			break;
		case DocumentoRegVPP.SOURCE_CONTR_ALQ_ELEM:
			sql.append(DocumentosDaoSql.SQL_WHERE_CONTRATO_ALQ_ELEMENTO);
			break;
		case DocumentoRegVPP.SOURCE_CONTR_ALQ_AD:
			sql.append(DocumentosDaoSql.SQL_WHERE_CONTRATO_ALQUILERAD);
			break;
		case DocumentoRegVPP.SOURCE_CONTR_COMPRA_VIV:
			sql.append(DocumentosDaoSql.SQL_WHERE_CONTRATO_VIVIENDA);
			break;
		default:
			break;
		}
		return sql.toString();
	}
	/**
	 * <p>Devuelve la DELETE que corresponde en funci&oacute;n del tipo de documento</p>
	 * @param source
	 * @return
	 */
	private String getDelete(int source){
		StringBuilder sql = new StringBuilder(); 
		switch (source) {
		case DocumentoRegVPP.SOURCE_VIVIENDA:
			sql.append(DocumentosDaoSql.SQL_DELETE_DOC_VIVIENDA);
			break;
		case DocumentoRegVPP.SOURCE_ELEMENTO:
			sql.append(DocumentosDaoSql.SQL_DELETE_DOC_ELEMENTO);
			break;
		case DocumentoRegVPP.SOURCE_CONTR_ALQ_VIV:
			sql.append(DocumentosDaoSql.SQL_DELETE_DOC_CONTRATO_ALQ_VIVIENDA);
			break;
		case DocumentoRegVPP.SOURCE_CONTR_ALQ_ELEM:
			sql.append(DocumentosDaoSql.SQL_DELETE_DOC_CONTRATO_ALQ_ELEMENTO);
			break;
		case DocumentoRegVPP.SOURCE_CONTR_ALQ_AD:
			sql.append(DocumentosDaoSql.SQL_DELETE_DOC_CONTRATO_ALQUILERAD);
			break;
		case DocumentoRegVPP.SOURCE_CONTR_COMPRA_VIV:
			sql.append(DocumentosDaoSql.SQL_DELETE_DOC_CONTRATO_VIVIENDA);
			break;
		default:
			break;
		}
		return sql.toString();
	}
	/*
	 * OPERACIONES CRUD
	 */
	// Metodo para rellenar los params con todos los campos del bean
	public void getParams(List<Object> params, DocumentoRegVPP documentoRegVPP) {
		if (documentoRegVPP == null) {
			return;
		}
		if(documentoRegVPP.getDocumento()!=null && documentoRegVPP.getDocumento().getNumDocumento()!=null){
			params.add(documentoRegVPP.getDocumento().getNumDocumento());
		}
		
		if(documentoRegVPP.getIdElemRelac()!=null){
			params.add(documentoRegVPP.getIdElemRelac());
		}
			
		// SQLGenericas.ABSTRACT_CLASS_SQL
		SQLGenericas.getParamsAbstractClass(params, documentoRegVPP);
	}

	
	public DocumentoRegVPP add(final DocumentoRegVPP documentoRegVPP){
		KeyHolder keyHolder = new GeneratedKeyHolder();
		DocumentosDaoImpl.logger.error("NUMEXPEDIENTE-->"+documentoRegVPP.getDocumento().getNumExpediente());
		this.jdbcTemplate.update(new PreparedStatementCreator() {
		    	@Override
		        public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
		    		//Sentencia SQL que realiza la INSERT y array con ids que son primary jey
		            PreparedStatement ps =
		                connection.prepareStatement(DocumentosDaoSql.SQL_INSERT_DATOS_DOCUMENTO, new String[] {"NUM_DOCUMENTO"});
		            ps.setString(1, /* COD_PLANTILLA */ documentoRegVPP.getDocumento().getPlantilla().getCodPlantilla());
		            ps.setString(2, /* COD_TIPO_PLANTILLA */ documentoRegVPP.getDocumento().getCodTipoPlantilla());
		            ps.setString(3, /* NUM_EXPEDIENTE */ documentoRegVPP.getDocumento().getNumExpediente());
		            ps.setString(4, /* NOM_DOCUMENTO */documentoRegVPP.getDocumento().getNomDocumento());
		            ps.setString(5, /* COD_USU_ULT_MODIF */documentoRegVPP.getCodUsuarioUltimaModif());
		            return ps;
		        }
		    },
		    keyHolder);
		
		//obtener NUM_DOCUMENTO generado por el trigger
		documentoRegVPP.getDocumento().setNumDocumento(new BigDecimal(keyHolder.getKeys().get("NUM_DOCUMENTO").toString()));
		
		return documentoRegVPP;
	}
	public Documento addSource(final Documento documento){
		LobHandler lobhandler = new DefaultLobHandler();
		 this.jdbcTemplate.execute(DocumentosDaoSql.SQL_INSERT_SOURCE_DOCUMENTO,
			 new AbstractLobCreatingPreparedStatementCallback(lobhandler) {
				protected void setValues(PreparedStatement ps, LobCreator lobCreator) throws SQLException {
					 int pos=1;
					 ps.setBigDecimal(pos++, documento.getNumDocumento());
					 lobCreator.setBlobAsBinaryStream(ps, pos++, documento.getContenido().getBinaryStream(), (int)documento.getContenido().length());
					 ps.setBigDecimal(pos++, BigDecimal.ZERO);
				}
			 }
		 );
		 return documento;
		
	}
	@Override
	public DocumentoRegVPP addRelacionDocElem(final DocumentoRegVPP documentoRegVPP){
		String query = getInsertRelac(documentoRegVPP.getFuente());

		// Campos para la generación de un elemento
		// Preparar Parametros
		List<Object> params = new ArrayList<Object>();
		getParams(params, documentoRegVPP);
		this.jdbcTemplate.update(query, params.toArray());
		 return documentoRegVPP;
		
	}

	@Override
	@Transactional(readOnly = true)
	public Documento find(Documento documento) throws SQLException, IOException {
		String query = DocumentosDaoSql.SQL_FIND_DESCARGA;
		List<Documento> documentoList = this.jdbcTemplate.query(query,
				this.rwMapDescarga, documento.getNumDocumento());
		if (documentoList.size()>1){
			documento=documentoList.get(0);
			ByteArrayOutputStream outputStream = new ByteArrayOutputStream( );
			for (Documento documento2 : documentoList) {
				outputStream.write(documento2.getContenido().getBytes(1, (int) documento2.getContenido().length()));
			}
			Blob clob = new SerialBlob(new byte[outputStream.size()]);
			clob.setBytes(1, outputStream.toByteArray());
			documento.setContenido(clob);
		    
		    return documento;
		}
		return DataAccessUtils.uniqueResult(documentoList);
	}

	
	@Override
	public void remove(DocumentoRegVPP documentoRegVPP) {
		//borrar de la tabla relacionada
		//String query =getDelete(documentoRegVPP.getFuente());
		//this.jdbcTemplate.update(query, documentoRegVPP.getDocumento().getNumDocumento());
		String query =getDelete(DocumentoRegVPP.SOURCE_VIVIENDA);
		this.jdbcTemplate.update(query, documentoRegVPP.getDocumento().getNumDocumento());
		query =getDelete(DocumentoRegVPP.SOURCE_ELEMENTO);
		this.jdbcTemplate.update(query, documentoRegVPP.getDocumento().getNumDocumento());
		query =getDelete(DocumentoRegVPP.SOURCE_CONTR_ALQ_VIV);
		this.jdbcTemplate.update(query, documentoRegVPP.getDocumento().getNumDocumento());
		query =getDelete(DocumentoRegVPP.SOURCE_CONTR_ALQ_ELEM);
		this.jdbcTemplate.update(query, documentoRegVPP.getDocumento().getNumDocumento());
		query =getDelete(DocumentoRegVPP.SOURCE_CONTR_ALQ_AD);
		this.jdbcTemplate.update(query, documentoRegVPP.getDocumento().getNumDocumento());
		query =getDelete(DocumentoRegVPP.SOURCE_CONTR_COMPRA_VIV);
		this.jdbcTemplate.update(query, documentoRegVPP.getDocumento().getNumDocumento());
		//borrar de la tabla m62e15t0 - Contenido del documento
		query =DocumentosDaoSql.SQL_DELETE_SOURCE_DOCUMENTO;
		this.jdbcTemplate.update(query, documentoRegVPP.getDocumento().getNumDocumento());
		//borrar de la tabla m62e14t0 - Contenido del documento
		query =DocumentosDaoSql.SQL_DELETE_DOCUMENTO;
		this.jdbcTemplate.update(query, documentoRegVPP.getDocumento().getNumDocumento());
	}

	
	@Override
	@Transactional(readOnly = true)
	public List<DocumentoRegVPP> findAllLike(DocumentoRegVPP documentoRegVPP,
			JQGridRequestDto jqGridRequestDto,Boolean startsWith) {
		StringBuilder query = new StringBuilder(getSelect(documentoRegVPP.getFuente()));
		
		// Where clause & Params
		Map<String, ?> mapaWhere = this.getWhereLikeMap(documentoRegVPP, startsWith);
		StringBuilder where = new StringBuilder(getWhere(documentoRegVPP.getFuente()));
		where.append(mapaWhere.get("query"));
		query.append(where);

		List<?> params = (List<?>) mapaWhere.get("params");

		if (jqGridRequestDto != null) {
			query = JQGridManager.getPaginationQuery(jqGridRequestDto, query);
		}

		return this.jdbcTemplate.query(query.toString(), this.rwMap,
				params.toArray());
	}
	/**
	 * @param documentoRegVPP
	 *            DocumentoRegVPP
	 * @param startsWith
	 *            Boolean
	 * @return Long
	 */
	@Override
	@Transactional(readOnly = true)
	public Long findAllLikeCount(DocumentoRegVPP documentoRegVPP,Boolean startsWith) {
		StringBuilder query = new StringBuilder(getSelectCount(documentoRegVPP.getFuente()));

		// Where clause & Params
		Map<String, ?> mapaWhere = this.getWhereLikeMap(documentoRegVPP, startsWith);
		StringBuilder where = new StringBuilder(getWhere(documentoRegVPP.getFuente()));
		where.append(mapaWhere.get("query"));
		query.append(where);

		List<?> params = (List<?>) mapaWhere.get("params");

		return this.jdbcTemplate.queryForLong(query.toString(),
				params.toArray());
	}

	/*
	 * MÉTODOS PRIVADOS
	 */
//
//	/**
//	 * Returns a map with the needed value to create the conditions to filter by
//	 * the Documentos entity
//	 * 
//	 * @param documento
//	 *            Documentos Bean with the criteria values to filter by.
//	 * @return Map created with two keys key query stores the sql query syntax
//	 *         key params stores the parameter values to be used in the
//	 *         condition sentence.
//	 */
//	// CHECKSTYLE:OFF CyclomaticComplexity - Generación de código de UDA
//	private Map<String, ?> getWhereMap(DocumentoRegVPP documento) {
//
//		StringBuilder where = new StringBuilder(
//				DocumentosDaoImpl.STRING_BUILDER_INIT);
//		List<Object> params = new ArrayList<Object>();
//
//		Map<String, Object> mapWhere = new HashMap<String, Object>();
//		mapWhere.put("query", where);
//		mapWhere.put("params", params);
//
//		return mapWhere;
//	}

	// CHECKSTYLE:ON CyclomaticComplexity - Generación de código de UDA

	/**
	 * Returns a map with the needed value to create the conditions to filter by
	 * the Documentos entity
	 * 
	 * @param documento
	 *            Documentos Bean with the criteria values to filter by.
	 * @param startsWith
	 *            Boolean
	 * @return Map created with two keys key query stores the sql query syntax
	 *         key params stores the parameter values to be used in the
	 *         condition sentence.
	 */
	// CHECKSTYLE:OFF CyclomaticComplexity - Generación de código de UDA
	private Map<String, Object> getWhereLikeMap(DocumentoRegVPP documento,
			Boolean startsWith) {

		StringBuilder where = new StringBuilder(
				DocumentosDaoImpl.STRING_BUILDER_INIT);
		List<Object> params = new ArrayList<Object>();
		params.add(documento.getIdElemRelac());
		Map<String, Object> mapWhere = new HashMap<String, Object>();
		mapWhere.put("query", where);
		mapWhere.put("params", params);

		return mapWhere;
	}

	/**
	 * StringBuilder initilization value
	 */
	public static final int STRING_BUILDER_INIT = 4096;
//
//	@Override
//	public void remove(Documento documento) {
//		// TODO Auto-generated method stub
//		
//	}
}
