package com.ejie.ad74a.util;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.stream.Stream;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 
 * Clase de utilidades para ficheros
 *
 */
public final class UtilFiles {
	private static final Logger logger = LoggerFactory.getLogger(UtilFiles.class);

	// Constructor privado. Clase de utilidades
	private UtilFiles() {
	}

	/**
	 * Traspasa informacion de un stream de entrada a uno de salida, de forma 'no
	 * bloqueante' (usando la libreria nio).
	 * 
	 * @param input  InputStream
	 * @param output OutputStream
	 * @return long
	 * @throws IOException e
	 */
	public static long stream(InputStream input, OutputStream output) throws IOException {
		ReadableByteChannel inputChannel = null;
		WritableByteChannel outputChannel = null;

		try {
			inputChannel = Channels.newChannel(input);
			outputChannel = Channels.newChannel(output);
			ByteBuffer buffer = ByteBuffer.allocate(1024);
			long size = 0;

			while (inputChannel.read(buffer) != -1) {
				buffer.flip();
				size += outputChannel.write(buffer);
				buffer.clear();
			}

			return size;
		} finally {
			if (outputChannel != null) {
				try {
					outputChannel.close();
				} catch (IOException e) {
					logger.debug("Error al cerrar canal de escritura", e);
				}
			}
			if (inputChannel != null) {
				try {
					inputChannel.close();
				} catch (IOException e) {
					logger.debug("Error al cerrar canal de lectura", e);
				}
			}
		}
	}

	/**
	 * Obtiene un String con el contenido de un inputStream
	 * 
	 * @param inputStream contenido
	 * @param encoding    Codificacion a utilizar
	 * @return String
	 * @throws IOException
	 */
	public static String getStringContent(InputStream inputStream, String encoding) throws IOException {
		try (BufferedReader br = new BufferedReader(new InputStreamReader(inputStream, encoding))) {
			StringBuilder sb = new StringBuilder();
			String line;
			while ((line = br.readLine()) != null) {
				sb.append(line);
				sb.append('\n');
			}
			return sb.toString();
		}
	}

	/**
	 * Obtiene nombre sin extension de un fichero
	 * 
	 * @param filePath
	 * @return
	 */
	public static String getNameWithoutExt(String filePath) {
		String aux = getName(filePath);
		int end = aux.lastIndexOf('.');
		end = end < 0 ? aux.length() : end;

		return aux.substring(0, end);
	}

	/**
	 * Obtiene extension de un fichero (Ejemplo: ".pdf").
	 * 
	 * @param filePath
	 * @return
	 */
	public static String getExtension(String filePath) {
		String aux = getName(filePath);
		int start = aux.lastIndexOf('.');
		if (start < 0) {
			return "";
		} else {
			return aux.substring(start, aux.length());
		}
	}

	/**
	 * Devuelve la ruta al fichero, cambiando su extension por la nueva informada
	 * por parametro
	 * 
	 * @param filePath
	 * @param newExtension
	 * @return
	 */
	public static String replaceExtension(String filePath, String newExtension) {
		int lastSlashIndex = filePath.lastIndexOf('/');
		int endIndex = filePath.lastIndexOf('.');
		endIndex = (endIndex < 0 || endIndex < lastSlashIndex) ? filePath.length() : endIndex;
		return filePath.substring(0, endIndex) + newExtension;
	}

	/**
	 * Obtiene nombre (con extension) de un fichero en pif
	 * 
	 * @param filePath String
	 * @return
	 */
	public static String getName(String filePath) {
		int start = filePath.lastIndexOf('/');
		start = start < 0 ? 0 : start + 1;
		return filePath.substring(start, filePath.length());
	}

	/**
	 * Obtiene la carpeta de un fichero (sin el nombre del fichero)
	 * 
	 * @param filePath String
	 * @return
	 */
	public static String getFolder(String filePath) {
		int end = filePath.lastIndexOf('/');
		end = end < 0 ? filePath.length() : end;
		return filePath.substring(0, end) + "/";
	}

	/**
	 * Normaliza el path de una carpeta, haciendo que acabe siempre en una '/'.
	 * 
	 * @param filePath String
	 * @return
	 */
	public static String normalizeFolder(String filePath) {
		String result = filePath.replace('\\', '/').replaceAll("//", "/");
		if (filePath.charAt(filePath.length() - 1) != '/') {
			result += "/";
		}
		return result;
	}

	/**
	 * Mueve un fichero de una localizacion a otra
	 * 
	 * @param originPath path original de fichero (nombre de fichero incluido)
	 * @param finalPath  path final del fichero (nombre de fichero incluido)
	 */
	public static void moveFile(Path originPath, Path finalPath) {
		try {
			Path parentDir = finalPath.getParent();
			if (!Files.exists(parentDir)) {
				Files.createDirectories(parentDir);
			}
			Files.move(originPath, finalPath, StandardCopyOption.REPLACE_EXISTING);
		} catch (IOException e1) {
			logger.error("Se ha producido un error al mover el fichero " + originPath + " a " + finalPath + ".", e1);
		}
	}

	/**
	 * Utilidad para borrar ficheros en todos los subdirectorios, en base a tres
	 * posibles criterios.
	 * 
	 * @param basePath                 Carpeta base desde la que se empieza a buscar
	 *                                 los ficheros a borrar (tambien en sus
	 *                                 subcarpetas)
	 * @param prefixFilter             Si se informa, el fichero debe empezar por
	 *                                 este prefijo
	 * @param suffixFilter             Si se informa, el fichero debe terminar por
	 *                                 este sufijo
	 * @param millisFromModifiedFilter Si se informa, el fichero debe tener un
	 *                                 tiempo, desde su modificación, mayor a este
	 *                                 valor
	 * @throws IOException
	 */
	public static void removeFiles(Path basePath, String prefixFilter, String suffixFilter,
			Long millisFromModifiedFilter) throws IOException {
		try (Stream<Path> files = Files.find(basePath, Integer.MAX_VALUE, (path, attr) ->
		// Borrar los csv de mas de una semana
		(!path.toFile().isDirectory() // solo ficheros
				&& (suffixFilter == null || path.getFileName().toString().endsWith(suffixFilter)) // cumple filtro
																									// sufijo
				&& (prefixFilter == null || path.getFileName().toString().startsWith(prefixFilter)) // cumple filtro
																									// prefijo
				// cumple filtro fecha modificacion
				&& (millisFromModifiedFilter == null || (System.currentTimeMillis()
						- attr.lastModifiedTime().toMillis()) > millisFromModifiedFilter)))) {
			files.map(Path::toFile).forEach(File::delete);
		}
	}

}
