package de.dhbwstuttgart.syntaxtree;
import java.io.IOException;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.function.Function;

import de.dhbwstuttgart.typeinference.Menge;

import java.util.stream.Stream;

import de.dhbwstuttgart.logger.Logger;
import de.dhbwstuttgart.logger.Section;
import de.dhbwstuttgart.core.MyCompiler;
import de.dhbwstuttgart.parser.JavaClassName;
import de.dhbwstuttgart.syntaxtree.factory.UnifyTypeFactory;
import de.dhbwstuttgart.syntaxtree.modifier.Modifiers;
import de.dhbwstuttgart.syntaxtree.modifier.Public;
import de.dhbwstuttgart.syntaxtree.type.FunN;
import de.dhbwstuttgart.syntaxtree.type.GenericTypeVar;
import de.dhbwstuttgart.syntaxtree.type.RefType;
import de.dhbwstuttgart.syntaxtree.type.Type;
import de.dhbwstuttgart.syntaxtree.type.TypePlaceholder;
import de.dhbwstuttgart.syntaxtree.type.Void;
import de.dhbwstuttgart.typeinference.ByteCodeResult;
import de.dhbwstuttgart.typeinference.ConstraintsSet;
import de.dhbwstuttgart.typeinference.FunNInterface;
import de.dhbwstuttgart.typeinference.Pair;
import de.dhbwstuttgart.typeinference.ResultSet;
import de.dhbwstuttgart.typeinference.TypeinferenceResultSet;
import de.dhbwstuttgart.typeinference.TypeinferenceResults;
import de.dhbwstuttgart.typeinference.UnifyConstraintsSet;
import de.dhbwstuttgart.typeinference.assumptions.ClassAssumption;
import de.dhbwstuttgart.typeinference.assumptions.TypeAssumptions;
import de.dhbwstuttgart.typeinference.exceptions.DebugException;
import de.dhbwstuttgart.typeinference.exceptions.TypeinferenceException;
import de.dhbwstuttgart.typeinference.unify.TypeUnify;
import de.dhbwstuttgart.typeinference.unify.Unifikationsalgorithmus;
import de.dhbwstuttgart.typeinference.unify.model.FiniteClosure;
import de.dhbwstuttgart.typeinference.unify.model.PlaceholderType;
import de.dhbwstuttgart.typeinference.unify.model.UnifyPair;



public class SourceFile extends SyntaxTreeNode
{
    
    protected static Logger codegenlog = Logger.getLogger("codegen");
    protected static Logger inferencelog = Logger.getLogger("inference");
    
    private String pkgName;
    
    public List<Class> KlassenVektor = new Menge<Class>();
    private List<JavaClassName> imports;

    /**
     * Die SourceFile repräsntiert eine zu einem Syntaxbaum eingelesene Java-Datei.
     * SourceFile stellt dabei den Wurzelknoten des Syntaxbaumes dar.
     */
    public SourceFile(List<Class> classDefinitions) {
		this.KlassenVektor = classDefinitions;
	}

    /**
     * PL 2014-10-25
     * schnitt1 checkt ob die Typeplaceholders aus in den Elemeneten aus vars enthalten sind
     * Rückgabe ist die Menge der Indizies von vars der Schnittmengen mit var nicht leer sind.
     * @param var
     * @param vars
     * @param indexe
     * @return
     */
    static Menge<Integer> schnitt1 (Menge<PlaceholderType> var, Menge<Menge<PlaceholderType>> vars, Menge<Integer> indexe) {
        int j = -1;
        for (Menge<PlaceholderType> varelems : vars) {
                j++;
                if (varelems != null) {
                        if (var.stream().map(x -> varelems.contains(x)).reduce(false, (a,b) -> (a || b))
                                        && (!indexe.contains(j)))
                        {
                                Menge<PlaceholderType> rekvarelements = vars.elementAt(j);
                                vars.setElementAt(null, j);//Element erledigt muss nicht nochmals bearbeitet werden.
                                indexe.addElement(j);
                                indexe = schnitt1(rekvarelements, vars, indexe);
                        }
                }
        }
        return indexe;
    }

    /**
     * Bildet Schnittmengen der Mengen von Typeplaceholders
     * Rueckgabe ist die Menge der Menge von Indizies die Schnittmengen sind.
     * @param vars
     * @return
     */
    public static Menge<Menge<Integer>> schnitt (Menge<Menge<PlaceholderType>> vars) {
        Menge<Menge<Integer>> ret = new Menge<>();
        int i = -1;
        for (Menge<PlaceholderType> var : vars) {
                i++;
                if (var != null) {//Element wurde noch bearbeitet
                        Menge<Integer> indexe = new Menge<>();
                        indexe.add(i);
                        ret.add(schnitt1(var, vars, indexe));
                }
        }
        return ret;
    }

    public static Set<Set<UnifyPair>> cartesianProduct(List<UnifyPair> constraints, FiniteClosure finiteClosure){
    	//IDEE: Man bildet Zusammenhangskomponenten von Paaren, die gemeinsame Variablen haben
        //      und unifizert nur die Zusammenhangskomponenten in Schritten 1 - 5
        //Schritt 1: Alle Variablen in den Paaren von Elementen einsammeln
        Menge<Menge<PlaceholderType>> constraintsclonevars = constraints.stream().map(p -> {Menge<PlaceholderType> TPHs = new Menge<>(); 
          TPHs.addAll(p.getInvolvedPlaceholderTypes());
          TPHs.addAll(p.getInvolvedPlaceholderTypes());
          return TPHs;}
          ).collect(Menge::new, Menge::add, Menge::addAll);    	

        
        //Schritt 2: Schnittmengen jedes Elements mit jedem Elememt von vars bilden und dann index zusammenfassen
        //in indexset sind dann die Mengen von Indizes enthalten, die gemeisam unifiziert wreden müssen
      	Menge<Menge<Integer>> indexeset = new Menge<>();
      	if (constraintsclonevars != null && constraintsclonevars.size()>0) { 
      		indexeset = SourceFile.schnitt(constraintsclonevars);
      	}
      	
      	//Schritt 3: Umwandlung der Indizes in die zugehoerigen Elemente
      	//           In streamconstraintsclone sind die Mengen von Paar enthalten die unifiziert werden muessen
      	Stream<Menge<UnifyPair>> streamconstraintsclone = indexeset.stream().<Menge<UnifyPair>>map(x -> x.stream()
      			                                                           .<UnifyPair>map(i -> constraints.get(i))
      			                                                               .<Menge<UnifyPair>>collect(Menge::new, Menge::add, Menge::addAll));
     	//Menge<Menge<Pair>> vecconstraintsclone = streamconstraintsclone.collect(Menge::new, Menge::add, Menge::addAll);
          //System.out.println();
      	//Schritt 4: Unifikation  
      	Menge<Set<Set<UnifyPair>>> vecunifyResult =
      			//streamconstraintsclone.map(x -> Unify.unify(x, finiteClosure)).collect(Menge::new, Menge::add, Menge::addAll);
      			//DEBUG-Variante
      			streamconstraintsclone.map(x -> 
      			  	{ 	Set<Set<UnifyPair>> z = new TypeUnify().unify(x, finiteClosure);
      					return z;
      			  	}
      					).collect(Menge::new, Menge::add, Menge::addAll);
      			  	
      	
      	//card gibt die Cardinalitaet der unifizierten Mengen an
      	Menge<Integer> card = vecunifyResult.stream().map(x -> x.size()).collect(Menge::new, Menge::add, Menge::addAll);
      			;//.reduce(1,(a,b) -> { if ((a > 0) && (b > 0)) return (a * b); else return 1; });
      			
          //Schritt 5: Bildung des cartesischen Produkts
          //sollte wieder entfernt werden: Weiterarbeit mit: 
      	//[[x_1 -> t_1, x_2 -> t2], [x_1 -> t'_1, x_2 -> t'_2]] x ... x [[x_n -> t_1n], [x_n -> t2n], [x_n -> t3n]] 
      	Set<Set<UnifyPair>> cardprodret_start = new Menge<>();
      	cardprodret_start.add(new Menge<UnifyPair>());
      	
      	//cart. Produkt mit Linkverschiebung
      	Set<Set<UnifyPair>> unifyResult = vecunifyResult.stream().reduce(cardprodret_start, (x, y) -> {
      		Set<Set<UnifyPair>> cardprodret= new Menge<>();
      		if (y.size() > 0) {
      			//System.out.println(y);
      			//Menge<Menge<Pair>> cardprodretold = x;
					//cardprodret = new Menge<>();
					for(Set<UnifyPair> xElement : x) {
						for (Set<UnifyPair> yElement : y){
							Set<UnifyPair> help = new Menge<>(); 
							help.addAll(yElement);
							help.addAll(xElement);
							cardprodret.add(help);
						}
					}
      		}
      		else 
      			return new Menge<>(); //kein unifiziertes Ergebnis, damit wird das Geseamtergebnis []
      		return cardprodret;
      	});
      	return unifyResult;
    }
    
    /////////////////////////////////////////////////////////////////////////
    // TypeReconstructionAlgorithmus
    /////////////////////////////////////////////////////////////////////////
    /**
     * Tyrekonstruktionsalgorithmus: ruft f�r jede Klasse den Algorithmus TRProg auf.
     * Dessen Ergebnismenge A, die Menge aller Typannahmen, f�r eine Klasse dient als
     * Eingabe f�r TRProg der n�chsten Klasse. Am Ende enth�lt A alle m�glichen
     * Typkombinationen f�r alle Klassen zusammen.
     * <br>Author: J�rg B�uerle
     * @return Liste aller m�glichen Typkombinationen
     * @throws CTypeReconstructionException Wenn was schief l�uft
     */
    public Menge<TypeinferenceResultSet> typeReconstruction(TypeAssumptions globalAssumptions)
{
    	Menge<TypeinferenceResultSet> ret = new Menge<TypeinferenceResultSet>();
    	
    	//Logger initialisieren:
    	Logger typinferenzLog = Logger.getLogger("Typeinference");
    	
    	//Alle Assumptions für diese SourceFile sammeln:
    	for(Class klasse : this.KlassenVektor){
    		globalAssumptions.add(klasse.getPublicFieldAssumptions());
    	}
    	
    	//Assumptions der importierten Klassen sammeln:
    	TypeAssumptions importAssumptions = this.makeBasicAssumptionsFromJRE(imports, true);
    	globalAssumptions.add(importAssumptions);
    	typinferenzLog.debug("Von JRE erstellte Assumptions: "+importAssumptions, Section.TYPEINFERENCE);
    	
    	ConstraintsSet oderConstraints = new ConstraintsSet();
    	//Alle Constraints der in dieser SourceFile enthaltenen Klassen sammeln:
        for(Class klasse : KlassenVektor){
        	oderConstraints.add(klasse.typeReconstruction(globalAssumptions));
        }
        
        /*////////////////
         * Paare in MPairs umwandeln
         * (Wird zunächst mal weggelassen. Constraints werden erst beim Unifizieren umgewandelt
         */////////////////
        //UnifyTypeFactory.convert(oderConstraints);
        

    	//FiniteClosure generieren:
    	FiniteClosure finiteClosure = UnifyTypeFactory.generateFC(globalAssumptions);
    	
    	typinferenzLog.debug("FiniteClosure: \n"+finiteClosure, Section.TYPEINFERENCE);
        
        ////////////////
        //Typen in UnifyTypen umwandeln:
        ////////////////
        UnifyConstraintsSet unifyConstraints = UnifyTypeFactory.convert(oderConstraints);
        
        //Unmögliche ConstraintsSets aussortieren durch Unifizierung
        Unifikationsalgorithmus unifier = (pairs)->new TypeUnify().unify(pairs, finiteClosure);
        	
        unifyConstraints.filterWrongConstraints(unifier);
        	
        //unifyConstraints.unifyUndConstraints(unifier); //rausgeworfen für Tests (08.12.2015)

    	typinferenzLog.debug("Übriggebliebene Konstraints:\n"+oderConstraints+"\n", Section.TYPEINFERENCE);
    	
    	typinferenzLog.debug("Übriggebliebene Konvertierte Konstraints:\n"+unifyConstraints+"\n", Section.TYPEINFERENCE);
    	
        ////////////////
	    //Karthesisches Produkt bilden:
	    ////////////////
        Set<Set<UnifyPair>> xConstraints = unifyConstraints.cartesianProduct();
        	
    	
        //Sets zu Listen umwandeln:
        //Set<List<UnifyPair>> allUnifiedConstraints = xConstraints.stream().map((set)-> new ArrayList<>(set)).collect(Menge::new, Menge::add, Menge::addAll);;

        typinferenzLog.debug("Finite Closure: "+finiteClosure, Section.TYPEINFERENCE);
        typinferenzLog.debug("Karthesisches Produkt der Constraints: "+xConstraints, Section.TYPEINFERENCE);
        
        //finiteClosure.generateFullyNamedTypes(globalAssumptions);
        
        //////////////////////////////
        // Unifizierung der Constraints:
        //////////////////////////////
    	boolean unifyFail = true;
        for(Set<UnifyPair> constraints : xConstraints){
        	//Alle durch das Karthesische Produkt entstandenen Möglichkeiten durchgehen:
            
        	typinferenzLog.debug("\nUnifiziere Constraints:\n"+constraints, Section.TYPEINFERENCE);
        	typinferenzLog.debug("\nFC:\n"+finiteClosure, Section.TYPEINFERENCE);
        	long start = System.currentTimeMillis();
			Set<Set<UnifyPair>> unifyResult = new TypeUnify().unify(constraints, finiteClosure);
			long time = System.currentTimeMillis()-start;
			typinferenzLog.debug("\nErgebnis der Unifizierung:\n"+unifyResult, Section.TYPEINFERENCE);
			typinferenzLog.debug("\nAnzahl Lösungen:\n"+unifyResult.size(), Section.TYPEINFERENCE);
			//typinferenzLog.debug("\nZeit für Unifizierung: "+time + "ms", Section.TYPEINFERENCE);
			
			
            Menge<Menge<Pair>> convertedResult = unifyResult.parallelStream().<Menge<Pair>>map((Set<UnifyPair> resultSet)->{
            	Menge<Pair> innerConvert = resultSet.stream().map((UnifyPair mp)->UnifyTypeFactory.convert(mp))
            			.collect(Menge<Pair>::new, Menge::add, Menge::addAll);
            	return innerConvert;
            }).collect(Menge::new, Menge::add, Menge::addAll);
            
            Menge<Pair> convertedConstraints = constraints.stream().map(
            		(UnifyPair mp)->{return UnifyTypeFactory.convert(mp);}
            		).collect(Menge<Pair>::new, Menge::add, Menge::addAll);
            
        	//Dann den Ergebnissen anfügen
        	typinferenzLog.debug("\nErgebnis der Unifizierung (Konvertiert):\n"+convertedResult, Section.TYPEINFERENCE);
            //result.addAll(convertedResult);
        
	        typinferenzLog.debug("\nJavaFiles:\n", Section.TYPEINFERENCE);
	        
	        //typinferenzLog.debug(this.printJavaCode(new ResultSet(new Menge<Pair>())));
	        
	
	        //Für jede Klasse in diesem SourceFile gilt das selbe ResultSet:
	        for(Class klasse : this.KlassenVektor){
	        	//Der Unifikationsalgorithmus kann wiederum auch mehrere Lösungen errechnen, diese werden im folgenden durchlaufen:
		        for(Menge<Pair> resultSet : convertedResult){
		        	unifyFail = false; //Ein Unifiziertes Ergebnis ist entstanden (es kann auch leer sein, das bedeutet nur, dass die Constraints mindestens in einem Fall Sinn ergaben)
		        	//Add Result set as a new ReconstructionResult to ret:
		        	TypeinferenceResultSet reconstructionResult = new TypeinferenceResultSet(klasse, convertedConstraints, new ResultSet(resultSet));
		        	ret.add(reconstructionResult);
		        	
		        	//ResultSet res = new ResultSet(resultSet);
		        	typinferenzLog.debug("JavaFile für ResultSet "+reconstructionResult+"\n", Section.TYPEINFERENCE);
		        	typinferenzLog.debug(klasse.printJavaCode(reconstructionResult), Section.TYPEINFERENCE);
		        	
		        }
	        }
        }
        if(unifyFail){
        	if(!this.KlassenVektor.isEmpty())throw new TypeinferenceException("Fehler in Typinferierung", this.KlassenVektor.firstElement());   	
        }
        return ret;
    }
    
    /**
     * Erstellt die Assumptions der standardmäßig importierten Packages (java.lang.) sowie der von imports übergebenen Klassen zusammen.
     * @param imports
     * @param withSuptypes - Gibt an, ob auch die subklassen der Packages den Assumptions angefügt werden sollen.
     * @return
     * TODO: Diese Methode neu erstellen
     */
    public TypeAssumptions makeBasicAssumptionsFromJRE(List<JavaClassName> imports, boolean withSubtypes)
    // ino.end
    // ino.method.makeBasicAssumptionsFromJRE.21409.body 
    {
		return null;
    }
    // ino.end

    private Class getSuperClassOfJREClass(java.lang.Class<?> x, TypeAssumptions ass) {
    	Class ret;
    	java.lang.Class s = x.getSuperclass();
    	if(s == null){
    		return new Class("java.lang.Object",new Modifiers(), 0);
    	}

    	Menge<String> supertypeGenPara = new Menge<>();//Die Generischen Parameter für die Superklasse berechnen:
    	java.lang.reflect.TypeVariable[] superclassTVS=s.getTypeParameters();
    	for(int tvi=0;tvi<superclassTVS.length;tvi++){
            supertypeGenPara.addElement(superclassTVS[tvi].getName());
        }
        
    	Class ss = this.getSuperClassOfJREClass(s, ass);
    	ret = new Class(s.getName(),ss.getType(),new Modifiers(),supertypeGenPara);
    	
        
    	ass.addClassAssumption(new ClassAssumption(ss)); //Die beiden SuperKlassen den Assumptions anfügen...
    	ass.addClassAssumption(new ClassAssumption(ret));
    	
    	return ret;
	}

	// ino.method.isBaseType.21412.definition 
    private boolean isBaseType(String type)
    // ino.end
    // ino.method.isBaseType.21412.body 
    {
        return baseTypeTranslationTable.containsValue(type);
    }
    // ino.end
    
    /*Die contains Methode des Menges vergleicht bei Strings nicht korrekt, 
     * da zwei Strings mit dem gleichen Inhalt unterschiedliche Instanzen sind.
     * Deshalb diese Methode 07-01-20 luar*/
    private boolean containsString(Menge<UsedId> searchMenge, String searchString)
    {
    	boolean found = false;
    	for(UsedId id : searchMenge)
    	{
    		String s = id.getQualifiedName().toString();
    		found |= s.equals(searchString);
    	}
    	return found;
    }


    // ino.method.createTypeFromJavaGenericType.21415.definition 
    private Type createTypeFromJavaGenericType(java.lang.reflect.Type type, java.lang.Class<?> cl, Hashtable<String,GenericTypeVar>jreSpiderRegistry, Class parentClass)
    // ino.end
    // ino.method.createTypeFromJavaGenericType.21415.body 
    {
        /* auskommentiert, da die Klassen von Sun in der Open JDK 1.8 nicht unterstützt werden.
        if(type instanceof TypeVariableImpl){
			TypeVariableImpl tvi=((TypeVariableImpl)type);
            return(new GenericTypeVar(jreSpiderRegistry.get(tvi.getName()).getName().toString(),parentClass,-1));
        }else{
        */
    	GenericTypeVar gtv = jreSpiderRegistry.get(type.getTypeName());
    	if(gtv != null)return gtv;
    	//new GenericTypeVar(jreSpiderRegistry.get(type.getTypeName()).getName().toString(),parentClass,-1));
        //String jccNameForClass=baseTypeTranslationTable.get(cl.getSimpleName());
        String jccNameForClass=baseTypeTranslationTable.get(cl.getName());          
        if(cl.getSimpleName().equalsIgnoreCase("void")){
            return(new Void(parentClass,-1));
        }else if(jccNameForClass!=null){
            RefType rt=new RefType(jccNameForClass,parentClass,-1);
            rt.setPrimitiveFlag(true);
            return(rt);
        }else{              
            //return(new RefType(cl.getSimpleName()));              
            return(new RefType(cl.getName(),parentClass,-1));               
        }
        //}
    }
    
    // ino.method.getPackageName.21427.defdescription type=javadoc
    /**
     * Erzeugt f�r jede Klasse einen Menge, in den Referenzen auf die GenericTypeVars 
     * dieser Klasse gespeichert werden. Diese Mengeen werden unter den Klassennamen  
     * in der 
     * Ergebnisdatenstruktur abgelegt. Au�erdem werden alle Klassennamen gespeichert.
     * <br/>Author: J�rg B�uerle
     * @param res
     * /
     * /*private void addClassNamesAndGenericsToRR(CTypeReconstructionResult res){
     * Iterator<Class> it = this.getClassIterator();
     * while(it.hasNext()){
     * Class cl = it.next();
     * res.addClassName(cl.get_classname());
     * Menge<GenericTypeVar> genericsList = new Menge<GenericTypeVar>();
     * 
     * for(int i =0; i<cl.get_ParaList().size(); i++){
     * Type para = (Type)cl.get_ParaList().elementAt(i);
     * if(para instanceof GenericTypeVar){
     * genericsList.addElement((GenericTypeVar)para);
     * }
     * }
     * res.addGenericTypeVars(cl.get_classname(), genericsList);
     * }
     * }
     */
    // ino.end
    
    // ino.method.getPackageName.21427.definition 
    public UsedId getPackageName()
    // ino.end
    // ino.method.getPackageName.21427.body 
    {
        return pkgName;
    }
    // ino.end

    // ino.method.setPackageName.21430.definition 
    public void setPackageName(UsedId pkgName)
    // ino.end
    // ino.method.setPackageName.21430.body 
    {
        this.pkgName = pkgName;
        
        // Die Package-Namen fuer alle Klassen und Interfaces
        // im Source-File nachziehen
        for (int i=0; i<KlassenVektor.size(); i++) {
            KlassenVektor.elementAt(i).setPackageName(pkgName);
        }
        
    }
    // ino.end

    public ImportDeclarations getImports()
    {
        return(imports);
    }
    
    
    // ino.method.getClassIterator.21439.definition 
    public Iterator<Class> getClassIterator()
    // ino.end
    // ino.method.getClassIterator.21439.body 
    {
        return KlassenVektor.iterator();
    }
    // ino.end
 
    // ino.method.getInterfaceIterator.21442.definition 
    public Iterator<Interface> getInterfaceIterator()
    // ino.end
    // ino.method.getInterfaceIterator.21442.body 
    {
        return InterfaceVektor.iterator();
    }
    // ino.end


	@Override
	public void parserPostProcessing(SyntaxTreeNode parent) {
		if(parent!=null)throw new DebugException("Eine SourceFile hat kein Elternelement im Syntaxbaum");
		super.parserPostProcessing(this);
		//for(SyntaxTreeNode node : this.getChildren())node.parserPostProcessing(this);
	}

	@Override
	public SyntaxTreeNode getParent() {
		return null;
	}

	@Override
	public Menge<SyntaxTreeNode> getChildren() {
		Menge<SyntaxTreeNode> ret = new Menge<SyntaxTreeNode>();
		for(Class cl : this.KlassenVektor){
			ret.add(cl);
		}
		return ret;
	}

	/**
	 * SourceFile stellt eine geparste Java-Datei dar. Mit dieser Methode wird der Name der eingelesenen Datei gesetzt.
	 * @param filename - Der Name der eingelesenen JavaDatei
	 */
	@Deprecated
	public void setFileName(String filename) {
		//this.filename = filename;
	}

	@Override
	public int getOffset() {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public int getVariableLength() {
		// TODO Auto-generated method stub
		return 0;
	}

	/**
	 * Bytecode generieren für das resultSet
	 * @return
	 */
	public Menge<ByteCodeResult> generateBytecode(TypeinferenceResults results) {
		Menge<ByteCodeResult> ret = new Menge<>();
		for(Class cl : this.KlassenVektor){
			ret.add(cl.genByteCode(results));
		}
		//Alle FunN Klassen erzeugen:
		for(ClassAssumption funNAss : MyCompiler.makeFunNAssumptions().getClassAssumptions()){
			ret.add(funNAss.getAssumedClass().genByteCode(results));
		}
		/*
		//Add all FunN Interfaces
		for(Pair ucons : results.getUnifiedConstraints()){
			for(Type t : ucons.getTypes()){
				List<Class> xClasses = t.isClassFromJavaX();
				for(Class xClass : xClasses){
					ByteCodeResult bC = xClass.genByteCode(results);
					if(! ret.contains(bC))ret.add(bC);
				}
				}
		}
		*/
		return ret;
	}

}
// ino.end