package es.ejie.frmk.infraestructura.gestorimpresion;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.Collection;
import java.util.HashMap;

import es.ejie.frmk.Q70Factoria;
import es.ejie.frmk.control.arranque.Q70GestorArranqueConfiguracionServidor;
import es.ejie.frmk.control.excepciones.Q70FrameworkException;
import es.ejie.frmk.infraestructura.traza.Q70TraceLevel;
import es.ejie.frmk.infraestructura.traza.Q70TrazaInterface;
import es.ejie.frmk.presentacion.sesiondatos.Q70ContextoEJIE;

/**
 * Procesa una plantilla sustituyendo los huecos por valores del contexto.
 * <p>
 * La misión del <b>Gestor de Impresión</b> es facilitar la impresión online de documentos 
 * desde el browser en base a diferentes tecnologías.
 * Su labor es recoger los datos que se van a imprimir del contexto, 
 * sustituir en una plantilla los campos reservados por su valor, 
 * aplicar alguna transformación y presentar en el browser el resultado. 
 * Se considera también la posibilidad de que aplicaciones que no hayan sido desarrolladas
 * sobre Geremua invoquen a la Operación de Presentación de Impresión.
 * <p>
 * Esta clase procesa una plantilla buscando el string especial ESCAPE=<b>$$$</b>.
 * Este string envuelve el nombre de un dato. 
 * Cuando se encuentra, se sustutuye por su valor.
 * Ejemplo: $$$NOMBRE$$$ -> $$$José Luis$$$
 * También se pueden hacer colecciones de datos
 * Ejemplo: $$$COLECCION=NOMBRES$$$...NOMBRE....$$$COLECCION=NOMBRES$$$
 * recorre la colección NOMBRES
 * 
 * @version 1.0
 */
public class Q70ProcesadorPlantilla {
	private static final String ESCAPE="$$$";
	private static final String ESCAPE_COLECCION="$$$COLECCION";
	private static final String IDENTIFICADOR_COLECCION="COLECCION=";
	
	private static final String COMPONENTE="GESTOR_IMPRESION";
	
	/**
	 * Único constructor 
	 */
	public Q70ProcesadorPlantilla() {
		super();
	}

	/**
	 * Procesa una plantilla leída a través de un InputStream
	 * @param is
	 * @param contexto
	 * @return
	 * @throws Exception
	 */
	public InputStream procesar(InputStream is, Q70ContextoEJIE contexto) throws Exception {
		return procesar(is,contexto,0,null);
	}
	
	private InputStream procesar(InputStream is, Q70ContextoEJIE contexto, int indiceColeccion, String nombreColeccion) throws Exception {
		byte[] bytes = new byte[is.available()];
		is.read(bytes);
		is.close(); 
		String contenido = new String(bytes);
		return procesar(contenido, contexto, indiceColeccion, nombreColeccion);
	}

	/**
	 * Recorre un texto buscando los huecos y sustituyéndolos por el valor del dato.
	 * Es recursivo, para facilitar la posible futura implementación de colecciones dentro de colecciones.
	 * @param contenido
	 * @param contexto
	 * @param indiceColeccion
	 * @param nombreColeccion
	 * @return
	 * @throws Exception
	 */
	private InputStream procesar(String contenido, Q70ContextoEJIE contexto, int indiceColeccion, String nombreColeccion) throws Exception {
		String contenidoProcesado = "";
		String nombreDato = null;
		int indexOpen = -1;
		int indexClose = -ESCAPE.length();
		boolean iterar = true;
		HashMap datosImpresion = (HashMap)contexto.get("datosImpresion");
		while (iterar) {
			indexOpen = contenido.indexOf(ESCAPE,indexClose+ESCAPE.length());
			if (indexOpen<0) {
				contenidoProcesado += contenido.substring(indexClose+ESCAPE.length());
				break;				
			}
			contenidoProcesado += contenido.substring(indexClose+ESCAPE.length(),indexOpen);
			//Busco el nuevo cierre de dato
			indexClose = contenido.indexOf(ESCAPE,indexOpen+ESCAPE.length());
			nombreDato = contenido.substring(indexOpen+ESCAPE.length(),indexClose);
			if (isColeccion(nombreDato)) {
				int finColeccion = contenido.indexOf(ESCAPE_COLECCION,indexOpen+ESCAPE_COLECCION.length());
				String parteRepetitiva = contenido.substring(indexClose+ESCAPE.length(),finColeccion);
				String nombreColeccionEncontrada = getNombreColeccion(nombreDato);
				Collection col = (Collection)datosImpresion.get(nombreColeccionEncontrada);
				int size = 0;
				if (col!=null)
					size = col.size();
				for (int i=0;i<size;i++) {
					InputStream isIteracion = procesar(parteRepetitiva,contexto,i,nombreColeccionEncontrada);
					byte[] bytesIteracion = new byte[isIteracion.available()];
					isIteracion.read(bytesIteracion);
					isIteracion.close(); 
					contenidoProcesado += new String(bytesIteracion);
				}
				indexClose = contenido.indexOf(ESCAPE,finColeccion+ESCAPE.length());
			}
			else
				contenidoProcesado += getDato(datosImpresion,nombreDato,nombreColeccion,indiceColeccion);
				
		}
		return new ByteArrayInputStream(contenidoProcesado.getBytes());
	}


	/**
	 * Recoge un dato del contexto para sustituirlo por un hueco.
	 * Puede recoger un dato simple o un dato que forme parte de una colección.
	 * @param contexto
	 * @param nombreDato
	 * @param nombreColeccion
	 * @return
	 */
	private String getDato(HashMap contexto, String nombreDato, String nombreColeccion, int indiceColeccion) throws Exception {
		try {
			if (nombreColeccion==null)
			{
				String dato=null;
				Object o =contexto.get(nombreDato);
				if (o==null)
					dato = "NULL";
				else
				   	dato=o.toString();
				   	
				return dato; 
			}
				
			else {
				HashMap item = (HashMap)((java.util.List)contexto.get(nombreColeccion)).get(indiceColeccion);
				return (String) item.get(nombreDato);
			}
		}
		catch (Exception e) {
			Q70TrazaInterface traza = Q70Factoria.getTraza();
			if (traza.isTraceActive()){
				traza.trace(COMPONENTE, Q70TraceLevel.ERROR, "Se ha producido un error en el método getDato de la clase Q70ProcesadorPlantilla");
			}
			throw new Q70FrameworkException(COMPONENTE,this.getClass().toString(),"getDato","Se ha producido un error en el método getDato.",e);
		}
	}

	/**
	 * Obtiene el nombre de la colección a partir de un dato
	 * @param nombreDato
	 * @return
	 */
	private String getNombreColeccion(String nombreDato) {
		return nombreDato.substring(IDENTIFICADOR_COLECCION.length());
	}

	/**
	 * En función del nombre de un dato decide si se trata de una colección o dato simple.
	 * Las colecciones tienen el formato $$$COLECCION=NOMBRES$$$
	 * @param nombreDato
	 * @return
	 */
	private boolean isColeccion(String nombreDato) {
		return nombreDato.startsWith(IDENTIFICADOR_COLECCION);
	}
}
