package de.dhbwstuttgart.syntaxtree;

import java.util.Vector;

import de.dhbwstuttgart.bytecode.ClassFile;
import de.dhbwstuttgart.myexception.JVMCodeException;
import de.dhbwstuttgart.syntaxtree.misc.DeclId;
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.typeinference.ConstraintsSet;
import de.dhbwstuttgart.typeinference.GenericTypeInsertable;
import de.dhbwstuttgart.typeinference.JavaCodeResult;
import de.dhbwstuttgart.typeinference.ResultSet;
import de.dhbwstuttgart.typeinference.TypeInsertable;
import de.dhbwstuttgart.typeinference.Typeable;
import de.dhbwstuttgart.typeinference.assumptions.TypeAssumptions;
import de.dhbwstuttgart.typeinference.typedeployment.TypeInsertPoint;

public abstract class Field extends SyntaxTreeNode implements TypeInsertable, Typeable, Generic, GenericTypeInsertable{
	
    protected Vector<DeclId> declid = new Vector<DeclId>(); // Vector, da 'int a, b, c, ...' auch eingeparst werden muss
	
    private Type typ;

	private int offset;

	private GenericDeclarationList genericParameters;
    
	public Field(int offset){
		this.offset = offset;
	}
	
	@Override
	public void setOffset(int offset){
		this.offset = offset;
	}
	@Override
	public int getOffset(){
		return this.offset;
	}
	
	@Override
	public void setType(Type typ) {
		this.typ = typ;
	}
	@Override
	public Type getType() {
		return typ;
	}
    
    public abstract void codegen(ClassFile classfile, Vector paralist)
    throws JVMCodeException;
    
	@Override
	public Vector<GenericTypeVar> getGenericParameter() {
		Vector<GenericTypeVar> ret = new Vector<>();
		if(this.genericParameters == null)return ret;
		ret.addAll(this.genericParameters.getVector());
		return ret;
	}
    
    public void set_DeclId(DeclId did)
    {
        this.declid.addElement(did);
    }
    
    
    
    public Vector<DeclId> get_Name()
    {
        return declid;
    }
    
    public Vector<DeclId> getDeclIdVector()
    {
        // otth: ganzer Vektor zur�ckgeben, um ihn zu kopieren (vgl. MyCompiler - Konstruktor in Methode umwandeln)
        return declid;
    }

    public void setDeclIdVector( Vector<DeclId> vDeclId )
    {
        // otth: kompletter Vektor setzen, um ihn zu kopieren (vgl. MyCompiler - Konstruktor in Methode umwandeln)
        declid = vDeclId;
    }




	public abstract JavaCodeResult printJavaCode(ResultSet resultSet);

	/**
	 * Diese Methode generiert die Assumptions f�r dieses Feld der Klasse classmember
	 * @param classmember
	 * @return
	 */
	public abstract TypeAssumptions createTypeAssumptions(Class classmember);
	
	public abstract ConstraintsSet TYPE(TypeAssumptions publicAssumptions);
	
	public String getIdentifier() {
		return this.get_Name().firstElement().get_Name();
	}
	
	@Override
	public String getDescription(){
		return this.getIdentifier();
	}
	
	@Override
	public TypeInsertPoint createTypeInsertPoint(TypePlaceholder tph,
			ResultSet resultSet) {
		return new TypeInsertPoint(this, this, resultSet.getTypeEqualTo(tph), resultSet);
	}
	
	/**
	 * Wird im Zuge des ParserPostProcessing aufgerufen.
	 * Der Parser kann den Unterschied zwischen einem RefType und einer GTV nicht erkennen.
	 * Diese Methode ersetzt die RefTypes gegebenenfalls durch eine GTV.
	 * @param paralist
	 */
	public void wandleRefTypeAttributes2GenericAttributes(Vector<Type> paralist){
		// Zuerst Returntype untersuchen
        Type type=getType();
        Type pendantReturnType = null;
        if(type instanceof RefType)pendantReturnType = ((RefType)type).findGenericType(paralist, new Vector<GenericTypeVar>());//GenericTypeVar pendantReturnType=ClassHelper.findGenericType(type, paralist,new Vector<GenericTypeVar>());
        if(pendantReturnType!=null){    //Wenn generisch, dann modifizieren
            setType(pendantReturnType);
        } 
	}

	public boolean isPublic() {
		//TODO: momentan ist jedes Feld public!
		return true;
	}

	@Override
	public String getGenericVarDeclarationString(String genericVarDeclaration)	{
		if(this.genericParameters != null){
			return ", "+genericVarDeclaration;
		}else{
			return "<"+genericVarDeclaration+">";
		}
	}
	
	@Override
	public int getGenericVarDeclarationOffset(){
		// Falls Generische Parameterliste vorhanden, hier Wert der Liste zur�ckgegebn
		if(this.genericParameters != null){
			return this.genericParameters.getEndOffset();
		}else{
			return this.offset;
		}
	}

	@Override
	public Vector<SyntaxTreeNode> getChildren() {
		Vector<SyntaxTreeNode> ret = new Vector<>();
		if(this.getType()!=null)ret.add(this.getType());
		ret.addAll(this.getGenericParameter());
		return ret;
	}

	@Override
	public void setGenericParameter(GenericDeclarationList params) {
		this.genericParameters = params;
	}
}