package br.org.scout23.persistencia.implementacoes.cayenne;

import java.beans.*;
import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.List;
 
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.objectstyle.cayenne.DataObject;

import br.org.scout23.modelo.ObjetoNegocioBase;

public class ConversorPOJOCayenne {
	
    private final Log _gLogManager = LogFactory.getLog(this.getClass());
	private static final String PREFIX_PKG_CLASSES_MODELO 		= "br.org.scout23.modelo.";
	private static final String PREFIX_PKG_CLASSES_PERSISTENCIA = "br.org.scout23.logic.cayenne.";
		
	public void objPersistenteParaObjModelo(DataObject pObjPersist, 
											ObjetoNegocioBase pObjBeanAlvo,
											boolean pBuscarVinculados) 
		throws IllegalArgumentException, 
				InvocationTargetException, 
				IntrospectionException, 
				ClassNotFoundException, 
				InstantiationException, 
				IllegalAccessException, 
				SecurityException, 
				NoSuchMethodException {

		BeanInfo 			wBeanInfo = null;
		MethodDescriptor[] 	wDescrMetodosAlvo = null;
		MethodDescriptor[] 	wDescrMetodosOrigem = null;
		Method 				wMetodoAlvo = null;
		Method 				wMetodoOrigem = null;
		Method				wMetodoInvocar = null;
		Object				wValorSet	= null;
		String 				wTempCls = null;
		Class 				wClasseInvoker = null;
		Object 				wArgList[] = null;
		Class 				wParTypes[] = null;
				
		wClasseInvoker = pObjBeanAlvo.getClass(); // Class.forName( wTempCls );
		// Destrinchar Objeto Alvo 
		wBeanInfo = Introspector.getBeanInfo( wClasseInvoker );
		wDescrMetodosAlvo = wBeanInfo.getMethodDescriptors();
		
		// Se existir métodos, destrinchar objeto origem 
		if (wDescrMetodosAlvo != null && wDescrMetodosAlvo.length > 0) {
			wBeanInfo = Introspector.getBeanInfo( pObjPersist.getClass() );
			wDescrMetodosOrigem = wBeanInfo.getMethodDescriptors();
			pObjBeanAlvo.setEhDerivadoPersistente(1);
			pObjBeanAlvo.setNomeClassePersistenteDerivado(pObjPersist.getClass().toString() );
			// Para cada método no obj. Alvo, procura o correspondente no obj. Origem
			for (int i = 0; i < wDescrMetodosAlvo.length; i++) {
				wMetodoAlvo = wDescrMetodosAlvo[i].getMethod();

				if (wMetodoAlvo.getName().startsWith("set")) {
					for (int j = 0; j < wDescrMetodosOrigem.length; j++) {
						wMetodoOrigem = wDescrMetodosOrigem[j].getMethod();
						if (wMetodoOrigem.getName().startsWith("get") &&
							wMetodoOrigem.getName().substring(3).equals(wMetodoAlvo.getName().substring(3))) {

							// Pega o valor no Obj. Origem
							wValorSet = wMetodoOrigem.invoke(pObjPersist, null);
							// Seta o valor no respectivo método
							if ((wValorSet != null) && !((wValorSet instanceof List) && (!pBuscarVinculados)) ) {
								wArgList = new Object[wMetodoAlvo.getParameterTypes().length];
								wParTypes = new Class[wMetodoAlvo.getParameterTypes().length];
								for (int x = 0; x < wParTypes.length; x++) {
									Class wTipoParam = wMetodoAlvo.getParameterTypes()[x];
									wArgList[x] = this.tratarTipoParametros(wParTypes, x, wTipoParam, wValorSet);
								} // for x
								wMetodoInvocar = wClasseInvoker.getMethod(wMetodoAlvo.getName(), wParTypes);
								// Executa o metodo
								wMetodoInvocar.invoke( pObjBeanAlvo, wArgList);
								break; 
							} // if ValorSet == null
						} // if getXXX = setXXX												
					} // for Origem
				} // if set

			} // for Alvo
			
		}
		_gLogManager.debug("ObjOrigem = " + pObjPersist);
		_gLogManager.debug("ObjAlvo = " + pObjBeanAlvo);

	} // Fim do Método

	public void objNegocioParaObjPersistente(ObjetoNegocioBase pObjModeloOrigem,
											 DataObject pObjPersist) 
		throws IllegalArgumentException, 
				InvocationTargetException, 
				IntrospectionException, 
				ClassNotFoundException, 
				InstantiationException, 
				IllegalAccessException, 
				SecurityException, 
				NoSuchMethodException {

		BeanInfo 			wBeanInfo = null;
		MethodDescriptor[] 	wDescrMetodosAlvo = null;
		MethodDescriptor[] 	wDescrMetodosOrigem = null;
		Method 				wMetodoAlvo = null;
		Method 				wMetodoOrigem = null;
		Method				wMetodoInvocar = null;
		Object				wValorSet	= null;
		String 				wTempCls = null;
		Class 				wClasseInvoker = null;
		Object 				wArgList[] = null;
		Class 				wParTypes[] = null;
				
		wClasseInvoker = pObjPersist.getClass(); // Class.forName( wTempCls );
		// Destrinchar Objeto Alvo 
		wBeanInfo = Introspector.getBeanInfo( wClasseInvoker );
		wDescrMetodosAlvo = wBeanInfo.getMethodDescriptors();
		
		// Se existir métodos, destrinchar objeto origem 
		if (wDescrMetodosAlvo != null && wDescrMetodosAlvo.length > 0) {
			wBeanInfo = Introspector.getBeanInfo( pObjModeloOrigem.getClass() );
			wDescrMetodosOrigem = wBeanInfo.getMethodDescriptors();
			// Para cada método no obj. Alvo, procura o correspondente no obj. Origem
			for (int i = 0; i < wDescrMetodosAlvo.length; i++) {
				wMetodoAlvo = wDescrMetodosAlvo[i].getMethod();
				if (wMetodoAlvo.getName().startsWith("set")) {
					for (int j = 0; j < wDescrMetodosOrigem.length; j++) {
						wMetodoOrigem = wDescrMetodosOrigem[j].getMethod();
						if (wMetodoOrigem.getName().startsWith("get") &&
							wMetodoOrigem.getName().substring(3).equals(wMetodoAlvo.getName().substring(3))) {

							// Pega o valor no Obj. Origem
							wValorSet = wMetodoOrigem.invoke(pObjModeloOrigem, null);
							// Seta o valor no respectivo método
							if (wValorSet != null) {
								wArgList = new Object[wMetodoAlvo.getParameterTypes().length];
								wParTypes = new Class[wMetodoAlvo.getParameterTypes().length];
								for (int x = 0; x < wParTypes.length; x++) {
									Class wTipoParam = wMetodoAlvo.getParameterTypes()[x];
									wArgList[x] = this.tratarTipoParametros(wParTypes, x, wTipoParam, wValorSet);
								} // for x
								wMetodoInvocar = wClasseInvoker.getMethod(wMetodoAlvo.getName(), wParTypes);
								// Executa o metodo
								wMetodoInvocar.invoke( pObjPersist, wArgList);
								break; 
							} // if ValorSet == null
						} // if getXXX = setXXX												
					} // for Origem
				} // if set

			} // for Alvo
			
		}
		_gLogManager.debug("ObjOrigem = " + pObjModeloOrigem);
		_gLogManager.debug("ObjAlvo = " + pObjPersist);

	} // Fim do Método

	private Object tratarTipoParametros(Class[] pParTypes, int x, Class pClassTipo, Object pValorSet) 
		throws IllegalArgumentException, 
				SecurityException, 
				InvocationTargetException, 
				IntrospectionException, 
				ClassNotFoundException, 
				InstantiationException, 
				IllegalAccessException, 
				NoSuchMethodException {

		
		List 		vecTempPersist		 = null;
		List		vecTempBean			 = null;
		Object 		wObjReturn			 = pValorSet;
		ObjetoNegocioBase wBeanTemp		 = null; 
		DataObject wPersistTemp	 = null;
		List 		wListQQTemp			 = null;
		
		if ( pClassTipo.getName().equalsIgnoreCase("int") ) {
			pParTypes[x] = Integer.TYPE;	
		} else if ( pClassTipo.getName().equalsIgnoreCase("long") ) {
			pParTypes[x] = Long.TYPE;
		} else if ( pClassTipo.getName().equalsIgnoreCase("java.lang.String") ) {
			pParTypes[x] = pClassTipo;
		} else if ( pClassTipo.getName().equalsIgnoreCase("boolean") ) {
			pParTypes[x] = Boolean.TYPE;
		} else if ( pClassTipo.getName().equalsIgnoreCase("double") ) {
			pParTypes[x] = Double.TYPE;
		} else if ( pClassTipo.getName().equalsIgnoreCase("float") ) {
			pParTypes[x] = Float.TYPE;
		} else if ( pClassTipo.getName().equalsIgnoreCase("byte") ) {
			pParTypes[x] = Byte.TYPE;
		} else if ( pClassTipo.getName().equalsIgnoreCase("short") ) {
			pParTypes[x] = Short.TYPE;
		} else if ( pClassTipo.getName().equalsIgnoreCase("java.util.Date") ) {
			pParTypes[x] = pClassTipo;
		} else if ( pClassTipo.getName().equalsIgnoreCase("java.util.List") ) {
			wObjReturn = null;
			if (pValorSet != null && pValorSet instanceof List) {
				// Abrir a Colecao e pegar um item para ver de qual tipo é a sua Classe
				wListQQTemp = (List) pValorSet;
				if (wListQQTemp.size() > 0) {
					if ( wListQQTemp.get(0).getClass().getName().startsWith(PREFIX_PKG_CLASSES_PERSISTENCIA) ) {
						// CayenneEntidadeBasica wCayQQTemp = (CayenneEntidadeBasica) wListQQTemp.get(0);
				    	String wNomeClasse = wListQQTemp.get(0).getClass().getName();
				    	wNomeClasse = wNomeClasse.substring(wNomeClasse.lastIndexOf(".") + 1);
						wObjReturn = this.colecaoPersistEmColecaoObjModelo(wListQQTemp, 
																		   Class.forName(PREFIX_PKG_CLASSES_MODELO + wNomeClasse),
																		   true); // wCayQQTemp.getNomeOficialEntidade()));
					} else if ( wListQQTemp.get(0).getClass().getName().startsWith(PREFIX_PKG_CLASSES_MODELO) ) {
						ObjetoNegocioBase wNegQQTemp = (ObjetoNegocioBase) wListQQTemp.get(0);
						wObjReturn = this.colecaoObjNegEmColecaoPersist(wListQQTemp, 
																		   Class.forName(PREFIX_PKG_CLASSES_PERSISTENCIA + wNegQQTemp.getNomeOficialEntidade()));
					}
				}
			}
			pParTypes[x] = pClassTipo;
		} else if ( pClassTipo.getName().startsWith("br.") ) {
			if ( pClassTipo.getName().startsWith(PREFIX_PKG_CLASSES_MODELO) ) {
				wBeanTemp = (ObjetoNegocioBase) pClassTipo.newInstance();
				this.objPersistenteParaObjModelo((DataObject) pValorSet, wBeanTemp, false);
				wObjReturn = wBeanTemp; 
			} else if ( pClassTipo.getName().startsWith(PREFIX_PKG_CLASSES_PERSISTENCIA) ) {
				wPersistTemp = (DataObject) pClassTipo.newInstance();
				this.objNegocioParaObjPersistente((ObjetoNegocioBase) pValorSet, wPersistTemp);
				wObjReturn = wPersistTemp; 
			}				
			pParTypes[x] = pClassTipo;
		} else {
			pParTypes[x] = pClassTipo;
			// throw new IntrospectionException("==> ReflectionSIATC.converterPersistenteParaBean(): TIPO NÃO DEFINIDO: " + pClassTipo.getName());	
		}
		return wObjReturn;
	}

	public List colecaoPersistEmColecaoObjModelo(List vecPersist, Class pClasse, boolean pBuscarVinculados) 
		throws IllegalArgumentException, 
				SecurityException, 
				InvocationTargetException, 
				IntrospectionException, 
				ClassNotFoundException, 
				InstantiationException, 
				IllegalAccessException,
				NoSuchMethodException {
		
		List vecBeans = null;
		DataObject wObjPersist = null;
		ObjetoNegocioBase wBean = null;
		
		if (vecPersist != null && vecPersist.size() > 0) {
			vecBeans = new ArrayList(vecPersist.size());
			for (int i = 0; i < vecPersist.size(); i++) {
				if (vecPersist.get(i) instanceof DataObject) {
					wObjPersist = (DataObject) vecPersist.get(i);
					wBean = (ObjetoNegocioBase) pClasse.newInstance();
					this.objPersistenteParaObjModelo(wObjPersist, wBean, pBuscarVinculados);
					vecBeans.add(wBean);
				} else if (vecPersist.get(i) instanceof List) {
					List wVecAux = (List) vecPersist.get(i); 
					vecBeans = this.colecaoPersistEmColecaoObjModelo(wVecAux, pClasse, pBuscarVinculados);
				} else if (vecPersist.get(i) instanceof Number) { // .getClass().isPrimitive()) {
					vecBeans.add(vecPersist.get(i));
				}
			}		
		}
		return vecBeans;
	}
	
	public List colecaoObjNegEmColecaoPersist(List pListBeans, Class pClasse) 
		throws IllegalArgumentException, 
				SecurityException, 
				InvocationTargetException, 
				IntrospectionException, 
				ClassNotFoundException, 
				InstantiationException, 
				IllegalAccessException, 
				NoSuchMethodException {
		
		List vecResults = null;
		DataObject wObjPersist = null;
		ObjetoNegocioBase wBean = null;
		
		if (pListBeans != null && pListBeans.size() > 0) {
			vecResults = new ArrayList(pListBeans.size());
			for (int i = 0; i < pListBeans.size(); i++) {
				wBean = (ObjetoNegocioBase) pListBeans.get(i);
				wObjPersist = (DataObject) pClasse.newInstance();
				this.objNegocioParaObjPersistente(wBean, wObjPersist);
				vecResults.add(wObjPersist);
			}		
		}
		return vecResults;
	}

}


