package de.dhbwstuttgart.sat.asp.writer;

import de.dhbwstuttgart.exceptions.NotImplementedException;
import de.dhbwstuttgart.parser.SyntaxTreeGenerator.FCGenerator;
import de.dhbwstuttgart.sat.asp.ASPStringConverter;
import de.dhbwstuttgart.sat.asp.model.ASPRule;
import de.dhbwstuttgart.syntaxtree.ClassOrInterface;
import de.dhbwstuttgart.syntaxtree.factory.NameGenerator;
import de.dhbwstuttgart.syntaxtree.type.*;
import de.dhbwstuttgart.typeinference.constraints.Constraint;
import de.dhbwstuttgart.typeinference.constraints.ConstraintSet;
import de.dhbwstuttgart.typeinference.constraints.Pair;
import de.dhbwstuttgart.typeinference.unify.model.PairOperator;

import java.util.*;

public class ASPFactory implements TypeVisitor<String>{

    public static String generateASP(ConstraintSet<Pair> constraints, Collection<ClassOrInterface> fcClasses) throws ClassNotFoundException{
        ASPFactory factory = new ASPFactory();
        factory.convertFC(fcClasses);
        List<Constraint<Pair>> constraints1 = constraints.cartesianProduct().iterator().next();
        for(Constraint<Pair> constraint : constraints1){
            for(Pair p : constraint){
                factory.convertPair(p);
            }
        }

        return factory.writer.getASPFile();
    }

    ASPWriter writer = new ASPWriter();
    boolean isFCType = false;

    private void convertFC(Collection<ClassOrInterface> classes) throws ClassNotFoundException {
        Set<Pair> fc = FCGenerator.toFC(classes);
        isFCType = true;
        for(Pair fcp : fc){
            convertPair(fcp);
        }
        isFCType = false;
    }

    private void convertPair(Pair p){
        String ls = p.TA1.acceptTV(this);
        String rs = p.TA2.acceptTV(this);
        ASPStatement pairStmt = null;
        if(p.GetOperator().equals(PairOperator.SMALLERDOT)){
            pairStmt = makeStatement(ASPRule.ASP_PAIR_SMALLER_DOT_NAME.toString(), ls, rs);
        }else if(p.GetOperator().equals(PairOperator.EQUALSDOT)){
            pairStmt = makeStatement(ASPRule.ASP_PAIR_EQUALS_NAME.toString(), ls, rs);
        }else if(p.GetOperator().equals(PairOperator.SMALLER)){
            pairStmt = makeStatement(ASPRule.ASP_PAIR_SMALLER_NAME.toString(), ls, rs);
        }else throw new NotImplementedException();
        writer.add(pairStmt);
    }

    private ASPStatement makeStatement(String rule, String... params){
        String stmt = rule + "(";
        for(String param : params){
            stmt += param + ",";
        }
        stmt = stmt.substring(0,stmt.length()-1);
        stmt += ")";
        return new ASPStatement(stmt);
    }

    private String convertParameterlist(List<String> pointers){
        String pointer = ASPStringConverter.toConstant(NameGenerator.makeNewName());
        Iterator<String> it = pointers.iterator();
        String p = pointer;
        if(!it.hasNext()){
            return ASPRule.ASP_PARAMLIST_END_POINTER.toString();
        }
        while (it.hasNext()){
            ASPStatement stmt;
            String type = it.next();
            String nextP = ASPStringConverter.toConstant(NameGenerator.makeNewName());
            if(it.hasNext()){
                stmt = makeStatement(ASPRule.ASP_PARAMLIST_NAME.toString(), p, type, nextP);
            }else{
                stmt = makeStatement(ASPRule.ASP_PARAMLIST_NAME.toString(), p, type,
                        ASPRule.ASP_PARAMLIST_END_POINTER.toString());
            }
            p = nextP;
            writer.add(stmt);
        }
        return pointer;
    }

    @Override
    public String visit(RefType refType) {
        String pointer = ASPStringConverter.toConstant(NameGenerator.makeNewName());
        List<String> params = new ArrayList<>();
        for(RefTypeOrTPHOrWildcardOrGeneric param : refType.getParaList()){
            params.add(param.acceptTV(this));
        }
        String typeName = ASPStringConverter.toConstant(refType.getName());
        String ruleName = isFCType?ASPRule.ASP_FCTYPE.toString():ASPRule.ASP_TYPE.toString();
        ASPStatement stmt = makeStatement(ruleName, pointer, typeName, convertParameterlist(params));
        writer.add(stmt);
        return pointer;
    }

    @Override
    public String visit(SuperWildcardType superWildcardType) {
        throw new NotImplementedException();
    }

    @Override
    public String visit(TypePlaceholder typePlaceholder) {
        String name = ASPStringConverter.toConstant(typePlaceholder.getName());
        ASPStatement stmt = makeStatement(ASPRule.ASP_TYPE_VAR.toString(), name);
        writer.add(stmt);
        return name;
    }

    @Override
    public String visit(ExtendsWildcardType extendsWildcardType) {
        throw new NotImplementedException();
    }

    @Override
    public String visit(GenericRefType genericRefType) {
        throw new NotImplementedException();
    }
}
