package org.jboss.windup.rules.apps.java.condition.annotation;

import java.util.HashMap;
import java.util.Map;

import org.jboss.windup.config.GraphRewrite;
import org.jboss.windup.config.condition.EvaluationStrategy;
import org.jboss.windup.rules.apps.java.scan.ast.annotations.JavaAnnotationTypeReferenceModel;
import org.jboss.windup.rules.apps.java.scan.ast.annotations.JavaAnnotationTypeValueModel;
import org.ocpsoft.rewrite.context.EvaluationContext;
import org.ocpsoft.rewrite.param.ParameterizedPatternResult;
import org.ocpsoft.rewrite.param.RegexParameterizedPatternParser;

/**
 * Matches on an Annotation type. For example, this would be used if the value of an annotation element
 * is itself another annotation.
 *
 * @author <a href="mailto:jesse.sightler@gmail.com">Jesse Sightler</a>
 */
public class AnnotationTypeCondition extends AnnotationCondition
{
    private RegexParameterizedPatternParser pattern;
    private Map<String, AnnotationCondition> conditions = new HashMap<>();

    /**
     * Creates an {@link AnnotationTypeCondition} with the provided pattern.
     */
    public AnnotationTypeCondition(String pattern)
    {
        this.pattern = new RegexParameterizedPatternParser(pattern);
    }

    /**
     * Adds another condition for an element within this annotation.
     */
    public AnnotationTypeCondition addCondition(String element, AnnotationCondition condition)
    {
        this.conditions.put(element, condition);
        return this;
    }

    @Override
    public String toString()
    {
        return "AnnotationTypeCondition{" +
                    "pattern=" + pattern +
                    ", conditions=" + conditions +
                    '}';
    }

    public boolean evaluate(GraphRewrite event, EvaluationContext context, EvaluationStrategy strategy, JavaAnnotationTypeValueModel value)
    {
        if (!(value instanceof JavaAnnotationTypeReferenceModel))
            return false;

        JavaAnnotationTypeReferenceModel typeReferenceModel = (JavaAnnotationTypeReferenceModel) value;

        // submit the value to the value pattern
        if (pattern != null)
        {
            String annotationValue = typeReferenceModel.getResolvedSourceSnippit();

            ParameterizedPatternResult referenceResult = pattern.parse(annotationValue);
            if (!referenceResult.matches())
                return false;

            referenceResult.submit(event, context);
        }

        return evaluateChildConditions(event, context, strategy, typeReferenceModel);
    }

    protected boolean evaluateChildConditions(GraphRewrite event, EvaluationContext context, EvaluationStrategy strategy,
                JavaAnnotationTypeReferenceModel annotation)
    {
        // recursively scan additional conditions
        for (Map.Entry<String, AnnotationCondition> conditionEntry : conditions.entrySet())
        {
            JavaAnnotationTypeValueModel subValue = annotation.getAnnotationValues().get(conditionEntry.getKey());

            if (subValue == null)
                return false;

            if (!conditionEntry.getValue().evaluate(event, context, strategy, subValue))
                return false;
        }
        return true;
    }
}
