package com.ejie.u74a.dao;

import java.math.BigDecimal;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import javax.annotation.Resource;
import javax.sql.DataSource;

import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import com.ejie.u74a.model.Articulo;
import com.ejie.u74a.model.CriteriosBusqueda;
import com.ejie.u74a.model.FormaEnvio;
import com.ejie.u74a.model.Idioma;
import com.ejie.u74a.model.Material;
import com.ejie.u74a.model.TemaMaterial;
import com.ejie.u74a.model.TipoMaterial;
import com.ejie.u74a.util.Constants;
import com.ejie.u74a.util.Utilidades;

/**
 * Interfaz de acceso a la base de datos para obtener datos de artículos. Tabla: ARTICULO
 * 
 *  
 */
@Repository()
@Transactional()
public class ArticuloDaoImpl implements ArticuloDao {

	private JdbcTemplate jdbcTemplate;

	@Resource(name = "appMessageSource")
	private ReloadableResourceBundleMessageSource messageSource;

	public static final HashMap<String, String> ORDER_BY_ARTICULO = new HashMap<String, String>();
	static {
		ORDER_BY_ARTICULO.put("tipoMaterial.abreviaturaCastellano", "TIM.ABREVIATURA");
		ORDER_BY_ARTICULO.put("tipoMaterial.abreviaturaEuskera", "TIM.ABREVIATURA_EU");
		ORDER_BY_ARTICULO.put("material.tituloCastellano", "M.TITULO");
		ORDER_BY_ARTICULO.put("material.tituloEuskera", "M.TITULO_EU");
		ORDER_BY_ARTICULO.put("descripcionIdioma", "IDIOMA");
		ORDER_BY_ARTICULO.put("tema.nombreCastellano", "TM.NOTEMAMAT");
		ORDER_BY_ARTICULO.put("tema.nombreEuskera", "TM.NOTEMAMAT_EU");
		ORDER_BY_ARTICULO.put("stock", "A.STOCK");
		ORDER_BY_ARTICULO.put("stockMin", "A.STOCKMIN");

		ORDER_BY_ARTICULO.put("fechaInventario", "A.FEINVENTARIO");
		ORDER_BY_ARTICULO.put("fechaEntradaSalida", "RE.FECHAES");

	}

	private StringBuilder fromFindByCriteria = new StringBuilder(
			" FROM  MATERIAL M LEFT JOIN ARTICULO A ON A.COMATERIAL = M.COMATERIAL ")
			.append("LEFT JOIN TIPOMATERIAL TIM ON M.COTIPOMAT = TIM.COTIPOMAT LEFT JOIN TEMAMATERIAL TM ON M.COTEMAMAT = TM.COTEMAMAT ");

	/**
	 * Clase que especifica el mapeo de las columnas de la tabla ARTICULO con los objetos de la clase Articulo
	 */
	public static final class ArticuloRowMapper implements RowMapper<Articulo> {

		/*
		 * (non-Javadoc)
		 * 
		 * @see org.springframework.jdbc.core.RowMapper#mapRow(java.sql.ResultSet, int)
		 */
		@Override()
		public Articulo mapRow(ResultSet rs, int rowNum) throws SQLException {
			Articulo articulo = new Articulo();

			articulo.setCodArticulo(rs.getInt("COARTICULO"));
			articulo.setCodMaterial(rs.getInt("COMATERIAL"));
			articulo.setCodigoFormaEnvio(rs.getInt("COFORMAENVIO"));
			articulo.setModoPeticion(rs.getString("MODOPETICION"));
			articulo.setNumMinimo(rs.getInt("NUMINIMO"));
			articulo.setNumEjemplares(rs.getInt("NUEJEMPLARES"));
			articulo.setUrlPortada(rs.getString("URLPORTADA"));
			articulo.setUrlPDF(rs.getString("URLPDF"));
			articulo.setUrlPortada(rs.getString("URLPORTADA"));
			articulo.setFechaModificacion(rs.getDate("FECHAMOD"));
			articulo.setCodigoUsuarioModificador(rs.getInt("COUSUARIOMOD"));
			articulo.setFechaInventario(rs.getDate("FEINVENTARIO"));
			articulo.setFechaEntradaSalida(rs.getDate("FEES"));
			articulo.setCodigoAlmacen(rs.getString("COALMACEN"));
			articulo.setDescatalogado(rs.getString("DESCATALOGADO"));
			articulo.setStock(rs.getInt("STOCK"));
			articulo.setStockMin(rs.getInt("STOCKMIN"));

			return articulo;
		}
	}

	/**
	 * Clase que especifica el mapeo de las columnas de la tabla ARTICULO con los objetos de la clase Articulo con
	 * información extra.
	 */
	private static final class ArticuloExtraRowMapper implements RowMapper<Articulo> {

		/*
		 * (non-Javadoc)
		 * 
		 * @see org.springframework.jdbc.core.RowMapper#mapRow(java.sql.ResultSet, int)
		 */
		@Override()
		public Articulo mapRow(ResultSet rs, int rowNum) throws SQLException {
			Articulo articulo = new Articulo();

			articulo.setCodArticulo(rs.getInt("COARTICULO"));
			articulo.setCodMaterial(rs.getInt("COMATERIAL"));
			articulo.setCodigoFormaEnvio(rs.getInt("COFORMAENVIO"));
			articulo.setModoPeticion(rs.getString("MODOPETICION"));
			articulo.setNumMinimo(rs.getInt("NUMINIMO"));
			articulo.setNumEjemplares(rs.getInt("NUEJEMPLARES"));
			articulo.setUrlPortada(rs.getString("URLPORTADA"));
			articulo.setUrlPDF(rs.getString("URLPDF"));

			// De momento no recupero datos extras de tipoMaterial, solo los que se van a mostrar por
			// pantalla
			TipoMaterial tipoMaterial = new TipoMaterial();
			tipoMaterial.setCodigo(rs.getInt("COTIPOMAT"));
			tipoMaterial.setAbreviaturaCastellano(rs.getString("ABREVIATURA"));
			tipoMaterial.setAbreviaturaEuskera(rs.getString("ABREVIATURA_EU"));

			articulo.setTipoMaterial(tipoMaterial);

			Material material = new Material();
			material.setCodigoMaterial(rs.getInt("COMATERIAL"));
			material.setTituloCastellano(rs.getString("TITULO"));
			material.setTituloEuskera(rs.getString("TITULO_EU"));

			articulo.setMaterial(material);

			FormaEnvio formaEnvio = new FormaEnvio();
			formaEnvio.setCodigoFormaEnvio(rs.getInt("COFORMAENVIO"));
			formaEnvio.setCodigoOrigen(rs.getString("COORIGEN"));
			formaEnvio.setNombreFormaEnvioCastellano(rs.getString("NOFORMAENVIO"));
			formaEnvio.setNombreFormaEnvioEuskera(rs.getString("NOFORMAENVIO_EU"));

			articulo.setFormaEnvio(formaEnvio);

			return articulo;
		}
	}

	/**
	 * Clase que especifica el mapeo de las columnas de la tabla ARTICULO con los objetos de la clase Articulo con
	 * información extra sin la forma de envío.
	 */
	private static final class ArticuloExtraSinFormaEnvioRowMapper implements RowMapper<Articulo> {

		/*
		 * (non-Javadoc)
		 * 
		 * @see org.springframework.jdbc.core.RowMapper#mapRow(java.sql.ResultSet, int)
		 */
		@Override()
		public Articulo mapRow(ResultSet rs, int rowNum) throws SQLException {
			Articulo articulo = new Articulo();

			articulo.setCodArticulo(rs.getInt("COARTICULO"));
			articulo.setCodMaterial(rs.getInt("COMATERIAL"));
			articulo.setCodigoFormaEnvio(rs.getInt("COFORMAENVIO"));
			articulo.setModoPeticion(rs.getString("MODOPETICION"));
			articulo.setNumMinimo(rs.getInt("NUMINIMO"));
			articulo.setNumEjemplares(rs.getInt("NUEJEMPLARES"));
			articulo.setUrlPortada(rs.getString("URLPORTADA"));
			articulo.setUrlPDF(rs.getString("URLPDF"));

			articulo.setStock(rs.getInt("STOCK"));
			articulo.setStockMin(rs.getInt("STOCKMIN"));
			articulo.setFechaInventario(rs.getDate("FEINVENTARIO"));
			articulo.setCodigoAlmacen(rs.getString("COALMACEN"));

			// De momento no recupero datos extras de tipoMaterial, solo los que se van a mostrar por
			// pantalla
			TipoMaterial tipoMaterial = new TipoMaterial();
			tipoMaterial.setCodigo(rs.getInt("COTIPOMAT"));
			tipoMaterial.setAbreviaturaCastellano(rs.getString("ABREVIATURA"));
			tipoMaterial.setAbreviaturaEuskera(rs.getString("ABREVIATURA_EU"));
			tipoMaterial.setNombreCastellano(rs.getString("NOTIPOMAT"));
			tipoMaterial.setNombreEuskera(rs.getString("NOTIPOMAT_EU"));

			articulo.setTipoMaterial(tipoMaterial);

			Material material = new Material();
			material.setCodigoMaterial(rs.getInt("COMATERIAL"));
			material.setTituloCastellano(rs.getString("TITULO"));
			material.setTituloEuskera(rs.getString("TITULO_EU"));

			articulo.setMaterial(material);

			// TODO Añadir campos aunque no se necesiten?

			return articulo;
		}
	}

	/**
	 * Clase que especifica el mapeo de las columnas de la tabla ARTICULO con los objetos de la clase Articulo, con los
	 * datos a mostrar en la lista de búsqueda
	 */
	private static final class ArticuloListRowMapper implements RowMapper<Articulo> {

		/*
		 * (non-Javadoc)
		 * 
		 * @see org.springframework.jdbc.core.RowMapper#mapRow(java.sql.ResultSet, int)
		 */
		@Override()
		public Articulo mapRow(ResultSet rs, int rowNum) throws SQLException {
			Articulo articulo = new Articulo();

			articulo.setCodArticulo(rs.getInt("COARTICULO"));
			articulo.setCodMaterial(rs.getInt("COMATERIAL"));
			articulo.setUrlPortada(rs.getString("URLPORTADA"));
			articulo.setUrlPDF(rs.getString("URLPDF"));
			articulo.setStock(rs.getInt("STOCK"));
			articulo.setStockMin(rs.getInt("STOCKMIN"));
			articulo.setDescatalogado(rs.getString("DESCATALOGADO"));

			// De momento no recupero datos extras de tipoMaterial, solo los que se van a mostrar por
			// pantalla
			TipoMaterial tipoMaterial = new TipoMaterial();
			tipoMaterial.setCodigo(rs.getInt("COTIPOMAT"));
			tipoMaterial.setAbreviaturaCastellano(rs.getString("ABREVIATURA"));
			tipoMaterial.setAbreviaturaEuskera(rs.getString("ABREVIATURA_EU"));

			articulo.setTipoMaterial(tipoMaterial);

			Material material = new Material();
			material.setCodigoMaterial(rs.getInt("COMATERIAL"));
			material.setTituloCastellano(rs.getString("TITULO"));
			material.setTituloEuskera(rs.getString("TITULO_EU"));
			material.setDescatalogado(rs.getString("MATERIAL_DESCATALOGADO"));

			articulo.setMaterial(material);

			TemaMaterial temaMaterial = new TemaMaterial();
			temaMaterial.setCodigoTemaMaterial(rs.getInt("COTEMAMAT"));
			temaMaterial.setCodigoTipoMaterial(rs.getInt("COTIPOMAT"));
			temaMaterial.setNombreCastellano(rs.getString("NOTEMAMAT"));
			temaMaterial.setNombreEuskera(rs.getString("NOTEMAMAT_EU"));

			articulo.setTema(temaMaterial);

			articulo.setDescripcionIdioma(rs.getString("IDIOMA"));

			return articulo;
		}
	}

	/**
	 * Clase que especifica el mapeo de las columnas de la tabla ARTICULO con los objetos de la clase Articulo, con los
	 * datos a mostrar en la lista de búsqueda
	 */
	private static final class ArticuloListGSRowMapper implements RowMapper<Articulo> {

		/*
		 * (non-Javadoc)
		 * 
		 * @see org.springframework.jdbc.core.RowMapper#mapRow(java.sql.ResultSet, int)
		 */
		@Override()
		public Articulo mapRow(ResultSet rs, int rowNum) throws SQLException {
			Articulo articulo = new Articulo();

			articulo.setCodArticulo(rs.getInt("COARTICULO"));
			articulo.setCodMaterial(rs.getInt("COMATERIAL"));
			articulo.setUrlPortada(rs.getString("URLPORTADA"));
			articulo.setUrlPDF(rs.getString("URLPDF"));
			articulo.setStock(rs.getInt("STOCK"));
			articulo.setStockMin(rs.getInt("STOCKMIN"));
			articulo.setFechaInventario(rs.getDate("FEINVENTARIO"));
			articulo.setFechaEntradaSalida(rs.getDate("FECHAES"));
			articulo.setDescripcionIdioma(rs.getString("IDIOMA"));

			// De momento no recupero datos extras de tipoMaterial, solo los que se van a mostrar por
			// pantalla
			TipoMaterial tipoMaterial = new TipoMaterial();
			tipoMaterial.setCodigo(rs.getInt("COTIPOMAT"));
			tipoMaterial.setAbreviaturaCastellano(rs.getString("ABREVIATURA"));
			tipoMaterial.setAbreviaturaEuskera(rs.getString("ABREVIATURA_EU"));

			articulo.setTipoMaterial(tipoMaterial);

			Material material = new Material();
			material.setCodigoMaterial(rs.getInt("COMATERIAL"));
			material.setTituloCastellano(rs.getString("TITULO"));
			material.setTituloEuskera(rs.getString("TITULO_EU"));

			articulo.setMaterial(material);

			TemaMaterial temaMaterial = new TemaMaterial();
			temaMaterial.setCodigoTemaMaterial(rs.getInt("COTEMAMAT"));
			temaMaterial.setCodigoTipoMaterial(rs.getInt("COTIPOMAT"));
			temaMaterial.setNombreCastellano(rs.getString("NOTEMAMAT"));
			temaMaterial.setNombreEuskera(rs.getString("NOTEMAMAT_EU"));

			articulo.setTema(temaMaterial);

			return articulo;
		}
	}

	/**
	 * Método para establecer el datasource.
	 * 
	 * @param dataSource DataSource
	 */
	@Resource()
	public void setDataSource(DataSource dataSource) {
		this.jdbcTemplate = new JdbcTemplate(dataSource);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.ejie.u74a.dao.ArticuloDao#findByListaCodigos(List<Integer>)
	 */
	@Transactional(readOnly = true)
	@Override()
	public List<Articulo> findByListaCodigos(List<Integer> codigoArticulosLst) { // findByMaterial(List<Integer>
																					// codigoMaterialesLst) {

		// De momento no recupero datos extras de material ni tipoMaterial, solo los que se van a mostrar por
		// pantalla
		StringBuilder query = new StringBuilder(
				"SELECT A.COARTICULO, A.COMATERIAL,A.COFORMAENVIO, A.MODOPETICION, A.NUMINIMO, A.NUEJEMPLARES, A.URLPORTADA, A.URLPDF, M.TITULO, M.TITULO_EU, TIM.COTIPOMAT")
				.append(", TIM.ABREVIATURA, TIM.ABREVIATURA_EU, FE.NOFORMAENVIO, FE.NOFORMAENVIO_EU, FE.COORIGEN, GET_DESC_IDIOMA(A.COARTICULO, '"
						+ LocaleContextHolder.getLocale() + "') IDIOMA ")
				.append(" FROM ARTICULO A INNER JOIN  MATERIAL M ON A.COMATERIAL = M.COMATERIAL LEFT JOIN TIPOMATERIAL TIM ON M.COTIPOMAT = TIM.COTIPOMAT")
				.append(" LEFT JOIN FORMAENVIO FE ON A.COFORMAENVIO = FE.COFORMAENVIO ");
		StringBuilder whereSql = new StringBuilder();

		if (codigoArticulosLst != null && codigoArticulosLst.size() > 0) {
			whereSql.append(" WHERE A.COARTICULO IN ("); // COMATERIAL cuando se buscaba por materiales
			StringBuilder args = new StringBuilder("");
			for (int i = 0; i < codigoArticulosLst.size(); i++) {
				args.append((!"".equals(args.toString()) ? "," : "")).append("?");
			}
			whereSql.append(args).append(") ");
		}
		query.append(whereSql).append("ORDER BY TITULO");
		if (new Locale("eu").equals(LocaleContextHolder.getLocale())) {
			query.append("_EU"); // Para que ordene por el nombre en euskera
		}

		List<Articulo> articulos;
		if (!"".equals(whereSql)) {
			articulos = this.jdbcTemplate.query(query.toString(), new ArticuloExtraRowMapper(),
					codigoArticulosLst.toArray());
		} else {
			articulos = this.jdbcTemplate.query(query.toString(), new ArticuloExtraRowMapper());
		}

		for (Articulo articulo : articulos) {
			if (articulo.getModoPeticion() != null && !"".equals(articulo.getModoPeticion())) {
				articulo.setDescModoPeticion(this.messageSource.getMessage(
						"articulo.modoPeticion." + articulo.getModoPeticion(), null, LocaleContextHolder.getLocale()));
			}
			actualizaIdiomasEnArticulo(articulo);
		}

		return articulos;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.ejie.u74a.dao.ArticuloDao#findByCriteria(int)
	 */
	@Transactional(readOnly = true)
	@Override()
	public List<Articulo> findByCriteria(CriteriosBusqueda filtro) {

		// De momento no recupero datos extras de material ni tipoMaterial, solo los que se van a mostrar por
		// pantalla
		StringBuilder query = new StringBuilder(
				"SELECT A.COARTICULO, M.COMATERIAL, A.URLPORTADA, A.URLPDF, A.STOCK, A.STOCKMIN, M.TITULO, M.TITULO_EU, TIM.COTIPOMAT, ")
				.append("TIM.ABREVIATURA, TIM.ABREVIATURA_EU, TM.COTEMAMAT,TM.NOTEMAMAT, TM.NOTEMAMAT_EU, A.DESCATALOGADO, M.DESCATALOGADO MATERIAL_DESCATALOGADO, GET_DESC_IDIOMA(A.COARTICULO, '"
						+ LocaleContextHolder.getLocale() + "') IDIOMA ");

		// Where clause & Params
		Map<String, ?> mapaWhere = getWhereMapByCriteria(filtro);
		query.append(mapaWhere.get("query"));
		List<?> argumentos = (List<?>) mapaWhere.get("params");

		// ordeno la tabla por la columna que haya clicado
		if (filtro.getSort() != null && !"".equals(filtro.getSort())) {
			query.append(" ORDER BY ").append(ORDER_BY_ARTICULO.get(filtro.getSort())).append(" ")
					.append(filtro.getOrder());
		} else {
			query.append("ORDER BY TITULO");
			if (new Locale("eu").equals(LocaleContextHolder.getLocale())) {
				query.append("_EU"); // Para que ordene por el nombre en euskera
			}
		}

		// Se obtiene la consulta final incluyendo la paginación.
		StringBuilder queryFinal = new StringBuilder(Utilidades.getPaginationQuery(query, filtro.getOffset(),
				filtro.getLimit(), null, null).toString());

		List<Articulo> articulos = this.jdbcTemplate.query(queryFinal.toString(), new ArticuloListRowMapper(),
				argumentos.toArray());

		for (Articulo articulo : articulos) {
			if (articulo.getModoPeticion() != null && !"".equals(articulo.getModoPeticion())) {
				articulo.setDescModoPeticion(this.messageSource.getMessage(
						"articulo.modoPeticion." + articulo.getModoPeticion(), null, LocaleContextHolder.getLocale()));
			}

			if (Constants.MULTILINGUE.equals(articulo.getDescripcionIdioma())) {
				articulo.setDescripcionIdioma(this.messageSource.getMessage("comun.multilingue", null,
						LocaleContextHolder.getLocale()));
			} else if (Constants.SIN_IDIOMA.equals(articulo.getDescripcionIdioma())) {
				articulo.setDescripcionIdioma(this.messageSource.getMessage("comun.sinIdioma", null,
						LocaleContextHolder.getLocale()));
			}

			// actualizaIdiomasEnArticulo(articulo);
		}

		return articulos;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.ejie.u74a.dao.ArticuloDao#findByCountCriteria(int)
	 */
	@Transactional(readOnly = true)
	@Override()
	public int findByCountCriteria(CriteriosBusqueda filtro) {
		StringBuilder query = new StringBuilder("SELECT COUNT(M.COMATERIAL) ");

		// Where clause & Params
		Map<String, ?> mapaWhere = getWhereMapByCriteria(filtro);
		query.append(mapaWhere.get("query"));
		List<?> argumentos = (List<?>) mapaWhere.get("params");

		return this.jdbcTemplate.queryForObject(query.toString(), argumentos.toArray(), Integer.class);
	}

	/**
	 * Devuelve un mapa con la query a ejecutar y los parámetros de búsqueda
	 * 
	 * @param filtro datos del filtro
	 * @return query a ejecutar
	 */
	private Map<String, ?> getWhereMapByCriteria(CriteriosBusqueda filtro) {
		StringBuilder query = new StringBuilder();
		List<Object> argumentos = new ArrayList<Object>();

		query.append(" FROM  MATERIAL M LEFT JOIN ARTICULO A ON A.COMATERIAL = M.COMATERIAL ");
		query.append("LEFT JOIN TIPOMATERIAL TIM ON M.COTIPOMAT = TIM.COTIPOMAT LEFT JOIN TEMAMATERIAL TM ON M.COTEMAMAT = TM.COTEMAMAT ");

		// que el stock actual sea mayor que el stock mínimo

		query.append(" WHERE 1=1");

		// si hay que controlar que el stock esté por encima del mínimo
		if ("S".equals(filtro.getControlStock())) {
			query.append(" AND A.STOCK > A.STOCKMIN ");
		}

		if (filtro.getTexto() != null && !"".equals(filtro.getTexto())) {
			query.append(" AND (UPPER(TITULO) LIKE UPPER(?) or UPPER(TITULO_EU) LIKE UPPER(?)) ");
			argumentos.add("%" + filtro.getTexto() + "%");
			argumentos.add("%" + filtro.getTexto() + "%");
		}
		if (filtro.getTipoMaterial() != null && !"".equals(filtro.getTipoMaterial())) {
			query.append(" AND TIM.COTIPOMAT = ? ");
			argumentos.add(filtro.getTipoMaterial());
		}
		if (filtro.getTema() != null && !"".equals(filtro.getTema())) {
			query.append(" AND")
					.append(" (TM.COTEMAMAT = ? or exists (SELECT MT.COTEMAMAT FROM MATERIALTEMA MT WHERE MT.COMATERIAL=A.COMATERIAL AND MT.COTEMAMAT = ?)) ");
			argumentos.add(filtro.getTema());
			argumentos.add(filtro.getTema());
		}
		if (filtro.getIdioma() != null && !"".equals(filtro.getIdioma())) {
			query.append(" AND")
					.append(" exists (SELECT IA.COIDIOMA FROM IDIOMASARTICULO IA WHERE IA.COARTICULO=A.COARTICULO AND IA.COIDIOMA = ?) ");
			argumentos.add(filtro.getIdioma());
		}
		if (filtro.getDescatalogado() != null && !"".equals(filtro.getDescatalogado())) {
			query.append(" AND A.DESCATALOGADO = ? ");
			argumentos.add(filtro.getDescatalogado());
		}

		Map<String, Object> mapWhere = new HashMap<String, Object>();
		mapWhere.put("query", query);
		mapWhere.put("params", argumentos);

		return mapWhere;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.ejie.u74a.dao.TemaMaterialDao#findByTipoMaterial(int)
	 */
	@Transactional(readOnly = true)
	@Override()
	public List<Articulo> findByCriteriaGestionStock(CriteriosBusqueda filtro) {

		// De momento no recupero datos extras de material ni tipoMaterial, solo los que se van a mostrar por
		// pantalla
		StringBuilder query = new StringBuilder(
				"SELECT DISTINCT(A.COARTICULO), M.COMATERIAL, A.URLPORTADA, A.URLPDF, A.STOCK, A.STOCKMIN, A.FEINVENTARIO, M.TITULO, M.TITULO_EU, TIM.COTIPOMAT, ");
		query.append("TIM.ABREVIATURA, TIM.ABREVIATURA_EU, TM.COTEMAMAT, TM.NOTEMAMAT, TM.NOTEMAMAT_EU, RE.FECHAES, ");
		query.append("GET_DESC_IDIOMA(A.COARTICULO, '" + LocaleContextHolder.getLocale() + "') IDIOMA ");
		query.append(this.fromFindByCriteria);
		query.append(" LEFT JOIN REGISTROES RE ON A.COARTICULO = RE.COARTICULO ");

		StringBuilder whereSql = new StringBuilder();

		List<Object> argumentos = new ArrayList<Object>();

		whereSql.append(" WHERE (RE.FECHAES IS NULL OR RE.FECHAES = (SELECT MAX(FECHAES) FROM REGISTROES RE2 WHERE RE2.COARTICULO = A.COARTICULO ))");

		// Cuidado, si cambia algo del filtro, hay que cambiar en el método findByCountCriteriaGestionStock que devuelve
		// el contador
		// de la consulta para la paginación.
		if (filtro.getTexto() != null && !"".equals(filtro.getTexto())) {
			whereSql.append(" AND (UPPER(TITULO) LIKE UPPER(?) or UPPER(TITULO_EU) LIKE UPPER(?)) ");
			argumentos.add("%" + filtro.getTexto() + "%");
			argumentos.add("%" + filtro.getTexto() + "%");
		}
		if (filtro.getTipoMaterial() != null && !"".equals(filtro.getTipoMaterial())) {
			whereSql.append(" AND TIM.COTIPOMAT = ? ");
			argumentos.add(filtro.getTipoMaterial());
		}
		if (filtro.getTema() != null && !"".equals(filtro.getTema())) {
			whereSql.append(" AND (TM.COTEMAMAT = ? or exists (SELECT MT.COTEMAMAT FROM MATERIALTEMA MT WHERE MT.COMATERIAL=A.COMATERIAL AND MT.COTEMAMAT = ?)) ");
			argumentos.add(filtro.getTema());
			argumentos.add(filtro.getTema());
		}
		if (filtro.getIdioma() != null && !"".equals(filtro.getIdioma())) {
			whereSql.append(" AND exists (SELECT IA.COIDIOMA FROM IDIOMASARTICULO IA WHERE IA.COARTICULO=A.COARTICULO AND IA.COIDIOMA = ?) ");
			argumentos.add(filtro.getIdioma());
		}
		if (filtro.getDescatalogado() != null && !"".equals(filtro.getDescatalogado())) {
			whereSql.append(" AND A.DESCATALOGADO=? ");
			argumentos.add(filtro.getDescatalogado());
		}

		if (filtro.getFecha1() != null) {
			whereSql.append(" AND TRUNC(A.FEINVENTARIO) >= ? ");
			argumentos.add(filtro.getFecha1());
		}
		if (filtro.getFecha2() != null) {
			whereSql.append(" AND TRUNC(A.FEINVENTARIO) <= ? ");
			argumentos.add(filtro.getFecha2());
		}

		if (filtro.getFecha3() != null) {
			whereSql.append(" AND exists (SELECT COARTICULO from REGISTROES R where A.COARTICULO=R.COARTICULO AND TRUNC(FECHAES) >= ? )");
			argumentos.add(filtro.getFecha3());
		}
		if (filtro.getFecha4() != null) {
			whereSql.append(" AND exists (SELECT COARTICULO from REGISTROES R where A.COARTICULO=R.COARTICULO AND TRUNC(FECHAES) <= ? )");
			argumentos.add(filtro.getFecha4());
		}
		if (filtro.getStockMin() != null && !"".equals(filtro.getStockMin())) {
			whereSql.append(" AND A.STOCK >= ? ");
			argumentos.add(filtro.getStockMin());
		}
		if (filtro.getStockMax() != null && !"".equals(filtro.getStockMax())) {
			whereSql.append(" AND A.STOCK <= ? ");
			argumentos.add(filtro.getStockMax());
		}

		query.append(whereSql);
		/*
		 * append("ORDER BY TITULO"); if (new Locale("eu").equals(LocaleContextHolder.getLocale())) {
		 * query.append("_EU"); // Para que ordene por el nombre en euskera }
		 */
		// ordeno la tabla por la columna que haya clicado
		if (filtro.getSort() != null && !"".equals(filtro.getSort())) {
			query.append(" ORDER BY ").append(ORDER_BY_ARTICULO.get(filtro.getSort())).append(" ")
					.append(filtro.getOrder());
		} else {
			query.append("ORDER BY TITULO");
			if (new Locale("eu").equals(LocaleContextHolder.getLocale())) {
				query.append("_EU"); // Para que ordene por el nombre en euskera
			}
		}

		// Se obtiene la consulta final incluyendo la paginación.
		StringBuilder queryFinal = new StringBuilder(Utilidades.getPaginationQuery(query, filtro.getOffset(),
				filtro.getLimit(), null, null).toString());

		List<Articulo> articulos;
		if (!"".equals(whereSql)) {
			articulos = this.jdbcTemplate.query(queryFinal.toString(), new ArticuloListGSRowMapper(),
					argumentos.toArray());
		} else {
			articulos = this.jdbcTemplate.query(queryFinal.toString(), new ArticuloListGSRowMapper());
		}

		for (Articulo articulo : articulos) {
			if (articulo.getModoPeticion() != null && !"".equals(articulo.getModoPeticion())) {
				articulo.setDescModoPeticion(this.messageSource.getMessage(
						"articulo.modoPeticion." + articulo.getModoPeticion(), null, LocaleContextHolder.getLocale()));
			}
			// actualizaIdiomasEnArticulo(articulo);
			if (Constants.MULTILINGUE.equals(articulo.getDescripcionIdioma())) {
				articulo.setDescripcionIdioma(this.messageSource.getMessage("comun.multilingue", null,
						LocaleContextHolder.getLocale()));
			} else if (Constants.SIN_IDIOMA.equals(articulo.getDescripcionIdioma())) {
				articulo.setDescripcionIdioma(this.messageSource.getMessage("comun.sinIdioma", null,
						LocaleContextHolder.getLocale()));
			}
		}

		return articulos;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.ejie.u74a.dao.TemaMaterialDao#findByTipoMaterial(int)
	 */
	@Transactional(readOnly = true)
	@Override()
	public int findByCountCriteriaGestionStock(CriteriosBusqueda filtro) {
		StringBuilder query = new StringBuilder("SELECT COUNT(M.COMATERIAL) ").append(this.fromFindByCriteria).append(
				" LEFT JOIN REGISTROES RE ON A.COARTICULO = RE.COARTICULO ");

		StringBuilder whereSql = new StringBuilder();

		List<Object> argumentos = new ArrayList<Object>();

		whereSql.append(" WHERE (RE.FECHAES IS NULL OR RE.FECHAES = (SELECT MAX(FECHAES) FROM REGISTROES RE2 WHERE RE2.COARTICULO = A.COARTICULO ))");

		// Cuidado, si cambia algo del filtro en el método findByCriteriaGestionStock hay que cambiarlo también aquí.
		if (filtro.getTexto() != null && !"".equals(filtro.getTexto())) {
			whereSql.append(" AND (UPPER(TITULO) LIKE UPPER(?) OR UPPER(TITULO_EU) LIKE UPPER(?)) ");
			argumentos.add("%" + filtro.getTexto() + "%");
			argumentos.add("%" + filtro.getTexto() + "%");
		}
		if (filtro.getTipoMaterial() != null && !"".equals(filtro.getTipoMaterial())) {
			whereSql.append(" AND TIM.COTIPOMAT = ? ");
			argumentos.add(filtro.getTipoMaterial());
		}
		if (filtro.getTema() != null && !"".equals(filtro.getTema())) {
			whereSql.append(" AND (TM.COTEMAMAT = ? or exists (SELECT MT.COTEMAMAT FROM MATERIALTEMA MT WHERE MT.COMATERIAL=A.COMATERIAL AND MT.COTEMAMAT = ?)) ");
			argumentos.add(filtro.getTema());
			argumentos.add(filtro.getTema());
		}
		if (filtro.getIdioma() != null && !"".equals(filtro.getIdioma())) {
			whereSql.append(" AND exists (SELECT IA.COIDIOMA FROM IDIOMASARTICULO IA WHERE IA.COARTICULO=A.COARTICULO AND IA.COIDIOMA = ?) ");
			argumentos.add(filtro.getIdioma());
		}
		if (filtro.getDescatalogado() != null && !"".equals(filtro.getDescatalogado())) {
			whereSql.append(" AND A.DESCATALOGADO=? ");
			argumentos.add(filtro.getDescatalogado());
		}

		if (filtro.getFecha1() != null) {
			whereSql.append(" AND TRUNC(A.FEINVENTARIO) >= ? ");
			argumentos.add(filtro.getFecha1());
		}
		if (filtro.getFecha2() != null) {
			whereSql.append(" AND TRUNC(A.FEINVENTARIO) <= ? ");
			argumentos.add(filtro.getFecha2());
		}

		if (filtro.getFecha3() != null) {
			whereSql.append(" AND exists (SELECT COARTICULO from REGISTROES R where A.COARTICULO=R.COARTICULO AND TRUNC(FECHAES) >= ? )");
			argumentos.add(filtro.getFecha3());
		}
		if (filtro.getFecha4() != null) {
			whereSql.append(" AND exists (SELECT COARTICULO from REGISTROES R where A.COARTICULO=R.COARTICULO AND TRUNC(FECHAES) <= ? )");
			argumentos.add(filtro.getFecha4());
		}
		if (filtro.getStockMin() != null && !"".equals(filtro.getStockMin())) {
			whereSql.append(" AND A.STOCK >= ? ");
			argumentos.add(filtro.getStockMin());
		}
		if (filtro.getStockMax() != null && !"".equals(filtro.getStockMax())) {
			whereSql.append(" AND A.STOCK <= ? ");
			argumentos.add(filtro.getStockMax());
		}
		return this.jdbcTemplate.queryForObject(query.append(whereSql).toString(), argumentos.toArray(), Integer.class);
	}

	/**
	 * Busca los idiomas para el artículo y actualiza en el articulo la descripción correspondiente, si no tiene
	 * idiomas: sin idioma, si tiene un idioma: el nombre del idioma, y si tiene más de un idioma: multilingüe.
	 * 
	 * @param articulo objeto @ link Articulo} a actualizar.
	 */
	@Transactional(readOnly = true)
	@Override()
	public void actualizaIdiomasEnArticulo(Articulo articulo) {
		List<Idioma> idiomasLst = findIdiomasByArticulo(articulo.getCodArticulo());
		articulo.setDescripcionIdioma(getDescripcionIdiomas(idiomasLst));
		articulo.setIdiomasLst(idiomasLst);
	}

	/**
	 * Busca los idiomas para el artículo y actualiza en el articulo la descripción correspondiente, si no tiene
	 * idiomas: sin idioma, si tiene un idioma: el nombre del idioma, y si tiene más de un idioma: multilingüe.
	 * 
	 * @param codArticulo Código del artículo
	 * 
	 * @return descripción de los idiomas del artículo
	 */
	@Transactional(readOnly = true)
	@Override()
	public String getDescripcionIdiomasArticulo(int codArticulo) {
		List<Idioma> idiomasLst = findIdiomasByArticulo(codArticulo);
		return getDescripcionIdiomas(idiomasLst);
	}

	/**
	 * Devuelve la descripción correspondiente para una lista de idiomas, si no tiene idiomas: sin idioma, si tiene un
	 * idioma: el nombre del idioma, y si tiene más de un idioma: multilingüe.
	 * 
	 * @param idiomasLst La lista de idiomas.
	 * @return Devuelve la descripción para la lista de idiomas.
	 */
	private String getDescripcionIdiomas(List<Idioma> idiomasLst) {
		if (idiomasLst != null) {
			if (idiomasLst.size() > 1) {
				return this.messageSource.getMessage("comun.multilingue", null, LocaleContextHolder.getLocale());
			} else if (idiomasLst.size() == 1) {
				if (new Locale("eu").equals(LocaleContextHolder.getLocale())) {
					return idiomasLst.get(0).getNombreEuskera();
				} else {
					return idiomasLst.get(0).getNombreCastellano();
				}
			} else if (idiomasLst.size() == 0) {
				return this.messageSource.getMessage("comun.sinIdioma", null, LocaleContextHolder.getLocale());
			}
		}
		return this.messageSource.getMessage("comun.sinIdioma", null, LocaleContextHolder.getLocale());
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.ejie.u74a.dao.ArticuloDao#getDatosStockArticulo(int)
	 */
	@Override()
	public Articulo getDatosStockArticulo(int codigoArticulo) {

		List<Map<String, Object>> rowsList = this.jdbcTemplate.queryForList(
				"SELECT STOCK, STOCKMIN FROM ARTICULO WHERE COARTICULO=?", new Object[] { codigoArticulo });

		Articulo articulo = new Articulo();
		Map<String, Object> row = rowsList.get(0);
		// for (Map<String, Object> row : rowsList) {

		articulo.setStock(((BigDecimal) row.get("STOCK")).intValue());
		articulo.setStockMin(((BigDecimal) row.get("STOCKMIN")).intValue());
		// }

		return articulo;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.ejie.u74a.dao.ArticuloDao#findIdiomasByArticulo(int)
	 */
	@Override()
	public List<Idioma> findIdiomasByArticulo(int codigoArticulo) {
		StringBuilder query = new StringBuilder(
				"SELECT  I.COIDIOMA, I.NOIDIOMA, I.NOIDIOMA_EU FROM IDIOMAS I INNER JOIN IDIOMASARTICULO IA ON I.COIDIOMA=IA.COIDIOMA AND IA.COARTICULO=? ORDER BY NOIDIOMA");
		if (new Locale("eu").equals(LocaleContextHolder.getLocale())) {
			query.append("_EU"); // Para que ordene por el nombre en euskera
		}
		return this.jdbcTemplate.query(query.toString(), new IdiomaDaoImpl.IdiomaRowMapper(), codigoArticulo);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.ejie.u74a.dao.ArticuloDao#findByCodigo(int)
	 */
	@Transactional(readOnly = true)
	@Override()
	public Articulo findByCodigo(int pCodigoArticulo) {

		StringBuilder query = new StringBuilder(
				"SELECT A.COARTICULO,A.COMATERIAL,A.URLPDF,A.FECHAMOD,A.COUSUARIOMOD,A.COALMACEN,")
				.append("A.DESCATALOGADO,A.STOCKMIN,A.STOCK,A.FEINVENTARIO,A.FEES,A.COFORMAENVIO,A.MODOPETICION,A.NUMINIMO,A.NUEJEMPLARES,A.URLPORTADA ")
				.append("FROM ARTICULO A WHERE A.COARTICULO=?");
		Articulo articulo = this.jdbcTemplate
				.queryForObject(query.toString(), new ArticuloRowMapper(), pCodigoArticulo);
		this.actualizaIdiomasEnArticulo(articulo);

		return articulo;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.ejie.u74a.dao.ArticuloDao#findByCodigoExtra(int)
	 */
	@Transactional(readOnly = true)
	@Override()
	public Articulo findByCodigoExtra(int pCodigoArticulo) {

		// TODO esta se utiliza en gestion de stock y no se necesitan alguno de los campos que se devuelven en la select
		StringBuilder query = new StringBuilder(
				"SELECT A.COARTICULO,A.COMATERIAL,A.URLPDF,A.FECHAMOD,A.COUSUARIOMOD,A.COALMACEN,")
				.append("A.DESCATALOGADO,A.STOCKMIN,A.STOCK,A.FEINVENTARIO,A.FEES,A.COFORMAENVIO,A.MODOPETICION,A.NUMINIMO,A.NUEJEMPLARES,A.URLPORTADA ")
				.append(", M.TITULO, M.TITULO_EU, TIM.COTIPOMAT, TIM.ABREVIATURA, TIM.ABREVIATURA_EU, TIM.NOTIPOMAT, TIM.NOTIPOMAT_EU ")
				.append("FROM ARTICULO A INNER JOIN  MATERIAL M ON A.COMATERIAL = M.COMATERIAL LEFT JOIN TIPOMATERIAL TIM ON M.COTIPOMAT = TIM.COTIPOMAT WHERE A.COARTICULO=?");

		Articulo articulo = this.jdbcTemplate.queryForObject(query.toString(),
				new ArticuloExtraSinFormaEnvioRowMapper(), pCodigoArticulo);
		this.actualizaIdiomasEnArticulo(articulo);

		if (new Locale("eu").equals(LocaleContextHolder.getLocale())) {
			articulo.setDescripcionTitulo(articulo.getMaterial() != null ? articulo.getMaterial().getTituloEuskera()
					: "");
			articulo.setDescripcionTipoMaterial(articulo.getTipoMaterial() != null ? articulo.getTipoMaterial()
					.getNombreEuskera() : "");
		} else {
			articulo.setDescripcionTitulo(articulo.getMaterial() != null ? articulo.getMaterial().getTituloCastellano()
					: "");
			articulo.setDescripcionTipoMaterial(articulo.getTipoMaterial() != null ? articulo.getTipoMaterial()
					.getNombreCastellano() : "");
		}

		return articulo;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.ejie.u74a.dao.ArticuloDao#insertarArticulo(Articulo)
	 */
	@Transactional()
	@Override()
	public Articulo insertarArticulo(Articulo articulo) {
		articulo.setCodArticulo(this.jdbcTemplate.queryForObject("SELECT U74A48Q00.NEXTVAL FROM dual", Integer.class));

		Object[] insertObj = { articulo.getCodArticulo(), articulo.getCodMaterial(), articulo.getUrlPDF(),
				articulo.getFechaModificacion(), articulo.getCodigoUsuarioModificador(), articulo.getCodigoAlmacen(),
				articulo.getDescatalogado(), articulo.getStockMin(), articulo.getStock(),
				articulo.getCodigoFormaEnvio(), articulo.getModoPeticion(), articulo.getNumMinimo(),
				articulo.getNumEjemplares(), articulo.getUrlPortada() };

		this.jdbcTemplate
				.update("INSERT INTO ARTICULO (COARTICULO,COMATERIAL,URLPDF,FECHAMOD,COUSUARIOMOD,COALMACEN,DESCATALOGADO,STOCKMIN,STOCK,COFORMAENVIO,MODOPETICION,NUMINIMO,NUEJEMPLARES,URLPORTADA) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)",
						insertObj);

		return articulo;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.ejie.u74a.dao.ArticuloDao#insertarIdiomaArticulo(int,String)
	 */
	@Transactional()
	@Override()
	public int insertarIdiomaArticulo(int codigoArticulo, String codigoIdioma) {
		Object[] insertObj = { codigoArticulo, codigoIdioma };

		int numAfectadas = this.jdbcTemplate.update("INSERT INTO IDIOMASARTICULO (COARTICULO, COIDIOMA) VALUES (?,?)",
				insertObj);

		return numAfectadas;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.ejie.u74a.dao.ArticuloDao#borrarIdiomasArticulo(int)
	 */
	@Transactional()
	@Override()
	public int borrarIdiomasArticulo(int codigoArticulo) {
		return this.jdbcTemplate.update("DELETE FROM IDIOMASARTICULO WHERE COARTICULO = ?", codigoArticulo);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.ejie.u74a.dao.ArticuloDao#borrarArticulo(int)
	 */
	@Transactional()
	@Override()
	public int borrarArticulo(int codigoArticulo) {
		return this.jdbcTemplate.update("DELETE FROM ARTICULO WHERE COARTICULO = ?", codigoArticulo);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.ejie.u74a.dao.ArticuloDao#actualizarArticulo(Articulo)
	 */
	@Transactional()
	@Override()
	public Articulo actualizarArticulo(Articulo articulo) {
		Object[] updateObj = { articulo.getUrlPDF(), articulo.getFechaModificacion(),
				articulo.getCodigoUsuarioModificador(), articulo.getCodigoAlmacen(), articulo.getDescatalogado(),
				articulo.getStockMin(), articulo.getStock(), articulo.getCodigoFormaEnvio(),
				articulo.getModoPeticion(), articulo.getNumMinimo(), articulo.getNumEjemplares(),
				articulo.getUrlPortada(), articulo.getCodArticulo() };

		this.jdbcTemplate
				.update("UPDATE ARTICULO  SET URLPDF = ?,FECHAMOD = ?,COUSUARIOMOD = ?,COALMACEN = ?,DESCATALOGADO = ?,STOCKMIN = ?,STOCK = ?,COFORMAENVIO = ?,MODOPETICION = ?,NUMINIMO = ?,NUEJEMPLARES = ?,URLPORTADA = ? WHERE COARTICULO = ?",
						updateObj);

		return articulo;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.ejie.u74a.dao.ArticuloDao#actualizarStockArticulo(Articulo)
	 */
	@Transactional()
	@Override()
	public int actualizarStockArticulo(int codigoArticulo, boolean salida, int diferenciaStock,
			int codigoUsuarioModificador, Date fechaModificacion) {
		Object[] updateObj = { diferenciaStock, fechaModificacion, codigoUsuarioModificador, codigoArticulo };

		return this.jdbcTemplate.update("UPDATE ARTICULO  SET STOCK = STOCK" + (salida ? " - " : " + ")
				+ " ?,FECHAMOD = ?,COUSUARIOMOD = ? WHERE COARTICULO = ?", updateObj);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.ejie.u74a.dao.ArticuloDao#actualizarInfoStockArticulo(Articulo)
	 */
	@Transactional()
	@Override()
	public int actualizarInfoStockArticulo(Articulo articulo) {
		Object[] updateObj = { articulo.getCodigoUsuarioModificador(), articulo.getCodigoAlmacen(),
				articulo.getStockMin(), articulo.getStock(), articulo.getCodigoFormaEnvio(),
				articulo.getModoPeticion(), articulo.getNumMinimo(), articulo.getNumEjemplares(),
				articulo.getCodArticulo() };

		return this.jdbcTemplate
				.update("UPDATE ARTICULO SET FECHAMOD = SYSDATE ,COUSUARIOMOD = ?,COALMACEN = ?,STOCKMIN = ?,STOCK = ?,COFORMAENVIO = ?,MODOPETICION = ?,NUMINIMO = ?,NUEJEMPLARES = ?,FEINVENTARIO = SYSDATE WHERE COARTICULO = ?",
						updateObj);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.ejie.u74a.dao.ArticuloDao#comprobarPeticionesActivasExistentes(int)
	 */
	@Transactional(readOnly = true)
	@Override()
	public boolean comprobarPeticionesActivasExistentes(int codigoArticulo) {
		int numPendientes = this.jdbcTemplate.queryForObject(
				"SELECT COUNT(COPETICION) FROM PETICION WHERE COARTICULO=? AND ESTADO=?", new Object[] {
						codigoArticulo, Constants.ESTADO_PENDIENTE }, Integer.class);
		return numPendientes > 0;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.ejie.u74a.dao.ArticuloDao#borrarArticulosDeMaterial(int)
	 */
	@Transactional()
	@Override()
	public int borrarArticulosDeMaterial(int codigoMaterial) {
		return this.jdbcTemplate.update("DELETE FROM ARTICULO WHERE COMATERIAL = ?", codigoMaterial);
	}

}
