package carray.translate;

import java.util.Collections;

import polyglot.ast.NodeFactory;
import polyglot.ast.TypeNode;
import polyglot.ext.jl5.ast.JL5NodeFactory;
import polyglot.frontend.ExtensionInfo;
import polyglot.frontend.Job;
import polyglot.translate.ExtensionRewriter;
import polyglot.types.SemanticException;
import polyglot.types.Type;
import polyglot.util.Position;
import carray.CArrayOptions;
import carray.types.CArrayArrayType;
import carray.types.ConstArrayType;

/**
 * {@code CArrayRewriter} extends {@code ExtensionRewriter} to provide
 * translations for array types when a CArray program is translated into Java.
 */
public class CArrayRewriter extends ExtensionRewriter {
    protected boolean translateCArray;

    public CArrayRewriter(Job job, ExtensionInfo from_ext, ExtensionInfo to_ext) {
        super(job, from_ext, to_ext);
        CArrayOptions options = (CArrayOptions) from_ext.getOptions();
        this.translateCArray = options.translateCArray;
    }

    public boolean translcateCArray() {
        return translateCArray;
    }

    @Override
    public TypeNode typeToJava(Type t, Position pos) throws SemanticException {
        if (translateCArray) {
            // We are preserving type tests by translating to Java 5.
            if (t instanceof ConstArrayType) {
                ConstArrayType at = (ConstArrayType) t;
                Type base = at.base();
                if (base.isPrimitive()) {
                    return qq.parseType("ConstArray_" + base.toString());
                }
                else {
                    // X const[] --> ConstArray<? extends [[X]]>
                    JL5NodeFactory nf = (JL5NodeFactory) to_nf();
                    return nf.AmbTypeInstantiation(pos,
                                                   qq.parseType("ConstArray"),
                                                   Collections.<TypeNode> singletonList(nf.AmbWildCardExtends(pos,
                                                                                                              typeToJava(base,
                                                                                                                         base.position()))));
                }
            }
            if (t instanceof CArrayArrayType) {
                CArrayArrayType at = (CArrayArrayType) t;
                Type base = at.base();
                if (base.isPrimitive()) {
                    return qq.parseType("Array_" + base.toString());
                }
                else {
                    // X[] --> Array<[[X]]>
                    JL5NodeFactory nf = (JL5NodeFactory) to_nf();
                    return nf.AmbTypeInstantiation(pos,
                                                   qq.parseType("Array"),
                                                   Collections.<TypeNode> singletonList(typeToJava(base,
                                                                                                   base.position())));
                }
            }
        }
        else {
            // We are coercing constant arrays with traditional arrays by
            // translating to Java 1.4.
            if (t instanceof ConstArrayType) {
                // X const[] --> X[]
                ConstArrayType at = (ConstArrayType) t;
                Type base = at.base();
                NodeFactory nf = to_nf();
                return nf.ArrayTypeNode(pos, typeToJava(base, base.position()));
            }
        }
        return super.typeToJava(t, pos);
    }
}
