package org.metaborg.meta.nabl2.solver.components;

import java.util.Optional;

import org.metaborg.meta.nabl2.constraints.ast.IAstConstraint;
import org.metaborg.meta.nabl2.constraints.messages.IMessageInfo;
import org.metaborg.meta.nabl2.solver.ASolver;
import org.metaborg.meta.nabl2.solver.ISolver.SeedResult;
import org.metaborg.meta.nabl2.solver.ISolver.SolveResult;
import org.metaborg.meta.nabl2.solver.SolverCore;
import org.metaborg.meta.nabl2.stratego.TermIndex;
import org.metaborg.meta.nabl2.terms.ITerm;
import org.metaborg.meta.nabl2.util.collections.IProperties;

public class AstComponent extends ASolver {

    private final IProperties.Transient<TermIndex, ITerm, ITerm> properties;

    public AstComponent(SolverCore core, IProperties.Transient<TermIndex, ITerm, ITerm> initial) {
        super(core);
        this.properties = initial;
    }

    // ------------------------------------------------------------------------------------------------------//

    public SeedResult seed(IProperties.Immutable<TermIndex, ITerm, ITerm> solution, IMessageInfo message)
            throws InterruptedException {
        solution.stream().forEach(entry -> {
            putProperty(entry._1(), entry._2(), entry._3());
        });
        return SeedResult.empty();
    }

    public Optional<SolveResult> solve(IAstConstraint constraint) throws InterruptedException {
        SolveResult result = constraint.match(IAstConstraint.Cases.of(astp -> {
            putProperty(astp.getIndex(), astp.getKey(), astp.getValue());
            return SolveResult.empty();
        }));
        return Optional.of(result);
    }

    private void putProperty(TermIndex index, ITerm key, ITerm value) {
        Optional<ITerm> prev = properties.getValue(index, key);
        if(!prev.isPresent()) {
            properties.putValue(index, key, value);
        } else {
            if(!value.equals(prev)) {
                throw new IllegalStateException("Should not set the same AST property multiple times.");
            }
        }
    }

    public IProperties.Immutable<TermIndex, ITerm, ITerm> finish() {
        return properties.freeze();
    }

}
@@ -120,26 +120,212 @@
 nts.
-ast.IAstConstraint
+IConstraint;%0Aimport org.metaborg.meta.nabl2.constraints.ast.CAstProperty;%0Aimport org.metaborg.meta.nabl2.constraints.ast.IAstConstraint;%0Aimport org.metaborg.meta.nabl2.constraints.equality.ImmutableCEqual
 ;%0Aim
@@ -1408,16 +1408,25 @@
 try._3()
+, message
 );%0A     
@@ -1652,102 +1652,287 @@
  -%3E 
-%7B%0A            putProperty(astp.getIndex(), astp.getKey(), astp.getValue());%0A            return
+solve(astp)));%0A        return Optional.of(result);%0A    %7D%0A%0A    private SolveResult solve(CAstProperty astp) %7B%0A        return putProperty(astp.getIndex(), astp.getKey(), astp.getValue(), astp.getMessageInfo())%0A                .map(cc -%3E SolveResult.constraints(cc)).orElseGet(() -%3E
  Sol
@@ -1951,82 +1951,51 @@
 ty()
+)
 ;%0A    
-  
-  %7D));%0A        return Optional.of(result);%0A    %7D%0A%0A    private void
+%7D%0A%0A    private Optional%3CIConstraint%3E
  put
@@ -2042,16 +2042,38 @@
 rm value
+, IMessageInfo message
 ) %7B%0A    
@@ -2228,173 +2228,133 @@
     
-%7D else %7B%0A            if(!value.equals(prev)) %7B%0A                throw new IllegalStateException(%22Should not set the same AST property multiple times.%22);%0A            %7D
+    return Optional.empty();%0A        %7D else %7B%0A            return Optional.of(ImmutableCEqual.of(value, prev.get(), message));
 %0A   

Use unification for AST properties, to allow forwarding rules.