package asp;

import de.dhbwstuttgart.parser.NullToken;
import de.dhbwstuttgart.parser.scope.JavaClassName;
import de.dhbwstuttgart.sat.asp.Clingo;
import de.dhbwstuttgart.sat.asp.parser.ASPParser;
import de.dhbwstuttgart.sat.asp.writer.ASPFactory;
import de.dhbwstuttgart.syntaxtree.ClassOrInterface;
import de.dhbwstuttgart.syntaxtree.factory.ASTFactory;
import de.dhbwstuttgart.syntaxtree.type.*;
import de.dhbwstuttgart.syntaxtree.visual.ResultSetPrinter;
import de.dhbwstuttgart.typeinference.constraints.ConstraintSet;
import de.dhbwstuttgart.typeinference.constraints.Pair;
import de.dhbwstuttgart.typeinference.result.ResultSet;
import de.dhbwstuttgart.typeinference.unify.model.PairOperator;
import org.junit.Test;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Ref;
import java.util.*;

public class UnifyWithoutWildcards {

    public static final String tempDirectory = "/tmp/";

    @Test
    public void adapt() throws InterruptedException, IOException, ClassNotFoundException {
        ConstraintSet<Pair> testSet = new ConstraintSet<>();
        List<RefTypeOrTPHOrWildcardOrGeneric> list1 = Arrays.asList(TypePlaceholder.fresh(new NullToken()));
        List<RefTypeOrTPHOrWildcardOrGeneric> list2 = Arrays.asList(TypePlaceholder.fresh(new NullToken()),TypePlaceholder.fresh(new NullToken()));
        RefType t1 = new RefType(new JavaClassName("asp.UnifyWithoutWildcards$Matrix"), list1, new NullToken());
        RefType t2 = new RefType(new JavaClassName("java.util.HashMap"), list2, new NullToken());
        testSet.addUndConstraint(new Pair(t1, t2, PairOperator.SMALLERDOT));
        ResultSet resultSet = run(testSet);
        System.out.println(ResultSetPrinter.print(resultSet));
        assert resultSet.results.size() > 0;
    }

    public ResultSet run(ConstraintSet<Pair> toTest) throws IOException, InterruptedException, ClassNotFoundException {
        String content = "";
        content = ASPFactory.generateASP(toTest, this.getFC());

        PrintWriter writer = new PrintWriter(tempDirectory + "test.lp", "UTF-8");
        writer.println(content);
        writer.close();
        Clingo clingo = new Clingo(Arrays.asList(new File(tempDirectory + "test.lp")));
        String result = clingo.runClingo();
        ResultSet resultSet = ASPParser.parse(result, getInvolvedTPHS(toTest));
        return resultSet;
    }

    private static class TPHExtractor implements TypeVisitor<List<TypePlaceholder>>{
        @Override
        public List<TypePlaceholder> visit(RefType refType) {
            ArrayList<TypePlaceholder> ret = new ArrayList<>();
            for(RefTypeOrTPHOrWildcardOrGeneric param : refType.getParaList()){
                ret.addAll(param.acceptTV(this));
            }
            return ret;
        }

        @Override
        public List<TypePlaceholder> visit(SuperWildcardType superWildcardType) {
            return superWildcardType.getInnerType().acceptTV(this);
        }

        @Override
        public List<TypePlaceholder> visit(TypePlaceholder typePlaceholder) {
            return Arrays.asList(typePlaceholder);
        }

        @Override
        public List<TypePlaceholder> visit(ExtendsWildcardType extendsWildcardType) {
            return extendsWildcardType.getInnerType().acceptTV(this);
        }

        @Override
        public List<TypePlaceholder> visit(GenericRefType genericRefType) {
            return new ArrayList<>();
        }
    }

    protected Collection<TypePlaceholder> getInvolvedTPHS(ConstraintSet<Pair> toTest) {
        List<TypePlaceholder> ret = new ArrayList<>();
        toTest.map((Pair p)-> {
            ret.addAll(p.TA1.acceptTV(new TPHExtractor()));
            ret.addAll(p.TA2.acceptTV(new TPHExtractor()));
            return p;
        });
        return ret;
    }

    private Collection<ClassOrInterface> getFC() {
        Set<ClassOrInterface> ret = new HashSet<>();
        ret.add(ASTFactory.createClass(Matrix.class));
        //ret.add(ASTFactory.createObjectClass());
        //ret.add(ASTFactory.createClass(java.util.List.class));
        return ret;
    }
    private class Matrix<A> extends HashMap<A,Map<Integer, A>>{}
}
