/*
 * AutoConfigurer.java
 *
 */

package com.uvigo.tMaestro.SCO;

import java.lang.reflect.Constructor;

/**
 * Todos los SCO's llevaran incorporada esta clase que sera la encargada
 * de configurarlos en el STB de acuerdo con la opcion indicada.
 * La opcion se decide en la clase Adaption, que toma esta decision de acuerdo
 * con los parametros de adaptacion expresados en el fichero de adaptacion y los valores
 * consultados al LMS de dichos parametros.
 * @author raul
 */
public class AutoConfigurer {
    
    
    /**
     * Esta funcion recorre todos los componentes de la plantilla oTemplate y les
     * aplica a sus propiedades los valores indicados en el fichero de configuracion
     * sOption.
     * @param oTemplate La referencia a la plantilla
     * @param sOption El nombre del fichero de la opcion que ser quiere aplicar.
     */
    public void configure(com.uvigo.mhp.Page oTemplate,String sOption) {
        //Leemos el contenido del fichero sOption y para cada elemento leido
        //aplicamos los valores de las propiedades indicadas en el fichero a las propiedades
        //del elemento.
        try {
            //Creamos el JAXBContext y el Unmarshaller que nos serviran de ayuda
            //para leer los ficheros .xml de opciones.
            javax.xml.bind.JAXBContext oJC = javax.xml.bind.JAXBContext.newInstance("com.uvigo.tMaestro.SCO.options");
            javax.xml.bind.Unmarshaller oUn = oJC.createUnmarshaller();
            com.uvigo.tMaestro.SCO.options.Option oOptionElement;
            
            //Obtenemos el elemento raiz "opcion" del fichero de la opcion.
            
            oOptionElement = (com.uvigo.tMaestro.SCO.options.Option) oUn.unmarshal(this.getClass().getResource("/"+sOption));
            
            
            java.util.List oComponentElements= oOptionElement.getComponents().getComponent(); //Lista de componentes
            for (int i=0;i<oComponentElements.size();i++) {
                com.uvigo.tMaestro.SCO.options.ComponentType oComponentElement = (com.uvigo.tMaestro.SCO.options.ComponentType) oComponentElements.get(i);
                //Obtenemos la referencia al elemento de la plantilla
                java.awt.Component oTemplateComponent = searchComponent(oTemplate,oComponentElement.getName());
                //si no encontramos un elemento con el nombre indicado seguimos con los dem�s
                if (oTemplateComponent==null) continue;
                java.util.List oPropertyElements = oComponentElement.getProperties().getProperty(); //La lista de propiedades del elemento
                for (int y=0;y<oPropertyElements.size();y++) {
                    com.uvigo.tMaestro.SCO.options.PropertyType oPropertyElement = (com.uvigo.tMaestro.SCO.options.PropertyType) oPropertyElements.get(y);
                    //Debemos obtener el valor de la propiedad como un objeto para poderselo pasar al m�todo set de esta propiedad
                    Object oValue = getValue(oPropertyElement);
                    if (oValue==null) {
                        System.out.println(oPropertyElement.getName());
                        continue; //Si el valor no puede ser convertido continuamos con las dem�s propiedades
                    }
                    //Intentamos establecer el valor
                    setValue(oTemplateComponent,oPropertyElement.getName(),oValue,oComponentElement.getComponentClass(), oPropertyElement.getPropertyClass());
                }
                
            }
        } catch(Exception oE) {
            oE.printStackTrace();
        }
    }
    
    /**
     * Busca en oTemplate el Componente de nombre sName
     * Esta funci�n trabaja con la susposici�n de que todos los componentes de la plantilla
     * han sido nombrados en el tiempo de dise�o, y que los nombres son �nicos.
     * @param oTemplate La plantilla en la que buscar
     * @param sName El nombre del componente a buscar
     * @return La referencia en la plantilla al componente de nombre sName. Null si no lo ha encontrado
     */
    private static java.awt.Component searchComponent(java.awt.Container oTemplate,String sName) {
        if (oTemplate.getName().equals(sName)) return oTemplate;
        java.awt.Component[] oComponents = oTemplate.getComponents();
        if (oComponents==null) return null;
        for (int i=0;i<oComponents.length;i++) {
            java.awt.Component oComponent = oComponents[i];
            if (oComponent.getName()!=null && oComponent.getName().equals(sName)) return oComponent;
            if (oComponent instanceof com.uvigo.mhp.ui.HScrollPane) {
                java.awt.Component oScrollComponent = ((com.uvigo.mhp.ui.HScrollPane)oComponent).getViewport().getView();
                if (oScrollComponent.getName().equals(sName)) return oScrollComponent;
            } else if (oComponent instanceof java.awt.Container) {
                java.awt.Component oAnswer = searchComponent((java.awt.Container) oComponent,sName);
                if (oAnswer!=null) return oAnswer;
            }
        }
        return null;
    }
    
    /**
     * Este m�todo obtiene a partir de un PropertyType el valor de la propiedad
     * como un objecto de su clase. Despues lo devolvemos haciendo un cast a Object
     * para que pueda ser utilizado con las classes del package reflect de Java.
     * @param oProperty La propiedad leida del fichero de configuraci�n .xml
     * @return El valor de la propiedad como un objecto
     */
    private static Object getValue(com.uvigo.tMaestro.SCO.options.PropertyType oProperty) {
        try {
            if (oProperty.getType().equals("primitive")) { //Es un tipo primitivo
                String sClass = oProperty.getPropertyClass();
                return getPrimitiveValue(sClass,oProperty.getValue());
            } else { //Es un objeto java
                //Obtenemos la lista de par�metros
                java.util.List oParams = oProperty.getParam();
                Object[] oObjectParams = new Object[oParams.size()]; //Contendr� los valores de los par�metros
                Class[] oClassParams = new Class[oParams.size()]; //Contendr� las clases de los par�metros
                for (int i=0; i<oObjectParams.length; i++) {
                    com.uvigo.tMaestro.SCO.options.PropertyType.ParamType oParam =(com.uvigo.tMaestro.SCO.options.PropertyType.ParamType) oParams.get(i);
                    oObjectParams[i] = getPrimitiveValue(oParam.getParamClass(), oParam.getValue());
                    oClassParams[i] = getType(oParam.getParamClass());
                }
                //Necesitamos un constructor de la clase de la propiedad para generar el Objeto
                Constructor oConstructor = Class.forName(oProperty.getPropertyClass()).getConstructor(oClassParams);
                //Generamos el objeto y lo devolvemos
                return oConstructor.newInstance(oObjectParams);
            }
        } catch(Exception oE) {
            return null; //Si se produce una excepci�n devolvemos null para no aplicar el valor
        }
    }
    
    /**
     * Convierte sValue (una String) en un objeto de la clase sClass
     * @param sClass La clase del objeto en que se quiere convertir sValue
     * @param sValue El valor del objeto como una String
     * @return Un objeto de la clase requerida.
     */
    private static Object getPrimitiveValue(String sClass,String sValue) {
        if (sClass.equals("java.lang.Integer")) return new Integer(sValue);
        else if (sClass.equals("java.lang.Long")) return new Long(sValue);
        else if (sClass.equals("java.lang.Boolean")) return new Boolean(sValue);
        else if (sClass.equals("java.lang.Float")) return new Float(sValue);
        else if (sClass.equals("java.lang.Double")) return new Double(sValue);
        else if (sClass.equals("java.lang.Character")) return new Character(sValue.charAt(0));
        else if (sClass.equals("java.lang.Byte")) return new Byte(sValue);
        else if (sClass.equals("java.lang.String")) return sValue;
        else if (sClass.equals("java.lang.Short")) return new Short(sValue);
        else return null; //Tipo desconocido devuelve null
    }
    
    /**
     * Esta funci�n invoca al m�todo set de la propiedad de nombre sName
     * en el componente oComponent. Le asigna el valor indicado por oValue.
     * @param oComponent El elemento de plantilla sobre la que invocar el m�todo set.
     * @param sName El nombre de la propiedad cuyo valor se desea cambiar.
     * @param oValue El nuevo valor a asignar
     * @param sComponentClass La clase de oComponent
     * @param sValueClass La clase de la propiedad
     */
    private static void setValue(java.awt.Component oComponent,String sName,Object oValue,String sComponentClass,String sValueClass) {
        if (sName.equals("Z") && oComponent.getParent()!=null) {
            //oComponent.getParent().setComponentZOrder(oComponent, (Integer)oValue);
            if ((((Integer) oValue).intValue() == 0) && (oComponent.getParent() instanceof com.uvigo.mhp.ui.HMultiPanel)) ((java.awt.CardLayout)oComponent.getParent().getLayout()).first(oComponent.getParent());
        } else if (sName.equals("X")) oComponent.setLocation(((Integer) oValue).intValue(), oComponent.getY());
        else if (sName.equals("Y")) oComponent.setLocation(oComponent.getX(), ((Integer) oValue).intValue());
        else if (sName.equals("Width")) oComponent.setSize(((Integer) oValue).intValue(), oComponent.getHeight());
        else if (sName.equals("Height")) oComponent.setSize(oComponent.getWidth(),((Integer) oValue).intValue());
        else {
            Class[] oClasses = new Class[1];
            Object[] oValues  = new Object[1];
            try {
                oClasses[0]=oValue.getClass();
                java.lang.reflect.Method oSetMethod= Class.forName(sComponentClass).getMethod("set"+sName.substring(0,1).toUpperCase()+sName.substring(1),oClasses);
                oValues[0]=oValue;
                oSetMethod.invoke(oComponent, oValues);
            } catch(Exception oE) { //Si se produce una excepci�n se sale si haber conseguido establecer el valor
                //Deber�amos ver si puede que se haya producido una excepci�n  porque ValueClass sea un tipo primitivo
                Class oType = getType(sValueClass);
                if (!oType.isPrimitive()) {
                    oE.printStackTrace();
                    return;
                }
                try {
                    oClasses[0]=oType;
                    java.lang.reflect.Method oSetMethod= Class.forName(sComponentClass).getMethod("set"+sName.substring(0,1).toUpperCase()+sName.substring(1),oClasses);
                    oValues[0]=oValue;
                    oSetMethod.invoke(oComponent, oValues);
                } catch(Exception oE2) {
                    oE2.printStackTrace();
                }
            }
        }
    }
    
    /**
     * Devuelve el tipo primitivo, de sClass, si se puede convertir, devuelve la misma
     * clase sei no se ha podido convertir.
     * @param sClass La clase cuyo tipo primitivo se desea
     * @return La clase correspondiente a sClass (sera un tipo primitivo si corresponde)
     */
    public static Class getType(String sClass) {
        Class oType=null;
        if (sClass.equals("java.lang.Integer")) oType = Integer.TYPE;
        else if (sClass.equals("java.lang.Boolean")) oType = Boolean.TYPE;
        else if (sClass.equals("java.lang.Float")) oType = Float.TYPE;
        else if (sClass.equals("java.lang.Byte")) oType = Byte.TYPE;
        else if (sClass.equals("java.lang.Double")) oType = Double.TYPE;
        else if (sClass.equals("java.lang.Long")) oType = Long.TYPE;
        else if (sClass.equals("java.lang.Short")) oType = Short.TYPE;
        else if (sClass.equals("java.lang.Character")) oType = Character.TYPE;
        else {
            try {oType = Class.forName(sClass);} catch(Exception oE) {return null;}
        }
        return oType;
    }
    
    
}
