package de.dhbwstuttgart.bytecode.signature;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;

import de.dhbwstuttgart.bytecode.genericsGeneratorTypes.GenericsGeneratorResult;
import de.dhbwstuttgart.syntaxtree.type.ExtendsWildcardType;
import de.dhbwstuttgart.syntaxtree.type.GenericRefType;
import de.dhbwstuttgart.syntaxtree.type.RefType;
import de.dhbwstuttgart.syntaxtree.type.RefTypeOrTPHOrWildcardOrGeneric;
import de.dhbwstuttgart.syntaxtree.type.SuperWildcardType;
import de.dhbwstuttgart.syntaxtree.type.TypePlaceholder;
import de.dhbwstuttgart.syntaxtree.type.TypeVisitor;

public class TypeToSignature implements TypeVisitor<String> {
	private List<GenericsGeneratorResult> constraints;

	public TypeToSignature() {
		this.constraints = new ArrayList<>();
	}

	public TypeToSignature(List<GenericsGeneratorResult> constraints) {
		this.constraints = constraints;
	}

	@Override
	public String visit(RefType refType) {
		if(refType.getName().toString().equals("void"))
			return "V";
//		return refType.toString().replace(".", "/");
		String params = "";
        if(refType.getParaList().size()>0){
            params += "<";
            Iterator<RefTypeOrTPHOrWildcardOrGeneric> it = refType.getParaList().iterator();
            while(it.hasNext()){
                RefTypeOrTPHOrWildcardOrGeneric param = it.next();
//                if(param instanceof TypePlaceholder) {
//                	params += "T" + ((TypePlaceholder) param).getName() + "$";
//                } else if(param instanceof ExtendsWildcardType) {
//                	params += "+" + ((ExtendsWildcardType) param).getInnerType().acceptTV(new TypeToSignature());
//                } else if(param instanceof SuperWildcardType) {
//                	params += "-" + ((SuperWildcardType) param).getInnerType().acceptTV(new TypeToSignature());
//                } else {
//                		params += "L"+param.toString().replace(".", "/");
//                }
                params += param.acceptTV(new TypeToSignature(constraints));

                if(param instanceof TypePlaceholder)
                	params += ";";
            }
            params += ">";
        }
//        		String t = refType.getName().toString().replace(".", "/");
//        		return t.equals("Fun1")?t+"$$"+params+";":t+params+";";
        return "L"+refType.getName().toString().replace(".", "/") + params+";";
	}

	@Override
	public String visit(SuperWildcardType superWildcardType) {
//		throw new NotImplementedException();
		String sig = "-" + superWildcardType.getInnerType().acceptTV(new TypeToSignature(constraints));
		if(superWildcardType.getInnerType() instanceof TypePlaceholder)
			sig += ";";
		return sig;
	}

	@Override
	public String visit(TypePlaceholder typePlaceholder) {
//		return typePlaceholder.toString().replace(".", "/");
		String name = typePlaceholder.getName();

		if(!constraints.isEmpty()){
			Optional<GenericsGeneratorResult> equalName = getEqualTPHFromClassConstraints(constraints, name);
			if(equalName.isPresent())
				name = equalName.get().getConstraint().getLeft();
		}

		return "T" + name + "$";
	}

	@Override
	public String visit(ExtendsWildcardType extendsWildcardType) {
//		throw new NotImplementedException();
		String sig = "+" + extendsWildcardType.getInnerType().acceptTV(new TypeToSignature(constraints));
		if(extendsWildcardType.getInnerType() instanceof TypePlaceholder)
			sig += ";";
		return sig;
	}

	@Override
	public String visit(GenericRefType genericRefType) {
		return genericRefType.getParsedName().replace(".", "/");
	}

	private Optional<GenericsGeneratorResult> getEqualTPHFromClassConstraints(List<GenericsGeneratorResult> listOfConstraints, String tph) {
		return listOfConstraints.stream()
				.filter(c -> c.getConstraint().getLeft().equals(tph) || c.getEqualsTPHs().contains(tph))
				.findFirst();
	}
}
