/*
 * OptionsGenerator.java
 *
 */
package com.uvigo.tMaestro.SCOCreator.tool.optionGenerator;

import com.uvigo.tMaestro.SCOCreator.tool.MainFrame;
import com.uvigo.tMaestro.SCOCreator.tool.Capsula;
import com.uvigo.tMaestro.SCOCreator.tool.MyOptionPane;
import com.uvigo.tMaestro.SCOCreator.tool.properties.Propiedad;
import com.uvigo.tMaestro.SCOCreator.tool.MyDirectoryFilter;
import com.uvigo.tMaestro.SCO.options.ComponentType;
import com.uvigo.tMaestro.SCO.options.ComponentsType;
import com.uvigo.tMaestro.SCO.options.Option;
import com.uvigo.tMaestro.SCO.options.PropertiesType;
import com.uvigo.tMaestro.SCO.options.PropertyType;
import com.uvigo.tMaestro.SCO.options.ObjectFactory;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Reader;
import java.net.URL;

/**
 * Clase base del paquete "Generador de opciones". Es la clase encargada de la generaci�n de los ficheros de configuraci�n de las opciones.
 * Estos ficheros son ficheros .xml con nombre el de la opci�n. Es decir, si una opci�n se llama f�tbol, se generar� el fichero f�tbol .xml
 * @author raul
 */
public class OptionsGenerator {

    /**
     * Mantiene una relaci�n con los PropertyWriter conocidos por el programa.
     * Cada PropertyWriter se dise�a para una clase Java.
     * Cada uno de estos PropertyWriter sabe como escribir la propiedad de su tipo a las estructuras
     * que posteriormente se utilizar�n para generar los ficheros .xml
     */
    private java.util.HashMap<Class, PropertyWriter> oPropertiesWriters;

    /**
     * Constructor de la clase. Carga la relaci�n de PropertiesWriters de la aplicaci�n
     */
    public OptionsGenerator() {
        oPropertiesWriters = new java.util.HashMap<Class, PropertyWriter>();

        //Cargamos din�micamente los PropertiesWriters del directorio de propertiesWriters
        try {
            //Buscamos los ficheros .class que sean PropertyWriters
            //Programamos dos comportamientos: si la aplicaci�n se ejecuta desde
            //un jar o desde el �rbol de directorios
            java.net.URI oURI = (this.getClass().getResource("")).toURI();
            java.util.ArrayList<String> oClasses = new java.util.ArrayList<String>();
            if (oURI.getScheme().equals("jar")) { //La aplicaci�n se ejecuta desde el jar
                //SSSS cambiamos SCOCreator.jar
                //TODO cambiamos JarFile por JarInputStream
                //java.util.jar.JarFile oJarFile = new java.util.jar.JarFile("ReloadTotalv4.jar", false);
                String packageUrl = this.getClass().getClassLoader().getResource(this.getClass().getPackage().getName().replace(".", "/")).toString();
                String reloadUrl = packageUrl.substring(packageUrl.indexOf(":") + 1, packageUrl.indexOf("!"));
                java.util.jar.JarInputStream oJarInputStream = new java.util.jar.JarInputStream(new URL(reloadUrl).openStream(), false);
                //String[] aRenders = oJarFile.getManifest().getMainAttributes().getValue("PropertiesWriters").split(" ");
                String[] aRenders = oJarInputStream.getManifest().getMainAttributes().getValue("PropertiesWriters").split(" ");
                oClasses.addAll(java.util.Arrays.asList(aRenders));
            } else { // La aplicaci�n se ejecuta desde el �rbol de directorios
                java.io.File oProperties = new java.io.File((this.getClass().getResource("")).toURI());
                MyDirectoryFilter oFiltro = new MyDirectoryFilter();
                oFiltro.vAddName("propertiesWriters");
                java.io.File oCellEditors = oProperties.listFiles(oFiltro)[0]; //Directorio propertiesWriters
                java.io.File[] oNombres = oCellEditors.listFiles();
                int i = 0;
                for (i = 0; i < oNombres.length; i++) oClasses.add(oNombres[i].getName());
            }

            int i = 0;
            for (i = 0; i < oClasses.size(); i++) { //Cargamos cada una de las classes correspondientes a un PropertyWriter
                //Descartamos los ficheros que no sean .class
                if (!oClasses.get(i).contains(".class")) continue;
                String sNombre = (oClasses.get(i).split(".class"))[0]; //Nos quedamos con el nombre de la clase
                Class oClase = Class.forName("com.uvigo.tMaestro.SCOCreator.tool.optionGenerator.propertiesWriters." + sNombre);
                //Instanciamos el PropertyWriter y lo a�adimos a la hastable
                PropertyWriter oInstancia = (PropertyWriter) oClase.newInstance();
                oPropertiesWriters.put(oInstancia.getTypeProperty(), oInstancia);
            }
        } catch (Exception oE) {
            oE.printStackTrace();
            MyOptionPane.showMessageDialog(MainFrame.oMainFrame, MainFrame.oMainFrame.getMessage("Error19"), MainFrame.oMainFrame.getMessage("Title2"), javax.swing.JOptionPane.WARNING_MESSAGE);
            System.exit(-1);
        }
    }

    /**
     * Funci�n que genera los ficheros de configuraci�n, incluyendo en ellos s�lo las propiedades
     * que han sido modificadas por el usuario respecto a su valor original en la plantilla (fichero .class)
     * @param oMainFrame Referencia al MainFrame de la herramienta
     * @throws java.lang.Exception Si se produce alg�n error durante la generaci�n de los ficheros .xml
     */
    public void generateConfigurationFiles(MainFrame oMainFrame) throws Exception {
        //Primeramente vemos si est�n cargados
        //El directorio del proyecto en este punto se supone creado. Obtenemos su ruta
         java.io.File oProjectDir = oMainFrame.fTemp; //M: carpeta TEMP
        //Obtenemos una lista con todos los componentes de la plantilla (capsulas)
        java.util.ArrayList<Capsula> oCapsulas = new java.util.ArrayList<Capsula>();
        oCapsulas.add(MainFrame.oMainFrame.oGetPlantilla());
        Capsula.getCapsulas(oCapsulas, MainFrame.oMainFrame.oGetPlantilla());

        //Para cada una de las opciones contempladas debemos crear un fichero de configuraci�n
        int i = 0;
        for (i = 0; i < oMainFrame.sizeOptions(); i++) {
            String sOptionName = oMainFrame.getOption(i); //Nombre de la opci�n
            //java.io.File oOptionsDir = new java.io.File(oProjectDir, "Options");
            java.io.File oOptionsDir = new java.io.File(oProjectDir, "OPTIONS");
            File fichero = new java.io.File(oOptionsDir, sOptionName + ".xml");
            java.io.FileOutputStream oFOS = new java.io.FileOutputStream(new java.io.File(oOptionsDir, sOptionName + ".xml"));
            ObjectFactory oFactory = new ObjectFactory(); //El ObjectFactory para crear las estructuras que luego ser�n portadas a .xml
            try {
                Option oOption = oFactory.createOption();
                oOption.setName(sOptionName);
                ComponentsType oComponents = oFactory.createComponentsType();
                //Para cada opci�n debemos recorrer todos los componentes y anotar aquellas propiedades
                //que hayan sido modificadas por el usuario respecto a los valores originales de plantilla

                for (int y = 0; y < oCapsulas.size(); y++) { //Recorremos todos los componentes de la plantilla

                    boolean bHasModifiedProperty = false;
                    ComponentType oComponent = oFactory.createComponentType();    //Elemento component en .xml
                    PropertiesType oProperties = oFactory.createPropertiesType(); //Elemento properties en .xml
                    oComponent.setProperties(oProperties);
                    oComponent.setName(oCapsulas.get(y).oGetBean().getName()); //Nombre del componente
                    oComponent.setComponentClass(oCapsulas.get(y).oGetBean().getClass().getName());
                    //Primero comprobamos si las propiedades de tama�o y dimensi�n han cambiado
                    bHasModifiedProperty = checkSizeAndPositionProperties(oFactory, oProperties, oCapsulas.get(y), i);

                    //Ahora debemos comprobar el resto de las propiedades. Para ellos las recorremos todas
                    java.util.ArrayList<Propiedad> oPropertiesList = oCapsulas.get(y).oGetPropiedades();
                    for (int z = 0; z < oPropertiesList.size(); z++) {
                        //Vemos si el valor de la propiedad en la opci�n ha cambiado respecto a su valor original
                        if (oPropertiesList.get(z).isPropertyChanged(i)) {
                            //Si la propiedad ha sido modificada la anotamos en las estructuras para pasar al .xml
                            bHasModifiedProperty = true;
                            PropertyType oProperty = oFactory.createPropertyType(); //Elemento propiedad en .xml
                            oProperty.setName(oPropertiesList.get(z).oGetName());
                            //Averiguamos el PropertyWriter de esta propiedad
                            PropertyWriter oPropertyWriter = null;
                            if (oPropertiesList.get(z).isMutable()) oPropertyWriter = oPropertiesWriters.get(Class.class);
                            else oPropertyWriter = oPropertiesWriters.get(oPropertiesList.get(z).oGetOptionValue(i).getClass());
                            if (oPropertyWriter != null) oPropertyWriter.writeProperty(oProperty, oPropertiesList.get(z).oGetOptionValue(i), oFactory);
                            else { //Estas l�neas hacen de propertyWriter por defecto. Los tipos primitivos y los que no tengan
                                //PropertyWriter las usar�n
                                oProperty.setType("primitive");
                                oProperty.setPropertyClass(oPropertiesList.get(z).oGetOptionValue(i).getClass().getName());
                                //oProperty.setValue(String.valueOf(oPropertiesList.get(z).oGetOptionValue(i)));
                                //M: queremos que en el fichero de opciones no se haga referencia a TEMP/
                                String sValue = String.valueOf(oPropertiesList.get(z).oGetOptionValue(i));
                                if(sValue.contains("TEMP")) sValue=sValue.substring(5);
                                oProperty.setValue(sValue);
                            }
                            oProperties.getProperty().add(oProperty);
                        }
                    }
                    //Si ha alguna propiedad modificada en el componente actual entonces lo a�adimos
                    //Est� claro que si no hay ninguna, no es necesario a�adirlo (los tags estar�an vac�os)
                    if (bHasModifiedProperty) {
                        oComponents.getComponent().add(oComponent);
                    }
                }

                oOption.setComponents(oComponents); //A�adimos los componentes al elemento opci�n
                //Creamos el fichero xml
                javax.xml.bind.JAXBContext oJC = javax.xml.bind.JAXBContext.newInstance("com.uvigo.tMaestro.SCO.options");
                javax.xml.bind.Marshaller oM = oJC.createMarshaller();
                oM.setProperty(javax.xml.bind.Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
                oM.marshal(oOption, oFOS);
                oFOS.flush();
                oFOS.close();
/*
                 File fichero2 = new File(oOptionsDir, sOptionName + "_aux.xml");

                    BufferedWriter outi = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fichero2), "UTF8"));
                    Reader ini = new BufferedReader(new InputStreamReader(new FileInputStream(fichero), "UTF8"));
                    int ch;
                    while ((ch = ini.read()) > -1) {
                        outi.write(transformaValor2((char) ch));
                    }
                    ini.close();
                    outi.close();
                fichero.delete();
                fichero2.renameTo(fichero);

*/
                 /*
                OutputStream out = new FileOutputStream(fichero2);
                BufferedInputStream in = new BufferedInputStream(new FileInputStream(fichero));
                byte[] buf = new byte[1024];

                String thisLine;
                int len;
                int total = 0;


                while ((len = in.read(buf)) != -1) {
                    total = total + len;
                    thisLine = transformaValor(buf, len);
                    //System.out.println(thisLine);
                    out.write(thisLine.getBytes(), 0, thisLine.length());//, 0, thisLine.getBytes().length);

                }
                in.close();
                out.close();
*/
                
            } catch (Exception oE) {
                oE.printStackTrace();
                //Si se produce un error borramos los ficheros .xml generados hasta el momento
                if (oFOS != null) oFOS.close();
                java.io.File[] oFiles = oProjectDir.listFiles();
                if (oFiles == null) return;
                for (java.io.File oFile : oFiles) {
                    if (oFile.getName().contains(".xml")) oFile.delete();
                }
                throw new Exception(MainFrame.oMainFrame.getMessage("Error18"));
            }


        }
    }

    /**
     * Esta funci�n comprueba si las propiedades de Posici�n y tama�o y visibilidad de oTemplateComponent en la opci�n i
     * han cambiado respecto al valor original de plantilla. Las a�ade en caso afirmativo a oProperties.
     * @return True si alguna propiedad ha cambiado, false en caso contrario
     * @param oFactory La factoria que crea las estructuras relacionadas con la generaci�n del fichero .xml
     * @param oProperties Las propiedades que se a�adir�n al .xml
     * @param oCapsula El componente del que se comprobar�n las propiedades
     * @param i El �ndice de la opci�n de la que se est�n comprobando los valores
     * @throws javax.xml.bind.JAXBException Si se produce algun error en la generaci�n de las estructurasque se utilizar�n en la generaci�n del fichero .xml
     */
    private boolean checkSizeAndPositionProperties(ObjectFactory oFactory, PropertiesType oProperties, Capsula oCapsula, int i) throws javax.xml.bind.JAXBException {
        boolean bHasModifiedProperty = false;
        //Propiedad: X
        if (oCapsula.getX(i) != oCapsula.getOriginalX()) {
            //Si la propiedad ha sido modificada la a�adimos
            bHasModifiedProperty = true;
            PropertyType oProperty = oFactory.createPropertyType(); //Elemento propiedad en .xml
            oProperty.setName("X");
            oProperty.setType("primitive");
            oProperty.setPropertyClass("java.lang.Integer");
            oProperty.setValue(String.valueOf(oCapsula.getX(i)));
            oProperties.getProperty().add(oProperty);
        }
        //Propiedad: Y
        if (oCapsula.getY(i) != oCapsula.getOriginalY()) {
            //Si la propiedad ha sido modificada la a�adimos
            bHasModifiedProperty = true;
            PropertyType oProperty = oFactory.createPropertyType(); //Elemento propiedad en .xml
            oProperty.setName("Y");
            oProperty.setType("primitive");
            oProperty.setPropertyClass("java.lang.Integer");
            oProperty.setValue(String.valueOf(oCapsula.getY(i)));
            oProperties.getProperty().add(oProperty);
        }
        //Propiedad: Z
        if (oCapsula.getZ(i) != oCapsula.getOriginalZ()) {
            //Si la propiedad ha sido modificada la a�adimos
            bHasModifiedProperty = true;
            PropertyType oProperty = oFactory.createPropertyType(); //Elemento propiedad en .xml
            oProperty.setName("Z");
            oProperty.setType("primitive");
            oProperty.setPropertyClass("java.lang.Integer");
            oProperty.setValue(String.valueOf(oCapsula.getZ(i)));
            oProperties.getProperty().add(oProperty);
        }
        //Propiedad: Width
        if (oCapsula.getWidth(i) != oCapsula.getOriginalWidth()) {
            //Si la propiedad ha sido modificada la a�adimos
            bHasModifiedProperty = true;
            PropertyType oProperty = oFactory.createPropertyType(); //Elemento propiedad en .xml
            oProperty.setName("Width");
            oProperty.setType("primitive");
            oProperty.setPropertyClass("java.lang.Integer");
            oProperty.setValue(String.valueOf(oCapsula.getWidth(i)));
            oProperties.getProperty().add(oProperty);
        }
        //Propiedad: Height
        if (oCapsula.getHeight(i) != oCapsula.getOriginalHeight()) {
            //Si la propiedad ha sido modificada la a�adimos
            bHasModifiedProperty = true;
            PropertyType oProperty = oFactory.createPropertyType(); //Elemento propiedad en .xml
            oProperty.setName("Height");
            oProperty.setType("primitive");
            oProperty.setPropertyClass("java.lang.Integer");
            oProperty.setValue(String.valueOf(oCapsula.getHeight(i)));
            oProperties.getProperty().add(oProperty);
        }
        //Propiedad: Visible
        if (oCapsula.isVisible(i) != oCapsula.isOriginalVisible()) {
            //Si la propiedad ha sido modificada la a�adimos
            bHasModifiedProperty = true;
            PropertyType oProperty = oFactory.createPropertyType(); //Elemento propiedad en .xml
            oProperty.setName("Visible");
            oProperty.setType("primitive");
            oProperty.setPropertyClass("java.lang.Boolean");
            oProperty.setValue(String.valueOf(oCapsula.isVisible(i)));
            oProperties.getProperty().add(oProperty);
        }
        return bHasModifiedProperty;
    }
     private String transformaValor(byte[] cadena, int longitud) {

        String sValue = new String(cadena, 0, longitud);


        String a[] = {"ñ", "á", "é", "í", "ó",
            "ú" ,"Ñ","Á", "É", "Í", "Ó", "Ú","¿","¡"};
        //ñ=209
        String b[] = {"&#241;", "&#225;", "&#233;", "&#237;", "&#243;", "&#250;",
        "&#209;","&#193;", "&#201;", "&#205;", "&#211;", "&#218;","&#191;","&#161;"};

        for (int i = 0; i < a.length; i++) {

            sValue = sValue.replaceAll((String) a[i], (String) b[i]);

        }
System.out.println(sValue);
        return sValue;

    }
      private String transformaValor2(char cadena) {
        String svalue = String.valueOf(cadena);
        char a[] = {'ñ', 'á', 'é', 'í', 'ó', 'ú', 'Ñ', 'Á', 'É', 'Í', 'Ó', 'Ú', '¿', '¡'};
        //ñ=209
        String b[] = {"&#241;", "&#225;", "&#233;", "&#237;", "&#243;", "&#250;",
            "&#209;", "&#193;", "&#201;", "&#205;", "&#211;", "&#218;", "&#191;", "&#161;"};

        for (int i = 0; i < a.length; i++) {
            if (a[i] == cadena) {
                svalue = b[i];
            }
        }
//System.out.print(svalue);
        return svalue;

    }
}
