package es.ejie.frmk.presentacion.sesiondatos.contexto.persistencia;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Properties;
import java.util.Stack;

import org.apache.commons.beanutils.BasicDynaClass;
import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.DynaBean;
import org.apache.commons.beanutils.DynaProperty;
import org.apache.commons.collections.Closure;
import org.apache.commons.collections.CollectionUtils;

import es.ejie.frmk.Q70Factoria;
import es.ejie.frmk.control.excepciones.Q70FrameworkException;
import es.ejie.frmk.control.excepciones.Q70GestorDeExcepcionesServidor;
import es.ejie.frmk.infraestructura.traza.Q70TraceLevel;
import es.ejie.frmk.infraestructura.traza.Q70TrazaInterface;
import es.ejie.frmk.infraestructura.utilidades.configurador.Q70Configurable;
import es.ejie.frmk.infraestructura.utilidades.configurador.Q70Configurador;
import es.ejie.frmk.infraestructura.utilidades.excepciones.Q70UtilidadesExcepcion;
import es.ejie.frmk.presentacion.sesiondatos.Q70ContextoJerarquicoEJIE;
import es.ejie.frmk.presentacion.sesiondatos.utilidades.Q70ContextoUtilidades;
import es.ejie.frmk.presentacion.sesiondatos.utilidades.Q70DatosDeSqlCampo;
/**
 * Contiene los datos necesarios para construir un Q70ContextoJerarquicoEJIE a partir de un documento XML.
 * <p>
 * Esta clase permite crear contextos a partir de su definición en un xml de configuración.
 * Por defecto, el contexto creado tendrá una persistencia gobernada por la clase Q70PersistenciaDesdeDynaBean, 
 * por lo tanto los cambios realizados sobre estos contextos no serán persistentes en un sistema externo 
 * a la propia memoria donde se está ejecutando Geremua y será mantenible.
 * <p>
 * En el xml de configuración se puede indicar la clase que va a manejar la persistencia y también 
 * si los valores de los campos van a ser mantenibles.
 * <p>
 * Todos los contextos excepto el contexto de OperacionPresentacion al ser creados desde ficheros xml, 
 * son manejados inicialmente por esta clase de persistencia. 
 * Aunque, posteriormente se indique otro tipo de persistencia.
 * <p>El contexto global SistemaTécnico usa únicamente este tipo de persistencia 
 * y además se indica que va a NO ser mantenible. 
 * Por tanto, sus cambios se realizarán en el xml de configuración y posteriormente 
 * desde la aplicación de Administración(r09) se recargará la definición del contexto 
 * para su actualización dentro de la instacia de Geremua.
 *
 * @see Q70PersistenciaDesdeBD
 * @see Q70PersistenciaDesdeDynaBean
 * @version 1.0
 * Fecha: 21-ene-04 
 */
public class Q70PersistenciaDesdeXml extends Q70PersistenciaDesdeDynaBean implements Q70Configurable
{

	private String rutaXml;
	private DynaBean dBeanSalida;
	
	private boolean isMantenible = true;	

	private transient Stack pilaDePadres;
	private transient DynaProperty propiedad;

	private transient String nombreAtributo, tipoAtributo, valorAtributo;
	private transient String nombreSqlCampo;
	private transient boolean isKey, isNoPersistente;

	private static final String ID_COMPONENTE = "PERSISTENCIA_DESDE_XML";

	private class DatosDeContexto
	{
		private DatosDeContexto padre;
		private List listaPropiedades; //Contiene un array de 2 valores: DynaProperty y Valor de esa DynaProperty		
		private String claseContexto, tipoContexto, idContexto;
		private List listaContextos;

		private String manejadorPersistenciaContexto;

		private String sqlDataSource;
		private String sqlTabla;
		private String sqlWhere;
		private List listaSqlCampos;

		public String getClaseContexto()
		{
			if (claseContexto == null)
				claseContexto = "es.ejie.frmk.presentacion.sesiondatos.Q70ContextoBasico";
			return claseContexto;
		}
		public String getIdContexto()
		{
			if (idContexto == null)
				idContexto = "";
			return idContexto;
		}
		public List getListaContextos()
		{
			return listaContextos;
		}
		public List getListaPropiedades()
		{
			return listaPropiedades;
		}
		public DatosDeContexto getPadre()
		{
			return padre;
		}
		public String getTipoContexto()
		{
			if (tipoContexto == null)
				tipoContexto = "";
			return tipoContexto;
		}
		public void setClaseContexto(String string)
		{
			claseContexto = string;
		}
		public void setIdContexto(String string)
		{
			idContexto = string;
		}
		public void setListaContextos(List list)
		{
			listaContextos = list;
		}
		public void setListaPropiedades(List list)
		{
			listaPropiedades = list;
		}
		public void setPadre(DatosDeContexto contexto)
		{
			padre = contexto;
		}
		public void setTipoContexto(String string)
		{
			tipoContexto = string;
		}
		public void setSqlTabla(String string)
		{
			sqlTabla = string;
		}
		public void setSqlWhere(String string)
		{
			sqlWhere = string;
		}
		public List getListaSqlCampos()
		{
			if (listaSqlCampos == null)
				listaSqlCampos = new ArrayList(5);
			return listaSqlCampos;
		}
		public String getSqlTabla()
		{
			if (sqlTabla == null)
				return "";
			return sqlTabla;
		}
		public String getSqlWhere()
		{
			if (sqlWhere == null)
				return "";
			return sqlWhere;
		}
		public String getSqlDataSource()
		{
			if (sqlDataSource == null)
				return "";
			return sqlDataSource;
		}
		public void setSqlDataSource(String string)
		{
			sqlDataSource = string;
		}
		public String getManejadorPersistenciaContexto()
		{
			return manejadorPersistenciaContexto;
		}
		public void setManejadorPersistenciaContexto(String string)
		{
			manejadorPersistenciaContexto = string;
		}
	}
	private transient DatosDeContexto datosDeContextoNuevo, datosDeContextoRaiz;

	public Q70PersistenciaDesdeXml(Object datos) throws Q70PersistenciaMalInicializadaException
	{
		super(datos);
	}

	protected void initialize(Object datos) throws Q70PersistenciaMalInicializadaException
	{
		if (datos != null && datos instanceof String)
		{
			rutaXml = (String) datos;
		}
		else
		{
			throw new Q70PersistenciaMalInicializadaException(
				ID_COMPONENTE,
				getClass().toString(),
				"initialize()",
				"Clase incorrectamente inicializada",
				null);
		}
	}

	/**
	 * @see es.ejie.frmk.presentacion.sesiondatos.contexto.persistencia.ManejadorPersistenciaContextoEJIE#isMantenible()
	 */
	public boolean isMantenible()
	{
		if (getManejadorPersistencia() == null)
			return isMantenible;
		return getManejadorPersistencia().isMantenible();
	}

	/**
	 * @see es.ejie.frmk.presentacion.sesiondatos.contexto.persistencia.ManejadorPersistenciaContextoEJIE#toDynaBean(java.lang.String)
	 */
	public DynaBean toDynaBean()
	{
		Q70TrazaInterface traza = Q70Factoria.getTraza();
		if (traza.isTraceActive())
		{
			traza.trace(ID_COMPONENTE, Q70TraceLevel.DEBUG, "*** INICIO toDynaBean() *****************************");
		}

		try {
			generarDynaBeanDesdeXML();
		} catch (Q70FrameworkException e) {
			Q70GestorDeExcepcionesServidor.getSingleton().recogerThrowable(e);
		}

		if (traza.isTraceActive())
		{
			traza.trace(ID_COMPONENTE, Q70TraceLevel.DEBUG, "*** FIN    toDynaBean() *****************************");
		}
		return dBeanSalida;
	}

	/**
	 * @see es.ejie.frmk.presentacion.sesiondatos.ManejadorPersistenciaContextoEJIE#doGetContexto()
	 */
	public synchronized boolean doGetContexto()
	{
		Q70TrazaInterface traza = Q70Factoria.getTraza();
		if (traza.isTraceActive())
		{
			traza.trace(ID_COMPONENTE, Q70TraceLevel.DEBUG, "*** INICIO doGetContexto() *****************************");
		}

		if (getManejadorPersistencia() != null)
		{
			return getManejadorPersistencia().doGetContexto();
		}
		
		try {
			generarDynaBeanDesdeXML();
		} catch (Q70FrameworkException e1) {
			Q70GestorDeExcepcionesServidor.getSingleton().recogerThrowable(e1);
		}

		Closure cl = new Closure()
		{
			public void execute(Object input)
			{
				String strName = ((DynaProperty) input).getName();
				try
				{
					getContextoEJIEParent().set(strName, dBeanSalida.get(strName));
				}
				catch (RuntimeException e)
				{}
			}
		};
		Collection props = new ArrayList(getContextoEJIEParent().getDynaClass().getDynaProperties().length);
		CollectionUtils.addAll(props, getContextoEJIEParent().getDynaClass().getDynaProperties());
		boolean isMantenibleAnterior=isMantenible();
		isMantenible=true;
		CollectionUtils.forAllDo(props, cl);
		isMantenible=isMantenibleAnterior;
		boolean rdo = true;

		if (traza.isTraceActive())
		{
			traza.trace(ID_COMPONENTE, Q70TraceLevel.DEBUG, "*** FIN    doGetContexto():" + rdo + " *****************************");
		}
		return rdo;
	}

	/**
	 * @see es.ejie.frmk.infraestructura.utilidades.configurador.Q70Configurable#beginConfiguration()
	 */
	public void beginConfiguration()
	{}

	/**
	 * @see es.ejie.frmk.infraestructura.utilidades.configurador.Q70Configurable#endConfiguration()
	 */
	public void endConfiguration()
	{
	}

	/**
	 * @see es.ejie.frmk.infraestructura.utilidades.configurador.Q70Configurable#beginList(java.lang.String, java.lang.String, java.lang.String)
	 */
	public void beginList(String strNombre, String strNivel, String strPath)
	{
		if (strNombre.equals("listaAtributos"))
		{
			if (datosDeContextoNuevo.getListaPropiedades() == null)
			{
				datosDeContextoNuevo.setListaPropiedades(new ArrayList(5));
			}
			return;
		}
		if (strNombre.equals("listaContextos"))
		{
			if (datosDeContextoNuevo.getListaContextos() == null)
			{
				datosDeContextoNuevo.setListaContextos(new ArrayList(5));
			}
			pilaDePadres.add(datosDeContextoNuevo);
			return;
		}
	}

	/**
	 * @see es.ejie.frmk.infraestructura.utilidades.configurador.Q70Configurable#endList(java.lang.String, java.lang.String, java.lang.String)
	 */
	public void endList(String strNombre, String strNivel, String strPath)
	{
		if (strNombre.equals("listaContextos"))
		{
			pilaDePadres.pop();
		}
	}

	/**
	 * @see es.ejie.frmk.infraestructura.utilidades.configurador.Q70Configurable#beginStructure(java.lang.String, java.lang.String, java.lang.String)
	 */
	public void beginStructure(String strNombre, String strNivel, String strPath)
	{
		if (strNombre.equals("contexto"))
		{
			if (strPath.indexOf("listaContextos/contexto") > -1)
			{
				datosDeContextoNuevo = new DatosDeContexto();
				datosDeContextoNuevo.setPadre((DatosDeContexto) pilaDePadres.peek());
			}
			else
			{
				datosDeContextoRaiz = new DatosDeContexto();
				datosDeContextoRaiz.setPadre(null);
				datosDeContextoNuevo = datosDeContextoRaiz;
				pilaDePadres = new Stack();
			}
			return;
		}
	}

	/**
	 * @see es.ejie.frmk.infraestructura.utilidades.configurador.Q70Configurable#endStructure(java.lang.String, java.lang.String, java.lang.String)
	 */
	public void endStructure(String strNombre, String strNivel, String strPath)
	{
		if (strNombre.equals("atributo"))
		{
			try
			{
				propiedad = new DynaProperty(nombreAtributo, Class.forName(tipoAtributo,true,Thread.currentThread().getContextClassLoader()));
				Object[] obj = new Object[2];
				obj[0] = propiedad;
				obj[1] = valorAtributo;
				datosDeContextoNuevo.getListaPropiedades().add(obj);
				datosDeContextoNuevo.getListaSqlCampos().add(
					new Q70DatosDeSqlCampo(nombreSqlCampo, tipoAtributo, nombreAtributo, isKey, isNoPersistente));
				inicializarVariables();
			}
			catch (ClassNotFoundException e)
			{
				Q70TrazaInterface traza = Q70Factoria.getTraza();
				if (traza.isTraceActive())
				{
					traza.trace(ID_COMPONENTE, Q70TraceLevel.ERROR, "Error en finEstructura(): " + e.getMessage());
				}
			}
			return;
		}
		if (strNombre.equals("contexto"))
		{
			if (strPath.indexOf("listaContextos/contexto") > -1)
			{
				datosDeContextoNuevo.getPadre().getListaContextos().add(crearContexto(datosDeContextoNuevo));
				datosDeContextoNuevo = datosDeContextoNuevo.getPadre();
			}
			else
			{
				Q70ContextoJerarquicoEJIE contexto = crearContexto(datosDeContextoRaiz);
				contexto.setContextoLista(datosDeContextoRaiz.getListaContextos());
				dBeanSalida = contexto;
			}
			return;
		}
	}

	/**
	 * @see es.ejie.frmk.infraestructura.utilidades.configurador.Q70Configurable#beginProperty(java.lang.String, java.lang.String, java.lang.String, java.lang.String)
	 */
	public void beginProperty(String strNombre, String strValor, String strNivel, String strPath)
	{
		if (strNombre.equals("nombreAtributo"))
		{
			nombreAtributo = strValor;
			return;
		}
		if (strNombre.equals("valorAtributo"))
		{
			valorAtributo = strValor;
			return;
		}
		if (strNombre.equals("tipoAtributo"))
		{
			tipoAtributo = strValor;
			return;
		}
		if (strNombre.equals("nombreSQL"))
		{
			nombreSqlCampo = strValor;
			return;
		}
		if (strNombre.equals("claveSQL"))
		{
			isKey = true;
			return;
		}
		if (strNombre.equals("NoPersistenteEnBD"))
		{
			isNoPersistente = true;
			return;
		}
		if (strNombre.equals("claseContexto"))
		{
			datosDeContextoNuevo.setClaseContexto(strValor);
			return;
		}
		if (strNombre.equals("tipoContexto"))
		{
			datosDeContextoNuevo.setTipoContexto(strValor);
			return;
		}
		if (strNombre.equals("idContexto"))
		{
			datosDeContextoNuevo.setIdContexto(strValor);
			return;
		}
		if (strNombre.equals("isMantenible"))
		{
			isMantenible = Boolean.valueOf(strValor).booleanValue();
			return;
		}
		if (strNombre.equals("manejadorPersistencia"))
		{
			datosDeContextoNuevo.setManejadorPersistenciaContexto(strValor);
			return;
		}
		if (strNombre.equals("dataSourceSQL"))
		{
			datosDeContextoNuevo.setSqlDataSource(strValor);
			return;
		}
		if (strNombre.equals("tablaSQL"))
		{
			datosDeContextoNuevo.setSqlTabla(strValor);
			return;
		}
		if (strNombre.equals("condicionSQL"))
		{
			datosDeContextoNuevo.setSqlWhere(strValor);
			return;
		}
	}

	/**
	 * @see es.ejie.frmk.infraestructura.utilidades.configurador.Q70Configurable#endProperty(java.lang.String, java.lang.String, java.lang.String)
	 */
	public void endProperty(String strNombre, String strNivel, String strPath)
	{}

	private Q70ContextoJerarquicoEJIE crearContexto(DatosDeContexto datosCtx)
	{
		Q70ContextoJerarquicoEJIE contextoAux = null;
		Q70TrazaInterface traza = Q70Factoria.getTraza();
		try
		{
			DynaProperty[] listaAux = new DynaProperty[datosCtx.getListaPropiedades().size()];
			for (int i = listaAux.length - 1; i >= 0; i--)
				listaAux[i] = (DynaProperty) ((Object[]) datosCtx.getListaPropiedades().get(i))[0];

			BasicDynaClass dClass = new BasicDynaClass(null, null, listaAux);
			DynaBean dBean = null;
			dBean = dClass.newInstance();

			for (int i = listaAux.length - 1; i >= 0; i--)
			{
				Object[] obj = (Object[]) datosCtx.getListaPropiedades().get(i);
				DynaProperty propAux = (DynaProperty) obj[0];
				cargarPropiedadEnDynaBean(dBean, propAux, (String) obj[1]);
			}

			Q70ManejadorPersistenciaContextoEJIE manejador = null;
			try
			{
				if (datosDeContextoNuevo.getManejadorPersistenciaContexto() == null)
				{
					manejador = new Q70PersistenciaDesdeDynaBean(dBean);
				}
				else
				{
					ArrayList listaParametros = new ArrayList(5);
					listaParametros.add(datosCtx.getSqlDataSource());
					listaParametros.add(datosCtx.getSqlTabla());
					listaParametros.add(datosCtx.getSqlWhere());
					listaParametros.add(datosCtx.getListaSqlCampos());
					Constructor c =
						Class.forName(datosDeContextoNuevo.getManejadorPersistenciaContexto()).getConstructor(new Class[] { Object.class });
					manejador = (Q70ManejadorPersistenciaContextoEJIE) c.newInstance(new Object[] { listaParametros });
					setManejadorPersistencia(manejador);
				}
			}
			catch (Q70PersistenciaMalInicializadaException e)
			{
				if (traza.isTraceActive())
				{
					traza.trace(
						ID_COMPONENTE,
						Q70TraceLevel.ERROR,
						"Error en crear persistencia por defecto, PersistenciaDesdeXML en clase:"
							+ this.getClass()
							+ "..."
							+ datosCtx.getIdContexto());
				}
				Q70GestorDeExcepcionesServidor.getSingleton().recogerThrowable(e);
			}

			Constructor con =
				Class.forName(datosCtx.getClaseContexto()).getConstructor(
					new Class[] { DynaBean.class, Q70ManejadorPersistenciaContextoEJIE.class, String.class, String.class });
			contextoAux =
				(Q70ContextoJerarquicoEJIE) con.newInstance(new Object[] { dBean, manejador, datosCtx.getTipoContexto(), datosCtx.getIdContexto()});

		}
		catch (ClassNotFoundException e)
		{
			if (traza.isTraceActive())
			{
				traza.trace(ID_COMPONENTE, Q70TraceLevel.ERROR, "Error en crearContexto():'" + e.getMessage()+"','"+e.getClass()+"','"+rutaXml+"'");
			}
		}
		catch (NoSuchMethodException e)
		{
			if (traza.isTraceActive())
			{
				traza.trace(ID_COMPONENTE, Q70TraceLevel.ERROR, "Error en crearContexto():'" + e.getMessage()+"','"+e.getClass()+"','"+rutaXml+"'");
			}
		}
		catch (IllegalAccessException e)
		{
			if (traza.isTraceActive())
			{
				traza.trace(ID_COMPONENTE, Q70TraceLevel.ERROR, "Error en crearContexto():'" + e.getMessage()+"','"+e.getClass()+"','"+rutaXml+"'");
			}
		}
		catch (InstantiationException e)
		{
			if (traza.isTraceActive())
			{
				traza.trace(ID_COMPONENTE, Q70TraceLevel.ERROR, "Error en crearContexto():'" + e.getMessage()+"','"+e.getClass()+"','"+rutaXml+"'");
			}
		}
		catch (InvocationTargetException e)
		{
			if (traza.isTraceActive())
			{
				traza.trace(ID_COMPONENTE, Q70TraceLevel.ERROR, "Error en crearContexto():'" + e.getMessage()+"','"+e.getClass()+"','"+rutaXml+"'");
			}
		}

		contextoAux.setContextoLista(datosCtx.getListaContextos());
		return contextoAux;
	}

	private void inicializarVariables()
	{
		nombreAtributo = tipoAtributo = valorAtributo = nombreSqlCampo = null;
		isKey = isNoPersistente = false;
	}

	private void cargarPropiedadEnDynaBean(DynaBean dBean, DynaProperty dProperty, String value)
	{
		Q70TrazaInterface traza = Q70Factoria.getTraza();
		
		if (!dProperty.getType().getName().equals("java.util.Properties"))
		{
			if (!dProperty.getType().getName().equals("java.lang.String") && value.equals(""))
			{
				dBean.set(dProperty.getName(), null);
			}
			else
			{
				dBean.set(dProperty.getName(), ConvertUtils.convert(value, dProperty.getType()));
			}
			return;
		}

		try
		{
			InputStream fis = Q70ContextoUtilidades.getInputStream(value);
			Properties prop = new Properties();
			prop.load(fis);
			dBean.set(dProperty.getName(), prop);
		}
		catch (NullPointerException e)
		{
			if (traza.isTraceActive())
			{
				traza.trace(
					ID_COMPONENTE,
					Q70TraceLevel.ERROR,
					e.getClass() + ":" + e.getMessage() + ". [" + dProperty.getName() + ":" + dProperty.getType() + ":" + value + "]");
			}
			Q70GestorDeExcepcionesServidor.getSingleton().recogerThrowable(e);
		}
		catch (IOException e)
		{
			if (traza.isTraceActive())
			{
				traza.trace(
					ID_COMPONENTE,
					Q70TraceLevel.ERROR,
					e.getClass() + ":" + e.getMessage() + ". [" + dProperty.getName() + ":" + dProperty.getType() + ":" + value + "]");
			}
			Q70GestorDeExcepcionesServidor.getSingleton().recogerThrowable(e);
		}
	}

	private void generarDynaBeanDesdeXML() throws Q70FrameworkException
	{
		Q70Configurador cu = new Q70Configurador(this, Q70ContextoUtilidades.getInputStream(rutaXml));
		try
		{
			cu.generateConfiguration();
		}
		catch (Q70UtilidadesExcepcion e)
		{
			String strMensaje = "Generando xml:" + this.getClass() + "...Exception:" + e.getMessage();
			if (getContextoEJIEParent() != null) {
				strMensaje = strMensaje + "...Contexto:" + getContextoEJIEParent().getTipoContexto() + ":"
						+ getContextoEJIEParent().getIdContexto();
			}
			throw new Q70FrameworkException(ID_COMPONENTE,this.getClass().getName(),"generarDynaBeanDesdeXML",strMensaje,e);
		}
	}

}
