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.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;

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

import org.apache.commons.beanutils.BeanUtils;
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.Transactional;
import org.springframework.web.multipart.MultipartFile;

import com.ejie.ad74a.constantes.Constantes;
import com.ejie.ad74a.dao.AvisosDao;
import com.ejie.ad74a.dao.DocumentosInformeDao;
import com.ejie.ad74a.dao.ExpedientesDao;
import com.ejie.ad74a.dao.InformesDao;
import com.ejie.ad74a.dao.comun.NoraDao;
import com.ejie.ad74a.exceptions.ManagerErrors;
import com.ejie.ad74a.model.Avisos;
import com.ejie.ad74a.model.Direcciones;
import com.ejie.ad74a.model.DocTemporales;
import com.ejie.ad74a.model.DocuAvisos;
import com.ejie.ad74a.model.DocumentosInforme;
import com.ejie.ad74a.model.Expedientes;
import com.ejie.ad74a.model.Informes;
import com.ejie.ad74a.model.MaestroActores;
import com.ejie.ad74a.model.MaestroSubtiposAviso;
import com.ejie.ad74a.model.MaestroTiposAviso;
import com.ejie.ad74a.model.Solicitantes;
import com.ejie.ad74a.model.comun.Calle;
import com.ejie.ad74a.model.comun.Localidad;
import com.ejie.ad74a.model.comun.Municipio;
import com.ejie.ad74a.model.comun.Portal;
import com.ejie.ad74a.model.comun.Provincia;
import com.ejie.ad74a.service.documentos.DocumentosService;
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.EstadoInformesEnum;
import com.ejie.ad74a.util.constantes.SubTipoAvisoEnum;
import com.ejie.ad74a.util.constantes.TipoAvisoEnum;
import com.ejie.ad74a.util.constantes.TipoInformesEnum;
import com.ejie.ad74a.util.pid.Ad74aPIDws;
import com.ejie.ad74a.util.pif.Ad74aPIFws;
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;

/**
 * AvisosServiceImpl generated by UDA, 12-may-2023 11:16:38.
 * 
 * @author UDA
 */

@Service(value = "avisosService")
public class AvisosServiceImpl implements AvisosService {

	@Autowired
	private AvisosDao avisosDao;

	@Autowired
	private InformesService informesService;

	@Autowired
	private InformesDao informesDao;

	@Autowired
	private DocumentosInformeDao documentosInformeDao;

	@Autowired
	private ExpedientesDao expedientesDao;

	@Autowired
	private DocumentosService documentosService;

	@Autowired
	private Ad74aPIDws pidWS;

	@Autowired
	private DocuAvisosService docuAvisosService;

	@Autowired
	private NoraDao noraDao;

	@Resource
	private ReloadableResourceBundleMessageSource appMessageSource;

	/**
	 * Inserts a single row in the Avisos table.
	 *
	 * @param avisos Avisos
	 * @return Avisos
	 */
	@Transactional(rollbackFor = Throwable.class)
	public Avisos add(Avisos avisos) {
		return this.avisosDao.add(avisos);
	}

	/**
	 * Updates a single row in the Avisos table.
	 *
	 * @param avisos Avisos
	 * @return Avisos
	 */
	@Transactional(rollbackFor = Throwable.class)
	public Avisos update(Avisos avisos) {
		return this.avisosDao.update(avisos);
	}

	/**
	 * Finds a single row in the Avisos table.
	 *
	 * @param avisos Avisos
	 * @return Avisos
	 */
	public Avisos find(Avisos avisos) {
		Avisos avisofilter = (Avisos) this.avisosDao.find(avisos);
		if (avisofilter.getIdInforme20() != null) {
			avisofilter.setInformes(this.informesService.find(new Informes(avisofilter.getIdInforme20())));
		}
		return avisofilter;
	}

	/**
	 * Deletes a single row in the Avisos table.
	 *
	 * @param avisos Avisos
	 */
	@Transactional(rollbackFor = Throwable.class)
	public void remove(Avisos avisos) {
		DocuAvisos docuAvisos = new DocuAvisos();
		docuAvisos.setIdAviso70(avisos.getIdAviso20());
		this.docuAvisosService.removeByIdAviso(docuAvisos);
		this.avisosDao.remove(avisos);
	}

	@Transactional(rollbackFor = Throwable.class)
	public String informeDeIntervencionDSS(Avisos aviso) {

		// Si ya tiene informe asociado el aviso viene de la bandeja de informes y hay
		// que avisar a PS
		// Si no lo tiene hay que crear el informe en la bandeja de informes

		Informes informes = new Informes();
		if (aviso.getIdInforme20() == null || aviso.getIdInforme20() == 0) {
			informes.setTipoInforme16(TipoInformesEnum.INFINT.getId());
			Solicitantes solicitante = new Solicitantes();
			solicitante.setIdSolicitante01(aviso.getIdSolicitante20());
			informes.setSolicitantes(solicitante);
//			informes.setfAlta16(aviso.getfAlta20());
			informes.setUsuAlta16(UtilSeguridad.getUsuarioConectado().getUserName());
			informes.setObservaciones16(aviso.getObservaciones20());
			informes.setEstado16(EstadoInformesEnum.PENDIENTE_APORTAR.getId());
			informes = this.informesService.addDesdeAvisos(informes);
		} else {
			informes.setIdInforme16(aviso.getIdInforme20());
			informes.setEstado16(EstadoInformesEnum.SOLICITADO_PROVEEDOR.getId());
			informes.setUsuModif16(UtilSeguridad.getUsuarioConectado().getUserName());
			this.informesService.updateEstado(informes);
		}

		// Crear aviso para el PS
		Avisos newAviso = new Avisos();
//		newAviso.setUsuAlta20(UtilSeguridad.getNameUsuarioConectado());
		newAviso.setUsuAlta20(Constantes.COD_APLICACION);
		newAviso.setMaestroTiposAviso(new MaestroTiposAviso(TipoAvisoEnum.INFINT.getIdLongValue()));
		newAviso.setMaestroSubtiposAviso(new MaestroSubtiposAviso(SubTipoAvisoEnum.SOLICPS.getIdLongValue()));
		newAviso.setMaestroActoresByOrigen20(new MaestroActores(ActoresEnum.DSS.getId()));
		newAviso.setMaestroActoresByDestino20(new MaestroActores(ActoresEnum.PS.getId()));
		newAviso.setIdInforme20(informes.getIdInforme16());
		newAviso.setObservaciones20(aviso.getObservaciones20());
		newAviso.setIdSolicitante20(aviso.getIdSolicitante20());
		newAviso.setUrgencia20(aviso.getUrgencia20());
		this.avisosDao.add(newAviso);

		return "ok";
	}

	@Transactional(rollbackFor = Throwable.class)
	public String informeAltoRiesgoPS(Avisos aviso) {

		Informes informes = new Informes();
		informes.setTipoInforme16(TipoInformesEnum.INFSUM.getId());
		Solicitantes solicitante = new Solicitantes();
		solicitante.setIdSolicitante01(aviso.getIdSolicitante20());
		informes.setSolicitantes(solicitante);
		informes.setUsuAlta16(UtilSeguridad.getUsuarioConectado().getUserName());
		informes.setObservaciones16(aviso.getObservaciones20());
		informes.setEstado16(EstadoInformesEnum.PENDIENTE_APORTAR.getId());
		informes = this.informesService.addDesdeAvisos(informes);

//		// Crear aviso para el PS. De momento no lo creamos
//		Avisos newAviso = new Avisos();
//		newAviso.setUsuAlta20(UtilSeguridad.getNameUsuarioConectado());
//		newAviso.setMaestroTiposAviso(new MaestroTiposAviso(TipoAvisoEnum.OTROS.getIdLongValue()));
//		newAviso.setMaestroSubtiposAviso(new MaestroSubtiposAviso(SubTipoAvisoEnum.ALTRIES.getIdLongValue()));
//		newAviso.setMaestroActoresByOrigen20(new MaestroActores(ActoresEnum.AUTOMATICO.getId()));
//		newAviso.setMaestroActoresByDestino20(new MaestroActores(ActoresEnum.PS.getId()));
//		newAviso.setIdInforme20(informes.getIdInforme16());
//		newAviso.setObservaciones20(aviso.getObservaciones20());
//		newAviso.setIdSolicitante20(aviso.getIdSolicitante20());
//		newAviso.setUrgencia20(aviso.getUrgencia20());
//		this.avisosDao.add(newAviso);

		return "ok";
	}

	@Transactional(rollbackFor = Throwable.class)
	public Long tieneExpediente(Avisos aviso) {

		return this.informesDao.tieneExpediente(aviso.getIdInforme20());
	}

	@Transactional(rollbackFor = Throwable.class)
	public String informeDeIntervencionPS(Avisos aviso) {
		Informes informes = new Informes();

		informes.setIdInforme16(aviso.getIdInforme20());
		informes.setEstado16(EstadoInformesEnum.PENDIENTE_APORTAR.getId());
		informes.setUsuModif16(UtilSeguridad.getUsuarioConectado().getUserName());
		this.informesService.updateEstado(informes);

		this.avisosDao.procesarAvisos(aviso);
		return "ok";
	}

	@Transactional(rollbackFor = Throwable.class)
	public Avisos solicitarPS(Avisos aviso) {

		// Actualizar el estado del infrome a pendeitne de aportar
		Informes informes = new Informes();
		informes.setIdInforme16(aviso.getIdInforme20());
		informes.setEstado16(EstadoInformesEnum.PENDIENTE_APORTAR.getId());
		informes.setUsuModif16(UtilSeguridad.getUsuarioConectado().getUserName());
		this.informesService.updateEstado(informes);

		// Procesar el aviso
		return this.avisosDao.procesarAvisos(aviso);

	}

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

	/**
	 * Finds rows in the Avisos table using like.
	 *
	 * @param avisos          Avisos
	 * @param tableRequestDto TableRequestDto
	 * @param startsWith      Boolean
	 * @return List<Avisos>
	 */
	public List<Avisos> findAllLike(Avisos avisos, TableRequestDto tableRequestDto, Boolean startsWith) {
		return (List<Avisos>) this.avisosDao.findAllLike(avisos, tableRequestDto, startsWith);
	}

	/*
	 * OPERACIONES RUP_TABLE
	 */

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

	/**
	 * Filter method in the Avisos table.
	 *
	 * @param filterAvisos    Avisos
	 * @param tableRequestDto TableRequestDto
	 * @param startsWith      Boolean
	 * @return TableResponseDto<Avisos>
	 */
	public TableResponseDto<Avisos> filter(Avisos filterAvisos, TableRequestDto tableRequestDto, Boolean startsWith,
			Integer usuario) {
		List<Avisos> listaAvisos = this.avisosDao.findAllLikeConUsuario(filterAvisos, tableRequestDto, false, usuario);
		Long recordNum = this.avisosDao.findAllLikeCountConUsuario(filterAvisos != null ? filterAvisos : new Avisos(),
				false, usuario);

		TableResponseDto<Avisos> tableResponseDto = new TableResponseDto<Avisos>(tableRequestDto, recordNum,
				listaAvisos);

		if (tableRequestDto.getMultiselection().getSelectedIds() != null) {
			List<TableRowDto<Avisos>> reorderSelection = this.avisosDao.reorderSelection(filterAvisos, 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<Avisos>> reorderSeeker = this.avisosDao.reorderSelection(filterAvisos, tableRequestDto,
					startsWith);
			tableResponseDto.setReorderedSeeker(reorderSeeker);
			tableResponseDto.addAdditionalParam("reorderedSeeker", reorderSeeker);
		}
		return tableResponseDto;
	}

	/**
	 * Searches rows in the Avisos table.
	 *
	 * @param filterAvisos    Avisos
	 * @param searchAvisos    Avisos
	 * @param tableRequestDto TableRequestDto
	 * @param startsWith      Boolean
	 * @return List<TableRowDto<Avisos>>
	 */
	public List<TableRowDto<Avisos>> search(Avisos filterAvisos, Avisos searchAvisos, TableRequestDto tableRequestDto,
			Boolean startsWith) {
		return this.avisosDao.search(filterAvisos, searchAvisos, tableRequestDto, startsWith);
	}

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

	/*
	 * OPERACIONES RUP_TABLE JERARQUIA
	 */

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

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

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

	/**
	 * Devuelve un fichero en el formato deseado que contiene los datos exportados
	 * de la tabla.
	 *
	 * @param filterAvisos    Avisos
	 * @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(Avisos filterAvisos, String[] columns, String[] columnsName, String fileName,
			String sheetTitle, TableRequestDto tableRequestDto, HttpServletRequest request,
			HttpServletResponse response) {
		// Accede a la DB para recuperar datos
		List<Avisos> filteredData = getDataForReports(filterAvisos, tableRequestDto);
		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 : Avisos.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 = Avisos.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);
					Avisos.class.getMethod("get" + methodName);
					tempColumns.add(fields[i].getName());
				} catch (NoSuchMethodException e) {
					e.printStackTrace();
				}

			}
			columns = tempColumns.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<Avisos>
	 * @param columns      String[]
	 * @param fileName     String
	 * @param sheetTitle   String
	 * @param extension    String
	 * @param formatter    SimpleDateFormat
	 * @param response     HttpServletResponse
	 */
	private void generateExcelReport(List<Avisos> 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 (Avisos rowAvisos : 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 cellAvisos = row.createCell(cellNumber++);
					cellAvisos.setCellValue(getCellValue(column, rowAvisos));
				}
			}

			// 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<Avisos>
	 * @param columns      String[]
	 * @param fileName     String
	 * @param response     HttpServletResponse
	 */
	private void generatePDFReport(List<Avisos> 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 (Avisos rowAvisos : 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, rowAvisos));
				}
			}

			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<Avisos>
	 * @param columns      String[]
	 * @param fileName     String
	 * @param sheetTitle   String
	 * @param response     HttpServletResponse
	 */
	private void generateODSReport(List<Avisos> 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 (Avisos rowAvisos : 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, rowAvisos));
				}
			}

			// 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<Avisos>
	 * @param columns      String[]
	 * @param fileName     String
	 * @param sheetTitle   String
	 * @param language     String
	 * @param response     HttpServletResponse
	 */
	private void generateCSVReport(List<Avisos> 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 (Avisos rowAvisos : 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, rowAvisos);

					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 filterAvisos    Avisos
	 * @param tableRequestDto TableRequestDto
	 */
	private List<Avisos> getDataForReports(Avisos filterAvisos, TableRequestDto tableRequestDto) {
		if (tableRequestDto.getMultiselection().getSelectedAll()
				&& tableRequestDto.getMultiselection().getSelectedIds().isEmpty()) {
			if (filterAvisos != null) {
				return this.avisosDao.findAllLike(filterAvisos, tableRequestDto, false);
			} else {
				return this.avisosDao.findAll(new Avisos(), null);
			}
		} else {
			return this.avisosDao.getMultiple(filterAvisos, tableRequestDto, false);
		}
	}

	/**
	 * Obtiene los valores de las celdas.
	 *
	 * @param column    String
	 * @param rowAvisos Avisos
	 */
	private String getCellValue(String column, Avisos rowAvisos) {
		String cellValue = "";
		try {
			if (column.equals("urgencia20String") && rowAvisos.getUrgencia20String() != null) {
				if (rowAvisos.getUrgencia20String().equals("0")) {
					rowAvisos.setUrgencia20String(UtilLiterales.getLiteralMessage(appMessageSource,
							LocaleContextHolder.getLocale(), "comun.no"));
				} else {
					rowAvisos.setUrgencia20String(UtilLiterales.getLiteralMessage(appMessageSource,
							LocaleContextHolder.getLocale(), "comun.si"));
				}
			}
			cellValue = BeanUtils.getProperty(rowAvisos, column) != null ? BeanUtils.getProperty(rowAvisos, column)
					: "";
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		}

		return cellValue;
	}

	public List<Avisos> avisosComplete(Avisos filterAvisos, TableRequestDto tableRequestDto) {
		List<Avisos> listaAvisos = this.avisosDao.filterComplete(filterAvisos, tableRequestDto);
		if (listaAvisos.get(0).getIdInforme20() != null) {
			listaAvisos.get(0)
					.setInformes(this.informesService.find(new Informes(listaAvisos.get(0).getIdInforme20())));
		}
		return listaAvisos;
	}

	@Transactional(rollbackFor = Throwable.class)
	public Avisos procesarAvisos(Avisos aviso) {

		String tipoAviso = aviso.getMaestroTiposAviso().getIdTipoAviso40().toString();
		String subtipoAviso = aviso.getMaestroSubtiposAviso().getIdSubTipoAviso41().toString();

		aviso.setUsuProcesado20(UtilSeguridad.getNameUsuarioConectado());

		// tipo: Actualización, subtipo: Anular baja, baja, baja/alta,baja anticipada y
		// cambio de domicilio urgente
		if (tipoAviso.equals(TipoAvisoEnum.ACTUAL.getId()) && (subtipoAviso.equals(SubTipoAvisoEnum.CAMBIODOM.getId())
				|| subtipoAviso.equals(SubTipoAvisoEnum.BAJAANTICIPADA.getId())
				|| subtipoAviso.equals(SubTipoAvisoEnum.ANULARBAJA.getId()))) {
			return this.avisosDao.procesarAvisos(aviso);
		}

		// tipo: Dispositivo, subtipo: Avería, nuevo dispositivo, otros, perdida,
		// recogida de aparato y sustitución
		if (tipoAviso.equals(TipoAvisoEnum.DISP.getId()) && (subtipoAviso.equals(SubTipoAvisoEnum.AVERIA.getId())
				|| subtipoAviso.equals(SubTipoAvisoEnum.NUEVODISP.getId())
				|| subtipoAviso.equals(SubTipoAvisoEnum.PERDIDA.getId())
				|| subtipoAviso.equals(SubTipoAvisoEnum.OTROS_DISP.getId())
				|| subtipoAviso.equals(SubTipoAvisoEnum.RECOGAPARATO.getId())
				|| subtipoAviso.equals(SubTipoAvisoEnum.SUSTITUCION.getId()))) {
			return this.avisosDao.procesarAvisos(aviso);
		}

		if (tipoAviso.equals(TipoAvisoEnum.INFINT.getId()) && (subtipoAviso.equals(SubTipoAvisoEnum.SOLICPS.getId()))) {
			// Actualizar el estado del infrome a pendeitne de aportar
			informeDeIntervencionPS(aviso);
			return this.avisosDao.procesarAvisos(aviso);
		}

		if (tipoAviso.equals(TipoAvisoEnum.INFINT.getId())
				&& (subtipoAviso.equals(SubTipoAvisoEnum.SOLICDSS.getId()))) {
			informeDeIntervencionDSS(aviso);
			return this.avisosDao.procesarAvisos(aviso);
		}

		if (tipoAviso.equals(TipoAvisoEnum.INFINT.getId()) && (subtipoAviso.equals(SubTipoAvisoEnum.ENVDSS.getId()))) {
			if (tieneExpediente(aviso) >= 1) {
				// si tiene expediente
				// se aporta el informe al expediente
				this.aportarInformeExpediente(aviso);
			}
			this.informesDao.actualizarEstadoInformeDisponible(new Informes(aviso.getIdInforme20()));
			return this.avisosDao.procesarAvisos(aviso);
		}

		if (tipoAviso.equals(TipoAvisoEnum.INFSOC.getId())
				&& (subtipoAviso.equals(SubTipoAvisoEnum.ENVEXPAB.getId()))) {
			if (tieneExpediente(aviso) >= 1) {
				// si tiene expediente
				// se aporta el informe al expediente
				this.aportarInformeExpediente(aviso);
			}
			this.informesDao.actualizarEstadoInformeDisponible(new Informes(aviso.getIdInforme20()));
			return this.avisosDao.procesarAvisos(aviso);
		}
		if (tipoAviso.equals(TipoAvisoEnum.INFSUIC.getId())
				&& (subtipoAviso.equals(SubTipoAvisoEnum.ENVDSSSUIC.getId()))) {
			this.informesDao.actualizarEstadoInformeDisponible(new Informes(aviso.getIdInforme20()));
			return this.avisosDao.procesarAvisos(aviso);
		}
		if (tipoAviso.equals(TipoAvisoEnum.OTROS.getId()) && subtipoAviso.equals(SubTipoAvisoEnum.ALTRIES.getId())) {
			// informeAltoRiesgoPS(aviso);
			return this.avisosDao.procesarAvisos(aviso);
		}

		if (tipoAviso.equals(TipoAvisoEnum.OTROS.getId()) && (subtipoAviso.equals(SubTipoAvisoEnum.CORRECERR.getId())
				|| subtipoAviso.equals(SubTipoAvisoEnum.NOCONTACUSU.getId()))) {
			return this.avisosDao.procesarAvisos(aviso);
		}
		if (tipoAviso.equals(TipoAvisoEnum.QUEJREC.getId()) && (subtipoAviso.equals(SubTipoAvisoEnum.INSTAL.getId())
				|| subtipoAviso.equals(SubTipoAvisoEnum.SERV.getId())
				|| subtipoAviso.equals(SubTipoAvisoEnum.OTRASQUEJ.getId()))) {
			return this.avisosDao.procesarAvisos(aviso);
		}
		if (tipoAviso.equals(TipoAvisoEnum.INST.getId()) && (subtipoAviso.equals(SubTipoAvisoEnum.CAMBIOCITA.getId())
				|| subtipoAviso.equals(SubTipoAvisoEnum.COMENTINST.getId()))) {
			return this.avisosDao.procesarAvisos(aviso);
		} else {
			return this.avisosDao.procesarAvisos(aviso);
		}

	}

	private void aportarInformeExpediente(Avisos aviso) {

		// recuperamos el informe y el expediente
		Informes informe = this.informesDao.find(new Informes(aviso.getIdInforme20()));
		Expedientes expediente = this.expedientesDao.find(new Expedientes(informe.getIdExpediente16()));
		DocumentosInforme docInforme = this.documentosInformeDao
				.find(new DocumentosInforme(informe.getDocumentosInforme().getIdDocInforme17()));

		DocTemporales docTemp = new DocTemporales();
		docTemp.setNombreDoc72(docInforme.getNombreDoc17());
		docTemp.setRutapif72(docInforme.getUrlRuta17());
		docTemp.setOid72(docInforme.getOid17());
		docTemp.setFolderId72(expediente.getFolderId00());
		docTemp.setUsuAportacion72(UtilSeguridad.getNameUsuarioConectado());
		docTemp.setIdTipoDoc72(docInforme.getIdTipoDoc17());
		docTemp.setExterno72(1);
		this.documentosService.aportarDocumento(docTemp);
	}

	@Transactional(rollbackFor = Throwable.class)
	public Avisos updateAvisos(Avisos avisos) {
		return this.avisosDao.update(avisos);
	}

	@Transactional(rollbackFor = Throwable.class)
	public Avisos insertAvisos(Avisos avisos) {
		return this.avisosDao.add(avisos);
	}

	public Avisos aportarExpediente(Avisos aviso) {
		return this.informesDao.aportarExpediente(aviso);
	}

	public Avisos correoSSB(Avisos aviso) {
		return this.avisosDao.procesarAvisos(aviso);
	}

	@Transactional(rollbackFor = Throwable.class)
	public Avisos aportar(Avisos avisos, MultipartFile documento, Long idTipoDoc36) {

		// Llama a un filtercomplete de avisos que devuelve una lista
		List<Avisos> avisosList = this.avisosComplete(avisos, null);
		Avisos avisosAux = new Avisos();

		if (!avisosList.isEmpty()) {
			avisosAux = avisosList.get(0);
		}

		// Subir a PIF
		String rutaPif = "";
		try {
			InputStream inputStream = new BufferedInputStream(documento.getInputStream());
			String pathDocumento = com.ejie.ad74a.constantes.Constantes.RUTA_PID
					.concat(Constantes.COD_APLICACION.toLowerCase()).concat("/")
					.concat(documento.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.getOriginalFilename();
		String usuarioAlta = Constantes.COD_APLICACION;
		String aplic = Constantes.COD_APLICACION;
		String rutaPid = pidWS.put(aplic, nombreFichero, rutaPif, usuarioAlta);

		// Insertar documento
		DocuAvisos docuAvisos = new DocuAvisos();
		try {
			docuAvisos.setUsuAlta70(UtilSeguridad.getUsuarioConectado().getUserName());
			docuAvisos.setRuta70(rutaPid);
			docuAvisos.setNombreDoc70(documento.getOriginalFilename());
			docuAvisos.setIdAviso70(avisos.getIdAviso20());
//			docuAvisos.setExtension70(
//					documento.getOriginalFilename().substring(documento.getOriginalFilename().length() - 3));
			docuAvisos.setExtension70(getExtension(documento.getOriginalFilename()));
			docuAvisos.setIdTipoDoc70(idTipoDoc36);
			this.docuAvisosService.add(docuAvisos);
		} catch (Exception e) {
			ManagerErrors.manageException(e, "ERROR generando el documento aviso");

		}
		return avisosAux;
	}

	@Transactional(rollbackFor = Throwable.class)
	public DocuAvisos subirDocumentoAPif(DocuAvisos docuAvisos, MultipartFile documento) {
		String rutaPif = "";
		try {
			InputStream inputStream = new BufferedInputStream(documento.getInputStream());
			String pathDocumento = com.ejie.ad74a.constantes.Constantes.RUTA_PID
					.concat(Constantes.COD_APLICACION.toLowerCase()).concat("/")
					.concat(documento.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.getOriginalFilename();
		String usuarioAlta = Constantes.COD_APLICACION;
		String aplic = Constantes.COD_APLICACION;
		String rutaPid = pidWS.put(aplic, nombreFichero, rutaPif, usuarioAlta);

		docuAvisos.setNombreDoc70(documento.getOriginalFilename());
//		docuAvisos.setExtension70(
//				documento.getOriginalFilename().substring(documento.getOriginalFilename().length() - 3));
		docuAvisos.setExtension70(getExtension(documento.getOriginalFilename()));
		docuAvisos.setRuta70(rutaPid);
		docuAvisos.setUsuAlta70(UtilSeguridad.getUsuarioConectado().getUserName());
		docuAvisos.setfecAlta70(UtilidadesFecha.getFechaSistema());

		return docuAvisos;
	}

	@Transactional(rollbackFor = Throwable.class)
	public Avisos aportarDocumento(MultipartFile documento) {

		// Subir a PIF
		String rutaPif = "";
		try {
			InputStream inputStream = new BufferedInputStream(documento.getInputStream());
			String pathDocumento = "/".concat(com.ejie.ad74a.constantes.Constantes.COD_APLICACION.toLowerCase())
					.concat("/").concat(documento.getOriginalFilename());
			rutaPif = Ad74aPIFws.getInstance().uploadDocument(inputStream, pathDocumento, true);
			inputStream.close();
		} catch (Exception e) {
			ManagerErrors.manageException(e, "ERROR subiendo documento aportar al informe a PIF");
		}

		// Subir a PID
		String nombreFichero = documento.getOriginalFilename();
		// String usuarioAlta = UtilSeguridad.getUsuarioConectado().getUserName();
		String usuarioAlta = Constantes.COD_APLICACION;
		String aplic = Constantes.COD_APLICACION;
		String rutaPid = pidWS.put(aplic, nombreFichero, rutaPif, usuarioAlta);

		// Insertar documento
		DocuAvisos docAvisos = new DocuAvisos();
		docAvisos.setIdAviso70((Long) 1L);
		docAvisos.setUsuAlta70(UtilSeguridad.getUsuarioConectado().getUserName());
		docAvisos.setRuta70(rutaPid);
//		docAvisos.setExtension70(
//				documento.getOriginalFilename().substring(documento.getOriginalFilename().length() - 3));
		docAvisos.setExtension70(getExtension(documento.getOriginalFilename()));
		docAvisos.setNombreDoc70(documento.getOriginalFilename());
		docuAvisosService.add(docAvisos);

		return new Avisos();

	}

	public Direcciones getDireccionDesdeXML(Direcciones direcciones) {
		Direcciones dir = new Direcciones();
		Provincia provincia = this.noraDao.findProvincia(new BigDecimal(direcciones.getProvincia06()));
		Municipio municipioFilter = new Municipio();
		municipioFilter.setCodProvincia(direcciones.getProvincia06());
		municipioFilter.setCodMunicipio(direcciones.getMunicipio06());
		Municipio municipio = this.noraDao.findMunicipioConProv(municipioFilter);
		Localidad localidadfilter = new Localidad();
		localidadfilter.setCodLocalidad(Long.valueOf(direcciones.getLocalidad06()));
		localidadfilter.setCodMunicipio(direcciones.getMunicipio06());
		localidadfilter.setCodProvincia(direcciones.getProvincia06());
		Localidad localidad = this.noraDao.findAllLocalidadesCambioDomic(localidadfilter).get(0);
		Calle calleFilter = new Calle();
		calleFilter.setCodCalle(Long.valueOf(direcciones.getCalle06()));
		calleFilter.setCodProvincia(direcciones.getProvincia06());
		calleFilter.setCodMunicipio(direcciones.getMunicipio06());
		Calle calle = this.noraDao.findAllCallesCambioDomic(calleFilter).get(0);
		Portal portalFilter = new Portal();
		portalFilter.setCodPortal(Long.valueOf(direcciones.getPortal06()));
		Portal portal = this.noraDao.findPortal(portalFilter);

		dir.setProvinciaNORA(provincia);
		dir.setMunicipioNORA(municipio);
		dir.setLocalidadNORA(localidad);
		dir.setCalleNORA(calle);
		dir.setPortalNORA(portal);

		return dir;
	}

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

}
