package net.northking.cctp.element.core.reflect;

import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import net.northking.cctp.element.core.IExecuteContext;
import net.northking.cctp.element.core.annotation.Argument;
import net.northking.cctp.element.core.annotation.ArgumentBusMap;
import net.northking.cctp.element.core.type.ParamScope;
import net.northking.cctp.element.util.Assert;
import net.northking.cctp.element.validation.ViolateComponentStandardsException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Assemble keyword arguments
 *
 * @author fengxiang.zhang
 * @since 1.0
 */
public class ArgumentGrouper implements IArgumentGrouper {

    private static final Logger logger = LoggerFactory.getLogger(ArgumentGrouper.class);
    private final Parameter[] parameters;

    public ArgumentGrouper(Parameter[] parameters) {
        this.parameters = parameters;
    }

    @Override
    public Object[] groupArguments(Map<String, Object> args, IExecuteContext context) throws Exception {
        Assert.notNull(args);
        List<Object> orderArgs = new ArrayList<Object>();
        if (this.parameters.length == 1) {
            ArgumentBusMap argumentBus = this.parameters[0].getAnnotation(ArgumentBusMap.class);
            if (argumentBus != null) {
                orderArgs.add(args);
                return orderArgs.toArray();
            }
        }
        for (Parameter parameter : this.parameters) {
            if (parameter.getType() == IExecuteContext.class) {
                orderArgs.add(context);
                continue;
            }
            Argument atpArgument = parameter.getAnnotation(Argument.class);
            if (atpArgument == null) {
                throw new ViolateComponentStandardsException("Keyword parameters must be annotated with @Argument.");
            }
            String name = atpArgument.name();
            if (!args.containsKey(name)) {
                if (atpArgument.required()) {
                    throw new IllegalArgumentException(String.format("Parameter name [%s] is required.", name));
                }
                logger.warn("Optional parameter name [{}] no input, using default value [{}]", name,
                        atpArgument.defaultValue());
                orderArgs.add(atpArgument.defaultValue());
            } else {
                if (atpArgument.notNull() && ParamScope.OUTPUT != atpArgument.scope()) {
                    // 输出返回变量可以为空
                    Assert.notNull(args.get(name),
                            String.format("组件参数:%s(%s)不能为null", atpArgument.name(), atpArgument.comment()));
                }
                orderArgs.add(args.get(name));
            }
        }
        return orderArgs.toArray();
    }
}