// Copyright (c) Corporation for National Research Initiatives

package org.python.compiler;

import java.util.ArrayList;
import java.util.List;

import org.python.antlr.ParseException;
import org.python.antlr.Visitor;
import org.python.antlr.ast.Assign;
import org.python.antlr.ast.Name;
import org.python.antlr.ast.Suite;
import org.python.antlr.ast.Tuple;
import org.python.antlr.ast.arguments;
import org.python.antlr.ast.expr_contextType;
import org.python.antlr.base.expr;
import org.python.antlr.base.stmt;

public class ArgListCompiler extends Visitor
{
    public boolean arglist, keywordlist;
    public List<expr> defaults;
    public List<String> names;
    public List<String> fpnames;
    public List<stmt> init_code;

    public ArgListCompiler() {
        arglist = keywordlist = false;
        defaults = null;
        names = new ArrayList<String>();
        fpnames = new ArrayList<String>();
        init_code = new ArrayList<stmt>();
    }

    public void reset() {
        arglist = keywordlist = false;
        defaults = null;
        names.clear();
        init_code.clear();
    }

    public void appendInitCode(Suite node) {
        node.getInternalBody().addAll(0, init_code);
    }

    public List<expr> getDefaults() {
        return defaults;
    }

    public void visitArgs(arguments args) throws Exception {
        for (int i = 0; i < args.getInternalArgs().size(); i++) {
            String name = (String) visit(args.getInternalArgs().get(i));
            names.add(name);
            if (args.getInternalArgs().get(i) instanceof Tuple) {
                List<expr> targets = new ArrayList<expr>();
                targets.add(args.getInternalArgs().get(i));
                Assign ass = new Assign(args.getInternalArgs().get(i),
                    targets,
                    new Name(args.getInternalArgs().get(i), name, expr_contextType.Load));
                init_code.add(ass);
            }
        }
        if (args.getInternalVararg() != null) {
            arglist = true;
            names.add(args.getInternalVararg());
        }
        if (args.getInternalKwarg() != null) {
            keywordlist = true;
            names.add(args.getInternalKwarg());
        }
        
        defaults = args.getInternalDefaults();
        for (int i = 0; i < defaults.size(); i++) {
            if (defaults.get(i) == null)
                throw new ParseException(
                    "non-default argument follows default argument",
                    args.getInternalArgs().get(args.getInternalArgs().size() - defaults.size() + i));
        }
    }

    @Override
    public Object visitName(Name node) throws Exception {
        //FIXME: do we need Store and Param, or just Param?
        if (node.getInternalCtx() != expr_contextType.Store && node.getInternalCtx() != expr_contextType.Param) {
            return null;
        } 

        if (fpnames.contains(node.getInternalId())) {
            throw new ParseException("duplicate argument name found: " +
                                     node.getInternalId(), node);
        }
        fpnames.add(node.getInternalId());
        return node.getInternalId();
    }

    @Override
    public Object visitTuple(Tuple node) throws Exception {
        StringBuffer name = new StringBuffer("(");
        int n = node.getInternalElts().size();
        for (int i = 0; i < n-1; i++) {
            name.append(visit(node.getInternalElts().get(i)));
            name.append(", ");
        }
        name.append(visit(node.getInternalElts().get(n - 1)));
        name.append(")");
        return name.toString();
    }
}
