package io.quarkus.qute;

import io.quarkus.qute.SectionHelperFactory.BlockInfo;
import io.quarkus.qute.TemplateNode.Origin;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;

/**
 * Each section tag consists of one or more blocks. The main block is always present. Additional blocks start with a label
 * definition: <code>{#label param1}</code>.
 */
public class SectionBlock {

    public final Origin origin;

    /**
     * Id generated by the parser. {@code main} for the main block.
     */
    public final String id;
    /**
     * Label used for the given part. {@code main} for the main block.
     */
    public final String label;
    /**
     * Map of parsed parameters.
     */
    public final Map<String, String> parameters;

    public final Map<String, Expression> expressions;

    /**
     * Section content.
     */
    final List<TemplateNode> nodes;

    public SectionBlock(Origin origin, String id, String label, Map<String, String> parameters,
            Map<String, Expression> expressions,
            List<TemplateNode> nodes) {
        this.origin = origin;
        this.id = id;
        this.label = label;
        this.parameters = parameters;
        this.expressions = expressions;
        this.nodes = ImmutableList.copyOf(nodes);
    }

    Set<Expression> getExpressions() {
        Set<Expression> expressions = new HashSet<>();
        expressions.addAll(this.expressions.values());
        for (TemplateNode node : nodes) {
            expressions.addAll(node.getExpressions());
        }
        return expressions;
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append("SectionBlock [origin=").append(origin).append(", id=").append(id).append(", label=").append(label)
                .append("]");
        return builder.toString();
    }

    static SectionBlock.Builder builder(String id, Function<String, Expression> expressionFunc,
            Function<String, TemplateException> errorFun) {
        return new Builder(id, expressionFunc, errorFun).setLabel(id);
    }

    static class Builder implements BlockInfo {

        private final String id;
        private Origin origin;
        private String label;
        private final Map<String, String> parameters;
        private final List<TemplateNode> nodes;
        private final Map<String, Expression> expressions;
        private final Function<String, Expression> expressionFun;
        private final Function<String, TemplateException> errorFun;

        public Builder(String id, Function<String, Expression> expressionFun, Function<String, TemplateException> errorFun) {
            this.id = id;
            this.parameters = new LinkedHashMap<>();
            this.nodes = new ArrayList<>();
            this.expressions = new LinkedHashMap<>();
            this.expressionFun = expressionFun;
            this.errorFun = errorFun;
        }

        SectionBlock.Builder setOrigin(Origin origin) {
            this.origin = origin;
            return this;
        }

        SectionBlock.Builder addNode(TemplateNode node) {
            nodes.add(node);
            return this;
        }

        SectionBlock.Builder addNodes(TemplateNode... nodes) {
            Collections.addAll(this.nodes, nodes);
            return this;
        }

        SectionBlock.Builder setLabel(String label) {
            this.label = label;
            return this;
        }

        SectionBlock.Builder addParameter(String name, String value) {
            this.parameters.put(name, value);
            return this;
        }

        @Override
        public Expression addExpression(String param, String value) {
            Expression expression = expressionFun.apply(value);
            expressions.put(param, expression);
            return expression;
        }

        public Map<String, String> getParameters() {
            return Collections.unmodifiableMap(parameters);
        }

        public String getLabel() {
            return label;
        }

        @Override
        public TemplateException createParserError(String message) {
            return errorFun.apply(message);
        }

        SectionBlock build() {
            return new SectionBlock(origin, id, label, parameters, expressions, nodes);
        }
    }

}
