/*
 *  CriterionManager.java
 * 
 *  RELOAD Editor - An IMS Metadata, Content Packaging and Learning Design tool 
 *  with ADAPTATION AT ACTIVITY LEVEL
 *
 *  Copyright (c) 2006 Miguel Fernandez Lores
 *
 *  Technical Contact:
 *  Miguel Fern�ndez Lores
 *  e-mail: tucias@hotmail.com
 *
 */

package uk.ac.reload.editor.scormadaptive.rules.jdom.criterion;

import uk.ac.reload.editor.scormadaptive.rules.tools.*;
import java.util.*;
import javax.swing.JOptionPane;
import org.jdom.input.*;
import org.jdom.*;

/**
 * This class is an intermediate class to read the adaptation 
 * criteria and the values. It is used by the Assistant for 
 * the generation of the rules
 *
 * @author  lagar
 * @version 0.2 
 */
public class CriterionManager {
    
    final public static int INTEGER=0;
    final public static int STATE=4;
    final public static int COMPLEX=5;
    final public static int LANGCODE=6;
    
    final private static String sSTATE = "state";
    final private static String sINTEGER = "Non-negative integer";
    final private static String sLANGCODE = "langcode";
    final private static String sCOMPLEX = "complex";
            
    /**
     * ArrayList con los parametros definidos en el vocabulario
     * Los elementos son objetos de la clase Criterion (@see Criterion)
     */
    private java.util.ArrayList<Criterion> oCriteria;
    
    private ArrayList<ComplexCriterion> oComplex;
    
    /**
     * Los nombre de los parametros (aparecen en el mismo orden que el array oCriteria)
     */
    private ArrayList<String> oCriteriaName;
    
    private String sVocabularyName;
    
    /**
     * Constructor of the class.
     * Lee el fichero de vocabulario de de t-Maestro (contiene los parametros, y sus posibles valores)
     * El fichero de vocabulario (adaptationCriteria.xml) es una fichero .xml que sigue el schema vocabulary.xsd
     */
    public CriterionManager() throws Exception {
        try {
            SAXBuilder builder=new SAXBuilder(false); 
            //usar el parser Xerces y no queremos 
            //que valide el documento
            Document oDoc = builder.build("src/uk/ac/reload/editor/scormadaptive/rules/jdom/criterion/adaptationCriteria.xml");
            Element oRaiz = oDoc.getRootElement();
            
            //java.io.InputStream oVocabularyFile = this.getClass().getResource("uk/ac/reload/editor/adaptation/adaptationCriteria.xml").openStream();
           
            //Creamos la estructura con los par�metros del vocabulario
            oCriteria = new ArrayList<Criterion>();
            oCriteriaName = new ArrayList<String>();
            
            //Leemos el nombre del vocabulario que luego
            //usaremos en los <adaptationParameter><source>
            sVocabularyName = oRaiz.getAttributeValue("vocabulary");
            
            //Creamos una lista con todos los <criterion>
            List oListCriteria = oRaiz.getChildren("criterion");
            Iterator oIt = oListCriteria.iterator();
            //Recorremos la lista
            
            while (oIt.hasNext()) {
                Element oEl = (Element)oIt.next();
                if ((oEl.getAttribute("criterionName") == null) || (oEl.getAttribute("criterionType") == null)) {
                    throw new Exception("adaptationCriteria.xml is incorrect. Do not exist criterionName or criterionType Attribute");
                }
                String sName = oEl.getAttributeValue("criterionName");
                String sType = oEl.getAttributeValue("criterionType");
                Integer iType = changeType(sType);
                
                Criterion oAuxCriterion = null;
                switch (iType){
                    case INTEGER:
                        oAuxCriterion = integerCase(sName, iType, oEl);
                        oCriteria.add(oAuxCriterion);
                        oCriteriaName.add(sName);
                        break;
                    case LANGCODE:
                        oAuxCriterion = new Criterion(sName,iType,java.util.Locale.getISOLanguages());
                        oCriteria.add(oAuxCriterion);
                        oCriteriaName.add(sName);
                        break;
                    case STATE:
                        oAuxCriterion = stateCase(sName,iType,oEl);
                        oCriteria.add(oAuxCriterion);
                        oCriteriaName.add(sName);   
                        break;
                    case COMPLEX:
                        String sPrefix = sName;
                        List sListCriterion = oEl.getChildren("criterion");
                        Iterator oItCri = sListCriterion.iterator();
                        if (oItCri == null) {
                            throw new Exception("adaptationCriteria.xml is incorrect. Type Collection");
                        }
                        Element oElSup = (Element)oItCri.next();
                        
                        if ((oElSup.getAttribute("criterionName") == null) || (oElSup.getAttribute("criterionType") == null)) {
                            throw new Exception("adaptationCriteria.xml is incorrect. Do not exist criterionName or criterionType Attribute");
                        }
                       
                         //Lo anadimos de todos modos a la lista de criterios
                        //pero no le damos valores
                        oAuxCriterion = new Criterion(sName,iType);
                        oCriteria.add(oAuxCriterion);
                        oCriteriaName.add(sName);
                        sName = oElSup.getAttributeValue("criterionName");
                        sType = oElSup.getAttributeValue("criterionType");
                        iType = changeType(sType);
                
                        ComplexCriterion oCC = null;
                        if (sType.equals(sLANGCODE)) {
                            iType = LANGCODE;
                            oCC = new ComplexCriterion(sPrefix,sName,iType,java.util.Locale.getISOLanguages());
                        }
                        else if (sType.equals(sSTATE)) {
                            oAuxCriterion = stateCase(sName,iType,oElSup);
                            oCC = new ComplexCriterion(sPrefix,oAuxCriterion);
                        }
                        else throw new Exception("adaptationCriteria.xml is incorrect. Invalid type");
                        if (oComplex ==  null) oComplex = new ArrayList<ComplexCriterion>();
                        oComplex.add(oCC);
                        
                        oAuxCriterion = null;
                        while (oItCri.hasNext()){
                            Element oElAux = (Element)oItCri.next();
                            if ((oElAux.getAttribute("criterionName") == null) || (oElAux.getAttribute("criterionType") == null)) {
                                throw new Exception("adaptationCriteria.xml is incorrect. Do not exist criterionName or criterionType Attribute");
                            }
                            sName = oElAux.getAttributeValue("criterionName");
                            sType = oElAux.getAttributeValue("criterionType");
                            iType = changeType(sType);
                            
                            switch (iType){
                                case INTEGER:
                                    oAuxCriterion = integerCase(sName, iType, oElAux);
                                    if (oCC != null) oCC.addCriterion(oAuxCriterion);
                                    break;
                                case LANGCODE:
                                    oAuxCriterion = new Criterion(sName,iType,java.util.Locale.getISOLanguages());
                                    if (oCC != null) oCC.addCriterion(oAuxCriterion);
                                    break;
                                case STATE:
                                    oAuxCriterion = stateCase(sName,iType,oElAux);
                                    if (oCC != null) oCC.addCriterion(oAuxCriterion);
                                    break;
                                case COMPLEX:
                                    //En caso de que un tipo coplejo pudiese tener
                                    //varios niveles...
                                    break;
                            }
                        }
                        break;
                    default:
                        throw new Exception("adaptationCriteria.xml is incorrect. Type incorrect");
                }
            }
        } catch(Exception oE) {
            oE.printStackTrace();
            java.awt.Toolkit.getDefaultToolkit().beep();
            System.exit(-1);
        }
    }
    
    /**
     * @return The vocabulary used name
     */
    public String getVocabularyName() {
        return sVocabularyName;
    }
    
    /**
     * Counts the integer value of a "numeric string"
     *
     * @param sValue The String
     * @return The Non-negative integer value or -1 if the String is not numeric only
     */
    public Integer StringToInteger(String sValue) {
        int iValue = 0;
        int iCard = 1;
        
        char[] oValue = sValue.toCharArray();
        for (int i = sValue.length()-1; i>=0; i--){
            int aux;
            switch (oValue[i]){
                case '0':
                    aux=0;
                    break;
                case '1':
                    aux=1;
                    break;
                case '2':
                    aux=2;
                    break;
                case '3':
                    aux=3;
                    break;
                case '4':
                    aux=4;
                    break;
                case '5':
                    aux=5;
                    break;
                case '6':
                    aux=6;
                    break;
                case '7':
                    aux=7;
                    break;
                case '8':
                    aux=8;
                    break;
                case '9':
                    aux=9;
                    break;
                default:
                    return -1;
            }
            iValue += aux * iCard;
            iCard = iCard * 10;
        }
        return iValue;
    }
    
    /**
     * @return An array with the criterion names
     */
    public Object[] getCriterionNames() {
        return oCriteriaName.toArray();
    }
    
    /**
     * Returns the iIndex criterion object
     *
     * @param iIndex The index
     * @return Class Criterion object (@see Criterion)
     */
    public Criterion getCriterion(int iIndex) {
        return oCriteria.get(iIndex);
    }
    
    /**
     * Returns the criterion object named sName
     *
     * @param sName The name
     * @return Class Criterion object (@see Criterion)
     */
    public Criterion getCriterion(String sName) {
        int iIndex = 0;
        
        for (int i=0; i<oCriteria.size(); i++){
            if (oCriteria.get(i).getName().equals(sName)) {
                iIndex = i;
                i = oCriteria.size();
            }
        }
        return oCriteria.get(iIndex);
    }
    
    public ComplexCriterion getComplex(String sName) {
        int iIndex = 0;
        
        if (oComplex == null) return null;
        else {
            for (int i=0; i<oComplex.size(); i++){
                if (oComplex.get(i).getPrefix().equals(sName)) {
                    iIndex = i;
                    i = oComplex.size();
                }
            }
        }
        return oComplex.get(iIndex);
    }

    private Criterion integerCase(String sName, int iType, Element oEl) throws Exception {
        Criterion oAuxCriterion;
        
        //Si es de tipo integer leemos los siguientes parametros
        if ((oEl.getAttribute("minValue") == null) || 
           (oEl.getAttribute("maxValue") == null) || 
           (StringToInteger(oEl.getAttributeValue("minValue"))==-1) || 
           (StringToInteger(oEl.getAttributeValue("maxValue"))==-1)) {
            throw new Exception("adaptationCriteria.xml is incorrect. Type integer");
        }

        oAuxCriterion = new Criterion(sName,iType,StringToInteger(oEl.getAttributeValue("minValue")),StringToInteger(oEl.getAttributeValue("maxValue")));
        return oAuxCriterion;
    }

    private Criterion stateCase(String sName, Integer iType, Element oEl) throws Exception {
        if ((oEl.getChild("values") == null) || (oEl.getChild("values").getChildren() == null)) {
            throw new Exception("adaptationCriteria.xml is incorrect. Type State");
        }
        List oAuxList = oEl.getChild("values").getChildren();
        Iterator oItAux = oAuxList.iterator();

        int i=0;
        String[] sAuxList = null;

        while (oItAux.hasNext()) {
            Element oAuxEl = (Element)oItAux.next();
            if (sAuxList == null) sAuxList = new String[oAuxList.size()];
            sAuxList[i] = oAuxEl.getText();
            i++;
        }

        Criterion oAuxCriterion = new Criterion(sName,iType,sAuxList);
        
        return oAuxCriterion;
    }

    private Integer changeType(String sType) throws Exception {
        int iType;
        
        if (sType.equals(sINTEGER)) iType = INTEGER;
        else if (sType.equals(sLANGCODE)) iType = LANGCODE;
        else if (sType.equals(sCOMPLEX)) iType = COMPLEX;
        else if (sType.equals(sSTATE)) iType = STATE;
        else throw new Exception("adaptationCriteria.xml is incorrect. Invalid type"); 
        return iType;
    }
    
    
    
    
    
}
