package com.ejie.ad74a.service;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.CreationHelper;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.odftoolkit.odfdom.doc.OdfSpreadsheetDocument;
import org.odftoolkit.odfdom.doc.table.OdfTable;
import org.odftoolkit.odfdom.doc.table.OdfTableRow;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import com.ejie.ad74a.constantes.Constantes;
import com.ejie.ad74a.dao.AvisosDao;
import com.ejie.ad74a.dao.ExpedientesDao;
import com.ejie.ad74a.dao.RiesgosDao;
import com.ejie.ad74a.dao.RiesgosDaoImpl;
import com.ejie.ad74a.dao.SolicitantesDao;
import com.ejie.ad74a.dao.comun.NoraDao;
import com.ejie.ad74a.dao.comun.NoraDaoImpl;
import com.ejie.ad74a.exceptions.Ad74aException;
import com.ejie.ad74a.exceptions.ErrorField;
import com.ejie.ad74a.exceptions.ManagerErrors;
import com.ejie.ad74a.exceptions.ValidatorException;
import com.ejie.ad74a.model.Avisos;
import com.ejie.ad74a.model.Ayuntamientos;
import com.ejie.ad74a.model.BeneficiarioWS;
import com.ejie.ad74a.model.Cuentas;
import com.ejie.ad74a.model.Direcciones;
import com.ejie.ad74a.model.DocuCuentas;
import com.ejie.ad74a.model.MaestroActores;
import com.ejie.ad74a.model.MaestroSubtiposAviso;
import com.ejie.ad74a.model.MaestroTiposAviso;
import com.ejie.ad74a.model.Riesgos;
import com.ejie.ad74a.model.Solicitantes;
import com.ejie.ad74a.model.Soporte;
import com.ejie.ad74a.model.comun.Portal;
import com.ejie.ad74a.util.comun.UtilLiterales;
import com.ejie.ad74a.util.comun.UtilSeguridad;
import com.ejie.ad74a.util.comun.Utilidades;
import com.ejie.ad74a.util.comun.UtilidadesFecha;
import com.ejie.ad74a.util.constantes.ActoresEnum;
import com.ejie.ad74a.util.constantes.SubTipoAvisoEnum;
import com.ejie.ad74a.util.constantes.TipoAvisoEnum;
import com.ejie.ad74a.util.constantes.modelos.TipoSolicitantes;
import com.ejie.ad74a.util.pid.Ad74aPIDws;
import com.ejie.ad74a.util.pif.Ad74aPIFws;
import com.ejie.ad74a.validacion.SolicitantesValidator;
import com.ejie.ad74a.ws.client.platea.service.PlateaService;
import com.ejie.x38.dto.JerarquiaDto;
import com.ejie.x38.dto.TableRequestDto;
import com.ejie.x38.dto.TableResponseDto;
import com.ejie.x38.dto.TableRowDto;
import com.ejie.x38.util.DateTimeManager;
import com.lowagie.text.Document;
import com.lowagie.text.Phrase;
import com.lowagie.text.pdf.PdfPCell;
import com.lowagie.text.pdf.PdfPTable;
import com.lowagie.text.pdf.PdfWriter;

/**
 * SolicitantesServiceImpl generated by UDA, 10-feb-2023 12:13:20.
 * 
 * @author UDA
 */

@Service(value = "solicitantesService")
public class SolicitantesServiceImpl implements SolicitantesService {

	@Autowired
	private SolicitantesDao solicitantesDao;

	@Autowired
	private AvisosDao avisosDao;

	@Autowired
	private RiesgosDao riesgosDao;

	@Autowired
	private DireccionesService direccionesService;

	@Autowired
	private PerfilesService perfilesService;

	@Autowired
	private ValoracionesService valoracionesService;

	@Autowired
	private CuentasService cuentasService;

	@Autowired
	private PlateaService plateaService;

	@Autowired
	private SolicitantesValidator solicitantesValidator;

	@Autowired
	private Ad74aPIDws pidWS;

	@Autowired
	private DocuCuentasService docuCuentasService;
	@Autowired
	private RiesgosService riesgosService;
	@Autowired
	private SoporteService soporteService;
	@Autowired
	private ExpedientesDao expedienteDao;

	@Autowired
	private AyuntamientosService ayuntamientosService;
	@Autowired
	private NoraDao noraDao;

	@Resource
	private ReloadableResourceBundleMessageSource appMessageSource;

	/**
	 * Inserts a single row in the Solicitantes table.
	 *
	 * @param solicitantes Solicitantes
	 * @return Solicitantes
	 */
	@Transactional(rollbackFor = Throwable.class)
	public Solicitantes add(Solicitantes solicitantes) {
		// Recuperamos el usuario
		solicitantes.setUsuAlta01(UtilSeguridad.getUsuarioConectado().getUserName());
		return this.solicitantesDao.add(solicitantes);
	}

	/**
	 * Updates a single row in the Solicitantes table.
	 *
	 * @param solicitantes Solicitantes
	 * @return Solicitantes
	 */
	@Transactional(rollbackFor = Throwable.class)
	public Solicitantes update(Solicitantes solicitantes) {

		if (TipoSolicitantes.PantallaOrigen.TAB_DATOS_BENEFICIARIOS
				.equalsIgnoreCase(solicitantes.getPantallaOrigen())) {

			List<ErrorField> errores = new ArrayList<ErrorField>();
			solicitantesValidator.validateSolicitante(errores, solicitantes);

			if (errores.size() != Constantes.CERO) {
				throw new ValidatorException(errores);
			}

			// Recuperamos el usuario
			solicitantes.setUsuModif01(UtilSeguridad.getUsuarioConectado().getUserName());

			Direcciones direccioneBBDD = this.direccionesService.find(new Direcciones(solicitantes.getIdDireccion01()));

			if (Boolean.TRUE.equals(this.solicitantesValidator.validateCambiosDireccion(direccioneBBDD,
					solicitantes.getDirecciones(), TipoSolicitantes.PantallaOrigen.TAB_DATOS_BENEFICIARIOS))) {
				direccioneBBDD.setfHistorico06(Calendar.getInstance().getTime());
				direccioneBBDD.setUsuHistorico06(UtilSeguridad.getUsuarioConectado().getNif());
				this.direccionesService.update(direccioneBBDD);
				solicitantes.getDirecciones().setUsuAlta06(UtilSeguridad.getUsuarioConectado().getNif());
				solicitantes.getDirecciones().setIdSolicitante06(solicitantes.getIdSolicitante01());
				solicitantes.setDirecciones(this.direccionesService.add(solicitantes.getDirecciones()));
			}

			Cuentas cuentasBBDD = this.cuentasService.find(new Cuentas(solicitantes.getIdCuenta01()));

			if (Boolean.TRUE.equals(this.solicitantesValidator.validateCambiosCuenta(cuentasBBDD,
					solicitantes.getCuentas(), TipoSolicitantes.PantallaOrigen.TAB_DATOS_BENEFICIARIOS))) {
				cuentasBBDD.setfHistorico05(Calendar.getInstance().getTime());
				cuentasBBDD.setUsuHistorico05(UtilSeguridad.getUsuarioConectado().getNif());
				this.cuentasService.update(cuentasBBDD);
				solicitantes.setCuentas(this.cuentasService.add(solicitantes.getCuentas()));
			}

			this.perfilesService.updateBloquePerfilesPisosTut(solicitantes.getPerfiles());
			return this.solicitantesDao.updateTabDatosBeneficiarios(solicitantes);
		} else if (TipoSolicitantes.PantallaOrigen.TAB_PRESTACION_SERVICIO
				.equalsIgnoreCase(solicitantes.getPantallaOrigen())) {
			// Primero vamos a comprobar si tiene o no un riesgo
			if (solicitantes.getRiesgo() != null) {
				// En caso de que tenga un riesgo tenemos que mirar si el alto riesgo ha
				// cambiado respecto a la BD para pasarlo a historico
				Riesgos riesgoDB = this.riesgosService.find(solicitantes.getRiesgo());

				if (riesgoDB != null && riesgoDB.getAltoRiesgo21() != solicitantes.getRiesgo().getAltoRiesgo21()) {
					this.riesgosDao.updateHistorico(solicitantes.getRiesgo());
					solicitantes.getRiesgo().setIdSolicitante21(solicitantes.getIdSolicitante01());
					this.riesgosDao.add(solicitantes.getRiesgo());
					Avisos aviso = new Avisos();
					aviso.setMaestroTiposAviso(new MaestroTiposAviso(TipoAvisoEnum.OTROS.getIdLongValue()));
					aviso.setMaestroSubtiposAviso(new MaestroSubtiposAviso(SubTipoAvisoEnum.ALTRIES.getIdLongValue()));
					aviso.setIdSolicitante20(solicitantes.getIdSolicitante01());
					aviso.setMaestroActoresByOrigen20(new MaestroActores(ActoresEnum.PS.getId()));
					aviso.setMaestroActoresByDestino20(new MaestroActores(ActoresEnum.DSS.getId()));
					aviso.setUsuAlta20(com.ejie.ad74a.constantes.Constantes.COD_APLICACION);
					this.avisosDao.add(aviso);
				} else {
					solicitantes.getRiesgo().setIdSolicitante21(solicitantes.getIdSolicitante01());
					solicitantes.getRiesgo().setAltoRiesgo21(solicitantes.getRiesgo().getAltoRiesgo21());
					this.riesgosDao.add(solicitantes.getRiesgo());
				}
			} else {
				// Si no lo tiene lo crearemos
				Riesgos nuevoRiesgo = solicitantes.getRiesgo();
				this.riesgosService.add(nuevoRiesgo);
			}
			if (solicitantes.getSoporte() != null) {
				Soporte soporteDB = this.soporteService.find(solicitantes.getSoporte());
				if (soporteDB != null
						&& soporteDB.getIdNivelSoporte22() != solicitantes.getSoporte().getIdNivelSoporte22()) {
					this.soporteService.updateHistorico(soporteDB);
					solicitantes.getSoporte().setIdSolicitante22(solicitantes.getIdSolicitante01());
					this.soporteService.add(solicitantes.getSoporte());
				} else {
					solicitantes.getSoporte().setIdSolicitante22(solicitantes.getIdSolicitante01());
					this.soporteService.add(solicitantes.getSoporte());
				}
			} else {
				solicitantes.getSoporte().setIdSolicitante22(solicitantes.getIdSolicitante01());
				this.soporteService.add(solicitantes.getSoporte());
			}
			// this.valoracionesService.updateTabPrestacionServicio(solicitantes.getValoraciones());
			// this.solicitantesDao.updateTabPrestacionServicio(solicitantes);
			return solicitantes;
		}
		return this.solicitantesDao.update(solicitantes);
	}

	/**
	 * Finds a single row in the Solicitantes table.
	 *
	 * @param solicitantes Solicitantes
	 * @return Solicitantes
	 */
	public Solicitantes find(Solicitantes solicitantes) {
		return (Solicitantes) this.solicitantesDao.find(solicitantes);
	}

	@Override
	public Solicitantes findPrestacionServicio(Solicitantes solicitantes) {
		return (Solicitantes) this.solicitantesDao.findPrestacionServicio(solicitantes);
	}

	public Solicitantes findPorDni(Solicitantes solicitantes) {
		return (Solicitantes) this.solicitantesDao.findPorDni(solicitantes);
	}

	/**
	 * Deletes a single row in the Solicitantes table.
	 *
	 * @param solicitantes Solicitantes
	 */
	@Transactional(rollbackFor = Throwable.class)
	public void remove(Solicitantes solicitantes) {
		this.solicitantesDao.remove(solicitantes);
	}

	/**
	 * Finds a list of rows in the Solicitantes table.
	 *
	 * @param solicitantes    Solicitantes
	 * @param tableRequestDto TableRequestDto
	 * @return List<Solicitantes>
	 */
	public List<Solicitantes> findAll(Solicitantes solicitantes, TableRequestDto tableRequestDto) {
		return (List<Solicitantes>) this.solicitantesDao.findAll(solicitantes, tableRequestDto);
	}

	/**
	 * Finds rows in the Solicitantes table using like.
	 *
	 * @param solicitantes    Solicitantes
	 * @param tableRequestDto TableRequestDto
	 * @param startsWith      Boolean
	 * @return List<Solicitantes>
	 */
	public List<Solicitantes> findAllLike(Solicitantes solicitantes, TableRequestDto tableRequestDto,
			Boolean startsWith, Boolean isSSB, Ayuntamientos ayuntamiento) {
		return (List<Solicitantes>) this.solicitantesDao.findAllLike(solicitantes, tableRequestDto, startsWith, isSSB,
				ayuntamiento);
	}

	public List<Solicitantes> findAllLike(Solicitantes solicitantes, TableRequestDto tableRequestDto,
			Boolean startsWith) {
		return (List<Solicitantes>) this.solicitantesDao.findAllLike(solicitantes, tableRequestDto, startsWith);
	}

	@Override
	public List<Solicitantes> findAllLikeBeneficiarios(Solicitantes solicitantes, TableRequestDto tableRequestDto,
			Boolean startsWith) {
		return (List<Solicitantes>) this.solicitantesDao.findAllLikeBeneficiarios(solicitantes, tableRequestDto,
				startsWith);

	}

	@Override
	public List<Solicitantes> obtenerDatosBeneficiario(Solicitantes solicitantes, TableRequestDto tableRequestDto,
			Boolean startsWith) {
		return (List<Solicitantes>) this.solicitantesDao.obtenerDatosBeneficiario(solicitantes, tableRequestDto,
				startsWith);

	}

	/*
	 * OPERACIONES RUP_TABLE
	 */

	/**
	 * Removes rows from the Solicitantes table.
	 *
	 * @param tableRequestDto TableRequestDto
	 */
	public void removeMultiple(TableRequestDto tableRequestDto) {
		this.solicitantesDao.removeMultiple(tableRequestDto);
	}

	/**
	 * Filter method in the Solicitantes table.
	 *
	 * @param filterSolicitantes Solicitantes
	 * @param tableRequestDto    TableRequestDto
	 * @param startsWith         Boolean
	 * @return TableResponseDto<Solicitantes>
	 */
	public TableResponseDto<Solicitantes> filter(Solicitantes filterSolicitantes, TableRequestDto tableRequestDto,
			Boolean startsWith, Boolean isDSS, Ayuntamientos ayuntamiento) {
		List<Solicitantes> listaSolicitantes = this.solicitantesDao.findAllLike(filterSolicitantes, tableRequestDto,
				false, isDSS, ayuntamiento);

		Long recordNum = this.solicitantesDao.findAllLikeCount(
				filterSolicitantes != null ? filterSolicitantes : new Solicitantes(), false, isDSS, ayuntamiento);

		TableResponseDto<Solicitantes> tableResponseDto = new TableResponseDto<Solicitantes>(tableRequestDto, recordNum,
				listaSolicitantes);

		if (tableRequestDto.getMultiselection().getSelectedIds() != null) {
			List<TableRowDto<Solicitantes>> reorderSelection = this.solicitantesDao.reorderSelection(filterSolicitantes,
					tableRequestDto, startsWith);
			tableResponseDto.setReorderedSelection(reorderSelection);
			tableResponseDto.addAdditionalParam("reorderedSelection", reorderSelection);
			tableResponseDto.addAdditionalParam("selectedAll", tableRequestDto.getMultiselection().getSelectedAll());
		}
		if (tableRequestDto.getSeeker().getSelectedIds() != null) {
			tableRequestDto.setMultiselection(tableRequestDto.getSeeker());
			List<TableRowDto<Solicitantes>> reorderSeeker = this.solicitantesDao.reorderSelection(filterSolicitantes,
					tableRequestDto, startsWith);
			tableResponseDto.setReorderedSeeker(reorderSeeker);
			tableResponseDto.addAdditionalParam("reorderedSeeker", reorderSeeker);
		}
		return tableResponseDto;
	}

	/**
	 * Searches rows in the Solicitantes table.
	 *
	 * @param filterSolicitantes Solicitantes
	 * @param searchSolicitantes Solicitantes
	 * @param tableRequestDto    TableRequestDto
	 * @param startsWith         Boolean
	 * @return List<TableRowDto<Solicitantes>>
	 */
	public List<TableRowDto<Solicitantes>> search(Solicitantes filterSolicitantes, Solicitantes searchSolicitantes,
			TableRequestDto tableRequestDto, Boolean startsWith) {
		return this.solicitantesDao.search(filterSolicitantes, searchSolicitantes, tableRequestDto, startsWith);
	}

	/**
	 * Reorder the selection made in Solicitantes table.
	 *
	 * @param filterSolicitantes Solicitantes
	 * @param tableRequestDto    TableRequestDto
	 * @param startsWith         Boolean
	 * @return Object
	 */
	public Object reorderSelection(Solicitantes filterSolicitantes, TableRequestDto tableRequestDto,
			Boolean startsWith) {
		return this.solicitantesDao.reorderSelection(filterSolicitantes, tableRequestDto, startsWith);
	}

	/*
	 * OPERACIONES RUP_TABLE JERARQUIA
	 */

	/**
	 * Finder method in the hierarchical the Solicitantes table.
	 *
	 * @param filterSolicitantes Solicitantes
	 * @param tableRequestDto    TableRequestDto
	 * @param startsWith         Boolean
	 * @return TableResponseDto<JerarquiaDto<Solicitantes>>
	 */
	public TableResponseDto<JerarquiaDto<Solicitantes>> jerarquia(Solicitantes filterSolicitantes,
			TableRequestDto tableRequestDto, Boolean startsWith) {
		List<JerarquiaDto<Solicitantes>> listaSolicitantes = this.solicitantesDao
				.findAllLikeJerarquia(filterSolicitantes, tableRequestDto);
		Long recordNum = this.solicitantesDao.findAllLikeCountJerarquia(filterSolicitantes, tableRequestDto);
		return new TableResponseDto<JerarquiaDto<Solicitantes>>(tableRequestDto, recordNum, listaSolicitantes);
	}

	/**
	 * Finder method for siblings rows in the hierarchical the Solicitantes table.
	 *
	 * @param filterSolicitantes Solicitantes
	 * @param tableRequestDto    TableRequestDto
	 * @return TableResponseDto<JerarquiaDto<Solicitantes>>
	 */
	public TableResponseDto<JerarquiaDto<Solicitantes>> jerarquiaChildren(Solicitantes filterSolicitantes,
			TableRequestDto tableRequestDto) {
		TableResponseDto<JerarquiaDto<Solicitantes>> tableResponseDto = new TableResponseDto<JerarquiaDto<Solicitantes>>();
		tableResponseDto.addAdditionalParam(TableResponseDto.CHILDREN,
				this.solicitantesDao.findAllChild(filterSolicitantes, tableRequestDto));
		return tableResponseDto;
	}

	/**
	 * Exporta Datos al clipBoard
	 *
	 */
	public List<Solicitantes> getMultiple(Solicitantes filterSolicitantes, TableRequestDto tableRequestDto,
			Boolean startsWith) {
		return this.solicitantesDao.getMultiple(filterSolicitantes, tableRequestDto, startsWith);
	}

	/**
	 * Devuelve un fichero en el formato deseado que contiene los datos exportados
	 * de la tabla.
	 *
	 * @param filterSolicitantes Solicitantes
	 * @param columns            String[]
	 * @param fileName           String
	 * @param sheetTitle         String
	 * @param reportsParams      ArrayList<?>
	 * @param tableRequestDto    TableRequestDto
	 * @param request            HttpServletRequest
	 * @param response           HttpServletResponse
	 */
	@Override
	public void generateReport(Solicitantes filterSolicitantes, String[] columns, String[] columnsName, String fileName,
			String sheetTitle, TableRequestDto tableRequestDto, HttpServletRequest request,
			HttpServletResponse response) {
		// Accede a la DB para recuperar datos
		List<Solicitantes> filteredData = getDataForReports(filterSolicitantes, tableRequestDto, request);
		String extension = null;

		// Comprobar si las siguientes variables estan vacias, en caso de estarlo se las
		// asigna un valor generico
		fileName = (fileName != null && !fileName.isEmpty()) ? fileName : "report";
		sheetTitle = (sheetTitle != null && !sheetTitle.isEmpty()) ? sheetTitle : Solicitantes.class.getSimpleName();

		// Obtener el formato de fecha especifico del locale
		SimpleDateFormat formatter = DateTimeManager.getDateTimeFormat(request.getLocale());

		// Cuando no se definen columnas porque se quieren obtener todas
		if (columns == null) {
			Field[] fields = Solicitantes.class.getDeclaredFields();
			ArrayList<String> tempColumns = new ArrayList<String>();

			for (int i = 0; i < fields.length; i++) {
				try {
					String methodName = fields[i].getName();
					methodName = methodName.substring(0, 1).toUpperCase() + methodName.substring(1);
					Solicitantes.class.getMethod("get" + methodName);
					tempColumns.add(fields[i].getName());
				} catch (NoSuchMethodException e) {
					e.printStackTrace();
				}

			}
			columns = tempColumns.toArray(new String[0]);
		}

		ArrayUtils.removeElement(columns, "altoRiesgo");
		List<String> columnList = new ArrayList<>(Arrays.asList(columns));
		columnList.remove(0);
		columns = columnList.toArray(new String[0]);

		// si no se definen el nombre de las columnas , se dejan las de por defecto.
		if (columnsName == null) {
			columnsName = columns;
		}

		String servletPath = request.getServletPath();
		String reportType = null;
		if (servletPath.contains("/") && (servletPath.lastIndexOf("/") + 1 != servletPath.length())) {
			reportType = servletPath.substring(servletPath.lastIndexOf("/") + 1, servletPath.length());
		} else {
			reportType = servletPath.substring(0, servletPath.length());
		}

		if (reportType.equals("xlsReport")) {
			extension = ".xls";
			generateExcelReport(filteredData, columns, columnsName, fileName, sheetTitle, extension, formatter,
					response);
		} else if (reportType.equals("xlsxReport")) {
			extension = ".xlsx";
			generateExcelReport(filteredData, columns, columnsName, fileName, sheetTitle, extension, formatter,
					response);
		} else if (reportType.equals("pdfReport")) {
			extension = ".pdf";
			generatePDFReport(filteredData, columns, columnsName, fileName, response);
		} else if (reportType.equals("odsReport")) {
			extension = ".ods";
			generateODSReport(filteredData, columns, columnsName, fileName, sheetTitle, response);
		} else if (reportType.equals("csvReport")) {
			extension = ".csv";
			// Obtener idioma
			String language = request.getLocale().getLanguage();
			generateCSVReport(filteredData, columns, columnsName, fileName, sheetTitle, language, response);
		}

	}

	/**
	 * Devuelve un fichero excel que contiene los datos exportados de la tabla.
	 *
	 * @param filteredData List<Solicitantes>
	 * @param columns      String[]
	 * @param fileName     String
	 * @param sheetTitle   String
	 * @param extension    String
	 * @param formatter    SimpleDateFormat
	 * @param response     HttpServletResponse
	 */
	private void generateExcelReport(List<Solicitantes> filteredData, String[] columns, String[] columnsName,
			String fileName, String sheetTitle, String extension, SimpleDateFormat formatter,
			HttpServletResponse response) {
		try {
			// Creacion del Excel
			Workbook workbook = null;
			if (extension == ".xlsx") {
				workbook = new XSSFWorkbook();
				response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
			} else {
				workbook = new HSSFWorkbook();
				response.setContentType("application/vnd.ms-excel");
			}

			response.setHeader("Content-Disposition", "attachment; filename=" + fileName + extension + "");

			// Creacion de una hoja y asignacion de su nombre
			Sheet sheet = workbook.createSheet(sheetTitle);

			// Se crea una fuente para estilizar las cabeceras
			Font headerFont = workbook.createFont();
			headerFont.setBold(true);
			headerFont.setFontHeightInPoints((short) 12);

			// Se crea un CellStyle con la fuente
			CellStyle headerCellStyle = workbook.createCellStyle();
			headerCellStyle.setAlignment(HorizontalAlignment.CENTER);
			headerCellStyle.setFont(headerFont);

			// Inicializar contador de filas
			int rowNumber = 0;

			// Se crea la fila para insertar los titulos de las columnas
			Row row = sheet.createRow(rowNumber++);

			// Añadir titulos
			for (int i = 0; i < columnsName.length; i++) {
				Cell cell = row.createCell(i);
				cell.setCellValue(
						UtilLiterales.getLiteralMessage(appMessageSource, "excelReportLiteral." + columnsName[i]));
				cell.setCellStyle(headerCellStyle);
			}

			// CreationHelper ayudara a mantener la compatibilidad del DataFormat tanto si
			// se crea un .xls como un .xlsx
			CreationHelper createHelper = workbook.getCreationHelper();

			// Se crea un CellStyle para añadir el formateador de fechas
			CellStyle dateCellStyle = workbook.createCellStyle();
			dateCellStyle.setDataFormat(createHelper.createDataFormat().getFormat(formatter.toPattern()));

			// Añadir datos
			for (Solicitantes rowSolicitantes : filteredData) {
				int cellNumber = 0;
				row = sheet.createRow(rowNumber++);

				// Se iteran las columnas y se insertan los datos respetando el orden que tenian
				// las columnas en la tabla
				for (String column : columns) {
					Cell cellSolicitantes = row.createCell(cellNumber++);
					cellSolicitantes.setCellValue(getCellValue(column, rowSolicitantes));
				}
			}

			// Se adapta el ancho de las columnas al contenido
			for (int i = 0; i < columns.length; i++) {
				sheet.autoSizeColumn(i);
			}

			// Se añade el fichero excel al response
			workbook.write(response.getOutputStream());
			workbook.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Devuelve un fichero pdf que contiene los datos exportados de la tabla.
	 *
	 * @param filteredData List<Solicitantes>
	 * @param columns      String[]
	 * @param fileName     String
	 * @param response     HttpServletResponse
	 */
	private void generatePDFReport(List<Solicitantes> filteredData, String[] columns, String[] columnsName,
			String fileName, HttpServletResponse response) {
		try {
			// Se añade el fichero excel al response y se añade el contenido
			response.setHeader("Content-Disposition", "attachment; filename=" + fileName + ".pdf");
			response.setContentType("application/pdf");

			Document document = new Document();
			// Se añade el fichero pdf al response
			PdfWriter.getInstance(document, response.getOutputStream());

			document.open();

			PdfPTable table = new PdfPTable(columns.length);

			for (String column : columnsName) {
				PdfPCell header = new PdfPCell();
				header.setBorderWidth(2);
				header.setPhrase(new Phrase(column));
				table.addCell(header);
			}

			// Añadir datos
			for (Solicitantes rowSolicitantes : filteredData) {
				// Se iteran las columnas y se insertan los datos respetando el orden que tenian
				// las columnas en la tabla
				for (String column : columns) {
					table.addCell(getCellValue(column, rowSolicitantes));
				}
			}

			document.add(table);
			document.close();

		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Devuelve un fichero ods que contiene los datos exportados de la tabla.
	 *
	 * @param filteredData List<Solicitantes>
	 * @param columns      String[]
	 * @param fileName     String
	 * @param sheetTitle   String
	 * @param response     HttpServletResponse
	 */
	private void generateODSReport(List<Solicitantes> filteredData, String[] columns, String[] columnsName,
			String fileName, String sheetTitle, HttpServletResponse response) {
		try {
			// Se añade el fichero ods al response y se añade el contenido
			response.setHeader("Content-Disposition", "attachment; filename=" + fileName + ".ods");
			response.setContentType("application/vnd.oasis.opendocument.spreadsheet");

			OdfSpreadsheetDocument ods = OdfSpreadsheetDocument.newSpreadsheetDocument();
			ods.getOrCreateDocumentStyles();

			// Hay que eliminar la hoja que se genera por defecto
			ods.getTableByName("Sheet1").remove();

			// Hoja nueva
			OdfTable table = OdfTable.newTable(ods, filteredData.size() + 1, columns.length);
			table.setTableName(sheetTitle);

			// Inicializar contador de filas
			int rowNumber = 0;

			// Cabeceras
			OdfTableRow row = table.getRowByIndex(rowNumber++);
			for (int i = 0; i < columnsName.length; i++) {
				row.getCellByIndex(i).setStringValue(columnsName[i]);
			}

			// Añadir datos
			for (Solicitantes rowSolicitantes : filteredData) {
				row = table.getRowByIndex(rowNumber++);
				int cellNumber = 0;

				// Se iteran las columnas y se insertan los datos respetando el orden que tenian
				// las columnas en la tabla
				for (String column : columns) {
					row.getCellByIndex(cellNumber++).setStringValue(getCellValue(column, rowSolicitantes));
				}
			}

			// Se añade el fichero ods al response
			ods.save(response.getOutputStream());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Devuelve un fichero csv que contiene los datos exportados de la tabla.
	 *
	 * @param filteredData List<Solicitantes>
	 * @param columns      String[]
	 * @param fileName     String
	 * @param sheetTitle   String
	 * @param language     String
	 * @param response     HttpServletResponse
	 */
	private void generateCSVReport(List<Solicitantes> filteredData, String[] columns, String[] columnsName,
			String fileName, String sheetTitle, String language, HttpServletResponse response) {
		try {
			// Se añade el fichero excel al response y se añade el contenido
			response.setHeader("Content-Disposition", "attachment; filename=" + fileName + ".csv");
			response.setContentType("text/csv");

			// Separador de campos dependiendo del idioma
			String separator = ";";
			if (language.equals("en")) {
				separator = ",";
			}

			// Se añade el fichero csv al response
			OutputStream out = response.getOutputStream();
			// Añadir titulos
			boolean addTitles = true;

			// Añadir datos
			for (Solicitantes rowSolicitantes : filteredData) {
				int cellNumber = 1;
				StringBuilder columnsTitles = new StringBuilder();
				StringBuilder row = new StringBuilder();

				// Se iteran las columnas y se insertan los datos respetando el orden que tenian
				// las columnas en la tabla
				for (String column : columns) {
					String cellValue = getCellValue(column, rowSolicitantes);

					if (cellNumber < columns.length) {
						if (addTitles) {
							columnsTitles.append("\"");
							columnsTitles.append(column);
							columnsTitles.append("\"");
							columnsTitles.append(separator);
						}
						row.append("\"");
						row.append(cellValue);
						row.append("\"");
						row.append(separator);
						cellNumber++;
					} else {
						if (addTitles) {
							columnsTitles.append("\"");
							columnsTitles.append(column);
							columnsTitles.append("\"\n");
						}
						row.append("\"");
						row.append(cellValue);
						row.append("\"\n");
					}
				}

				if (addTitles) {
					out.write(columnsTitles.toString().getBytes());
					addTitles = false;
				}
				out.write(row.toString().getBytes());
			}
			out.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Devuelve los datos recuperados de la DB.
	 *
	 * @param filterSolicitantes Solicitantes
	 * @param tableRequestDto    TableRequestDto
	 */
	private List<Solicitantes> getDataForReports(Solicitantes filterSolicitantes, TableRequestDto tableRequestDto,
			HttpServletRequest request) {
		Boolean isSSB = UtilSeguridad.isSSB(request);
		HttpSession sesion = request.getSession();
		Long ayuntamientoSeleccionado = (Long) sesion
				.getAttribute(com.ejie.ad74a.util.constantes.Constantes.VARCONTEXTO_AYUNTAMIENTO_SELECCIONADO);
		Ayuntamientos ayuntamiento = this.ayuntamientosService.find(new Ayuntamientos(ayuntamientoSeleccionado));
		if (tableRequestDto.getMultiselection().getSelectedAll()
				&& tableRequestDto.getMultiselection().getSelectedIds().isEmpty()) {
			if (filterSolicitantes != null) {
				return this.solicitantesDao.findAllLikeExcel(filterSolicitantes, null, false, isSSB, ayuntamiento);
			} else {
				return this.solicitantesDao.findAllLikeExcel(filterSolicitantes, null, false, isSSB, ayuntamiento);
			}
		} else {
			return this.solicitantesDao.findAllLikeExcel(filterSolicitantes, null, false, isSSB, ayuntamiento);
		}
	}

	/**
	 * Obtiene los valores de las celdas.
	 *
	 * @param column          String
	 * @param rowSolicitantes Solicitantes
	 */
	private String getCellValue(String column, Solicitantes rowSolicitantes) {
		String cellValue = "";
		try {
			if ("nombre01".equalsIgnoreCase(column)) {
				cellValue = rowSolicitantes.getNombreCompleto();
			} else if ("beneficiario01".equalsIgnoreCase(column) && rowSolicitantes.getBeneficiario01() == 1
					&& null != rowSolicitantes.getfBaja01()) {
				cellValue = UtilLiterales.getLiteralMessage(appMessageSource, LocaleContextHolder.getLocale(),
						"comun.no");
			} else if ("beneficiario01".equalsIgnoreCase(column) && rowSolicitantes.getBeneficiario01() == 1
					&& null == rowSolicitantes.getfBaja01()) {
				cellValue = UtilLiterales.getLiteralMessage(appMessageSource, LocaleContextHolder.getLocale(),
						"comun.si");
			} else {
				cellValue = BeanUtils.getProperty(rowSolicitantes, column) != null
						? BeanUtils.getProperty(rowSolicitantes, column)
						: "";
			}

		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		}

		return cellValue;
	}

	@Override
	public Boolean isSolAlta(Solicitantes filter) {
		return this.solicitantesDao.isSolAlta(filter);
	}

	/**
	 * Updates a single row in the Solicitantes table.
	 *
	 * @param solicitantes Solicitantes
	 * @return Solicitantes
	 */
	@Override
	public Solicitantes updateIdiomaServicio(Solicitantes solicitantes) {
		return this.solicitantesDao.updateIdiomaServicio(solicitantes);
	}

	/**
	 * Filter method in the Solicitantes table.
	 *
	 * @param filterSolicitantes Solicitantes
	 * @param tableRequestDto    TableRequestDto
	 * @param startsWith         Boolean
	 * @return TableResponseDto<Solicitantes>
	 */
	public TableResponseDto<Solicitantes> filterSameDirectionBenefActivo(Solicitantes filterSolicitantes,
			TableRequestDto tableRequestDto, Boolean startsWith) {

		// recogemos el identificador del solicitante
		Solicitantes sol = this.solicitantesDao.find(new Solicitantes(filterSolicitantes.getIdSolicitante01()));

		sol.setDirecciones(this.direccionesService.find(sol.getDirecciones()));
		if (sol.getDirecciones() != null && sol.getDirecciones().getIdPortal06() != null) {
			Portal port = new Portal();
			port.setCodPortal(sol.getDirecciones().getIdPortal06());
			port = this.noraDao.findPortal(port);
			sol.getDirecciones().setPortal06(port.getPortal());
		}

		List<Solicitantes> listaSolicitantes;
		Long recordNum;
		try {
			listaSolicitantes = this.solicitantesDao.findSameDirectionBenefActivo(sol.getDirecciones(),
					sol.getIdentificador01(), sol.getTlfnoServicio01(), tableRequestDto);
			recordNum = this.solicitantesDao.findSameDirectionBenefActivoCount(sol.getDirecciones(),
					sol.getIdentificador01(), sol.getTlfnoServicio01());
		} catch (Ad74aException exc) {
			listaSolicitantes = new ArrayList<Solicitantes>();
			recordNum = 0l;
		}

		TableResponseDto<Solicitantes> tableResponseDto = new TableResponseDto<Solicitantes>(tableRequestDto, recordNum,
				listaSolicitantes);

		if (tableRequestDto.getMultiselection().getSelectedIds() != null) {
			List<TableRowDto<Solicitantes>> reorderSelection = this.solicitantesDao.reorderSelection(filterSolicitantes,
					tableRequestDto, startsWith);
			tableResponseDto.setReorderedSelection(reorderSelection);
			tableResponseDto.addAdditionalParam("reorderedSelection", reorderSelection);
			tableResponseDto.addAdditionalParam("selectedAll", tableRequestDto.getMultiselection().getSelectedAll());
		}
		if (tableRequestDto.getSeeker().getSelectedIds() != null) {
			tableRequestDto.setMultiselection(tableRequestDto.getSeeker());
			List<TableRowDto<Solicitantes>> reorderSeeker = this.solicitantesDao.reorderSelection(filterSolicitantes,
					tableRequestDto, startsWith);
			tableResponseDto.setReorderedSeeker(reorderSeeker);
			tableResponseDto.addAdditionalParam("reorderedSeeker", reorderSeeker);
		}
		return tableResponseDto;
	}

	/**
	 * Filter method in the Solicitantes table.
	 *
	 * @param filterSolicitantes Solicitantes
	 * @param tableRequestDto    TableRequestDto
	 * @param startsWith         Boolean
	 * @return TableResponseDto<Solicitantes>
	 */
	public List<Solicitantes> tieneAsociados(Solicitantes filterSolicitantes) {

		// recogemos el identificador del solicitante
		Solicitantes sol = this.solicitantesDao.find(new Solicitantes(filterSolicitantes.getIdSolicitante01()));

		sol.setDirecciones(this.direccionesService.find(sol.getDirecciones()));
		if (sol.getDirecciones() != null && sol.getDirecciones().getIdPortal06() != null) {
			Portal port = new Portal();
			port.setCodPortal(sol.getDirecciones().getIdPortal06());
			port = this.noraDao.findPortal(port);
			sol.getDirecciones().setPortal06(port.getPortal());
		}
		List<Solicitantes> listaSolicitantes;
		Long recordNum;
		try {
			listaSolicitantes = this.solicitantesDao.findSameDirectionBenefActivo(sol.getDirecciones(),
					sol.getIdentificador01(), sol.getTlfnoServicio01(), null);
			recordNum = this.solicitantesDao.findSameDirectionBenefActivoCount(sol.getDirecciones(),
					sol.getIdentificador01(), sol.getTlfnoServicio01());
		} catch (Ad74aException exc) {
			listaSolicitantes = new ArrayList<Solicitantes>();
			recordNum = 0l;
		}

		return listaSolicitantes;
	}

	@Override
	public boolean tieneExpedienteIniciado(Long idSolicitante01) {
		return this.solicitantesDao.tieneExpedienteInciado(idSolicitante01);
	}

	/**
	 * Funcion para modificar la persona interesada. Llama a platea.
	 *
	 * @param solicitantes Solicitantes
	 * @return Solicitantes
	 */
	@Override
	@Transactional(rollbackFor = Throwable.class)
	public Solicitantes updatePersonaInteresada(Solicitantes solicitantes, Solicitantes representante,
			String idExpediente) {
		// Validar si hay que modificar la persona interesada
		List<ErrorField> errores = new ArrayList<ErrorField>();
		solicitantesValidator.validatePersonaInteresada(errores, solicitantes);

		if (errores.size() != Constantes.CERO) {
			throw new ValidatorException(errores);
		}

		// Modificar la base de datos
		solicitantes.setUsuModif01(UtilSeguridad.getUsuarioConectado().getUserName());
		solicitantes.setfModif01(UtilidadesFecha.getFechaSistema());
		solicitantes = this.solicitantesDao.updatePersonaInteresada(solicitantes);

		// Si existe modificamos al representante
		if (representante != null) {
			representante.setUsuModif01(UtilSeguridad.getUsuarioConectado().getUserName());
			representante.setfModif01(UtilidadesFecha.getFechaSistema());
			representante = this.expedienteDao.updateRepresentante(representante, idExpediente);
		}
		// Llamada a platea
		ArrayList<String> folderIDList = new ArrayList<String>();
		folderIDList.add(solicitantes.getExpSolicitud().getFolderId00());
		this.plateaService.updateTitularFolders(folderIDList, solicitantes, representante);

		return solicitantes;
	}

	@Override
	public Solicitantes updateDatosExpediente(Solicitantes solicitantes) {
		return this.solicitantesDao.updateDatosExpediente(solicitantes);
	}

	@Override
	public Solicitantes updateFNac(Solicitantes solicitantes) {
		return this.solicitantesDao.updateFNac(solicitantes);
	}

//	@Transactional(rollbackFor = Throwable.class)
//	public Solicitantes aportar(Solicitantes solicitantes, MultipartFile documento_cuenta, String idTipoDoc36) {
//
//		// Llama a un filtercomplete de avisos que devuelve una lista
//		List<Solicitantes> solicitantesList = this.solicitantesDao.findAllLike(solicitantes, null, false);
//		Solicitantes solicitantesAux = new Solicitantes();
//
//		if (!solicitantesList.isEmpty()) {
//			solicitantesAux = solicitantesList.get(solicitantesList.size() - 1);
//		}
//
//		// Subir a PIF
//		String rutaPif = "";
//		try {
//			InputStream inputStream = new BufferedInputStream(documento_cuenta.getInputStream());
//			String pathDocumento = com.ejie.ad74a.constantes.Constantes.RUTA_PID
//					.concat(documento_cuenta.getOriginalFilename());
//			rutaPif = Ad74aPIFws.getInstance().uploadDocument(inputStream, pathDocumento, true);
//			inputStream.close();
//		} catch (Exception e) {
//			ManagerErrors.manageException(e, "ERROR subiendo documento aportar al aviso a PIF");
//		}
//
//		// Subir a PID
//		String nombreFichero = documento_cuenta.getOriginalFilename();
//		String usuarioAlta = Constantes.COD_APLICACION;
//		String aplic = Constantes.COD_APLICACION;
//		String rutaPid = pidWS.put(aplic, nombreFichero, rutaPif, usuarioAlta);
//
//		// Insertar documento
//		DocuCuentas docuCuentas = new DocuCuentas();
//		try {
//			docuCuentas.setUsuAlta87(UtilSeguridad.getUsuarioConectado().getUserName());
//			docuCuentas.setRuta87(rutaPid);
//			docuCuentas.setNombreDoc87(documento_cuenta.getOriginalFilename());
//			docuCuentas.setIdCuenta87(solicitantes.getCuentas().getIdCuenta05());
//			docuCuentas.setIdTipoDoc87(Long.valueOf(idTipoDoc36));
//			docuCuentas.setExtension87(getExtension(documento_cuenta.getOriginalFilename()));
//			this.docuCuentasService.add(docuCuentas);
//		} catch (Exception e) {
//			ManagerErrors.manageException(e, "ERROR generando el documento cuenta");
//
//		}
//		return solicitantesAux;
//	}

	@Transactional(rollbackFor = Throwable.class)
	public Solicitantes aportar(Solicitantes solicitantes, MultipartFile documento_cuenta, String idTipoDoc36,
			HttpServletRequest request) {

		// Llama a un filtercomplete de avisos que devuelve una lista

		Solicitantes solicitantesAux = new Solicitantes();
		HttpSession sesion = request.getSession();
		List<DocuCuentas> docuCuentasList = new ArrayList<DocuCuentas>();
		if (sesion.getAttribute(com.ejie.ad74a.constantes.Constantes.VARCONTEXTO_DOCUCUENTAS) != null) {
			docuCuentasList = (List<DocuCuentas>) sesion.getAttribute(Constantes.VARCONTEXTO_DOCUCUENTAS);
		}
		String rutaPif = "";
		try {
			InputStream inputStream = new BufferedInputStream(documento_cuenta.getInputStream());
			String pathDocumento = com.ejie.ad74a.constantes.Constantes.RUTA_PID
					.concat(Constantes.COD_APLICACION.toLowerCase()).concat("/")
					.concat(documento_cuenta.getOriginalFilename());
			rutaPif = Ad74aPIFws.getInstance().uploadDocument(inputStream, pathDocumento, true);
		} catch (Exception e) {
			ManagerErrors.manageException(e, "ERROR subiendo documento aportar al aviso a PIF");
		}
		// Subir a PID
		String nombreFichero = documento_cuenta.getOriginalFilename();
		String usuarioAlta = Constantes.COD_APLICACION;
		String aplic = Constantes.COD_APLICACION;
		String rutaPid = pidWS.put(aplic, nombreFichero, rutaPif, usuarioAlta);

		// Insertar documento
		DocuCuentas docuCuentas = new DocuCuentas();
		try {
			docuCuentas.setUsuAlta87(UtilSeguridad.getUsuarioConectado().getUserName());
			docuCuentas.setNombreDoc87(documento_cuenta.getOriginalFilename());
			docuCuentas.setIdCuenta87(solicitantes.getIdCuenta01());
			docuCuentas.setIdTipoDoc87(Long.valueOf(idTipoDoc36));
			docuCuentas.setRuta87(rutaPid);
			docuCuentas.setExtension87(getExtension(documento_cuenta.getOriginalFilename()));
			docuCuentas.setFAlta87(new Date());
			docuCuentasList.add(docuCuentas);
			sesion.setAttribute(Constantes.VARCONTEXTO_DOCUCUENTAS, docuCuentasList);
		} catch (Exception e) {
			ManagerErrors.manageException(e, "ERROR generando el documento cuenta");

		}
		return solicitantesAux;
	}

	private String getExtension(String nombreDoc) {
		String ext = "";
		if (!Utilidades.esBlancoNulo(nombreDoc)) {
			String[] part = nombreDoc.split("\\.");
			if (part.length > 1) {
				ext = part[1];
			}
		}
		return ext;
	}

	@Override
	public Solicitantes updateNoReintentar(Solicitantes solicitantes) {
		return this.solicitantesDao.updateNoReintentar(solicitantes);
	}

	public Solicitantes anularBaja(Solicitantes solicitantes) {

		return this.solicitantesDao.anularBaja(solicitantes);
	}

	/**
	 * Función que devuelve si un solicitante es un beneficiario activo o no
	 * Específica para el WS de Beneficiario
	 * 
	 * @param solicitantes
	 * @return
	 */
	@Override
	public Solicitantes esBeneficiario(Solicitantes solicitantes) {
		return this.solicitantesDao.esBeneficiario(solicitantes);
	}

	/**
	 * Función que devuelve los datos de un solicitante Específica para el WS de
	 * Beneficiario
	 * 
	 * @param solicitantes
	 * @return
	 */
	public BeneficiarioWS datosBeneficiario(Solicitantes solicitantes) {
		return this.solicitantesDao.datosBeneficiario(solicitantes);
	}

	/*
	 * Se mete en una nueva transacción pq se usa justo después de enviar la baja al
	 * PS y puede dar timeout
	 */
	@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Throwable.class)
	public Solicitantes updateBajaPSBeneficiario(Solicitantes solicitantes) {
		return this.solicitantesDao.updateBajaPSBeneficiario(solicitantes);
	}

}
