/**
 * Copyright (c) 2004-2005, Regents of the University of California
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *
 * Neither the name of the University of California, Los Angeles nor the
 * names of its contributors may be used to endorse or promote products
 * derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package jintgen.gen;

import cck.text.*;
import cck.util.Option;
import cck.util.Util;
import jintgen.isdl.*;
import jintgen.isdl.parser.Token;
import jintgen.jigir.*;
import java.io.PrintStream;
import java.util.HashMap;
import java.util.List;

/**
 * @author Ben L. Titzer
 */
public class CodemapGenerator extends Generator {

    private Printer printer;
    HashMap<String, Integer> registerMap = new HashMap<String, Integer>();

    protected final Option.Str CODEMAP_FILE = options.newOption("codemap-template", "CodeMap.java",
            "This option specifies the name of the file that contains a template for generating the " +
            "code map.");

    public void generate() throws Exception {
        if ( CODEMAP_FILE.isBlank() )
            Util.userError("No template file specified");
        SectionFile sf = createSectionFile(CODEMAP_FILE.get(), "CODEMAP GENERATOR");
        printer = new Printer(new PrintStream(sf));

        initializeRegisterMap();
        printer.indent();
        for ( InstrDecl d : arch.instructions ) visit(d);
        generateHelperMethods();
        printer.unindent();
    }

    public void initializeRegisterMap() {
        // TODO: this is not portable
        for (int cntr = 0; cntr < 32; cntr++) {
            registerMap.put("R" + cntr, cntr);
        }
        throw Util.unimplemented();
    }

    static class Operand {
        boolean integer;
        String name;
    }

    public void visit(InstrDecl d) {
        printer.println("public void visit(" + d.getClassName() + " i) {");
        printer.indent();
        printer.println("Stmt stmt;");

        sgen.lastblock = -1;
        egen.operands = new HashMap<String, Operand>();

        for ( AddrModeDecl.Operand o : d.getOperands() ) {
            Operand op = new Operand();
            op.name = o.name.image;
            egen.operands.put(o.name.toString(), op);
        }

        String bname = generateBlock(d.code.getStmts(), "\"===== \"+i.getName()+\" \"+i.getOperands()+" +
                "\" ==========================================\"");

        printer.print("result = new CodeRegion(new LinkedList(), " + bname + ");");
        printer.unindent();
        printer.nextln();
        printer.println("}");
    }

    public final ExprGenerator egen = new ExprGenerator();
    public final StmtGenerator sgen = new StmtGenerator();

    protected int biggestList;

    protected String generateBlock(List<Stmt> stmts, String comment) {
        String lname = "list" + (++sgen.lastblock);

        printer.println("LinkedList " + lname + " = new LinkedList();");

        if (comment != null) {
            printer.println("stmt = new CommentStmt(" + comment + ");");
            printer.println(lname + ".addLast(stmt);");
        }

        for ( Stmt s : stmts ) {
            s.accept(sgen);
            printer.println(lname + ".addLast(stmt);");
        }

        return lname;
    }

    protected void generateHelperMethods() {
        for (int cntr = 1; cntr <= biggestList; cntr++) {
            printer.print("protected LinkedList tolist" + cntr + '(');
            for (int var = 1; var <= cntr; var++) {
                printer.print("Object o" + var);
                if (var < cntr) printer.print(", ");
            }
            printer.println(") {");
            printer.indent();
            printer.println("LinkedList retlist = new LinkedList();");
            for (int var = 1; var <= cntr; var++) {
                printer.println("retlist.addLast(o" + var + ");");
            }
            printer.println("return retlist;");
            printer.unindent();
            printer.nextln();
            printer.println("}");
        }
    }

    protected void generateExprList(List<Expr> exprs) {
        int len = exprs.size();
        String hname = "tolist" + len;
        if (len > biggestList) biggestList = len;
        if (len == 0) {
            printer.print("new LinkedList()");
        } else {
            printer.print(hname + '(');
            int cntr = 0;
            for ( Expr e : exprs ) {
                if ( cntr++ != 0 ) printer.print(", ");
                e.accept(egen);
            }
            printer.print(")");
        }
    }

    protected class StmtGenerator implements StmtVisitor {
        int lastblock;

        public void visit(CallStmt s) {
            printer.print("stmt = new CallStmt(");
            generate(s.method);
            printer.print(", ");
            generateExprList(s.args);
            printer.println(");");
        }

        public void visit(WriteStmt s) {
            throw Util.unimplemented();
        }

        public void visit(CommentStmt s) {
            printer.println(s.toString());
        }

        public void visit(DeclStmt s) {
            generate("DeclStmt", s.name, s.typeRef, s.init);
        }

        public void visit(IfStmt s) {
            String ltrue = generateBlock(s.trueBranch, null);
            String lfalse = generateBlock(s.falseBranch, null);
            generate("IfStmt", s.cond, ltrue, lfalse);
        }

        public void visit(ReturnStmt s) {
            printer.print("stmt = new ReturnStmt(");
            generate(s.expr);
            printer.println(");");
        }

        public void visit(AssignStmt s) {
            throw Util.unimplemented();
        }

        public void visit(AssignStmt.Var s) {
            throw Util.unimplemented();
        }

        public void visit(AssignStmt.Map s) {
            throw Util.unimplemented();
        }

        public void visit(AssignStmt.Bit s) {
            throw Util.unimplemented();
        }

        public void visit(AssignStmt.FixedRange s) {
            throw Util.unimplemented();
        }

        // unused
/*        private void generate(String clname, Object o1, Object o2, Object o3, Object o4) {
            printer.print("stmt = new " + clname + '(');
            generate(o1);
            printer.print(", ");
            generate(o2);
            printer.print(", ");
            generate(o3);
            printer.print(", ");
            generate(o4);
            printer.println(");");
        }
*/
        private void generate(String clname, Object o1, Object o2, Object o3) {
            printer.print("stmt = new " + clname + '(');
            generate(o1);
            printer.print(", ");
            generate(o2);
            printer.print(", ");
            generate(o3);
            printer.println(");");
        }

        // unused
/*        private void generate(String clname, Object o1, Object o2) {
            printer.print("stmt = new " + clname + '(');
            generate(o1);
            printer.print(", ");
            generate(o2);
            printer.println(");");
        }
*/
        private void generate(Object o) {
            if (o instanceof Expr)
                ((Expr)o).accept(egen);
            else if (o instanceof Token)
                printer.print(StringUtil.quote(o));
            else
                printer.print(o.toString());
        }

        // unused
/*        private void generate(int i) {
            printer.print("" + i);
        }
*/    
    }

    protected class ExprGenerator implements CodeVisitor {
        HashMap<String, Operand> operands;

        public void generate(BinOpExpr e, String clname) {
            printer.print("new BinOpExpr(");
            e.left.accept(this);
            printer.print(", ");
            e.right.accept(this);
            printer.print(")");
        }

        private void generate(UnOpExpr e, String clname) {
            printer.print("new UnOpExpr(");
            e.expr.accept(this);
            printer.print(")");
        }

        //- Begin real visitor code

        public void visit(BinOpExpr e) {
            generate(e, "AddExpr");
        }

        public void visit(IndexExpr e) {
            printer.print("new IndexExpr(");
            e.expr.accept(this);
            printer.print(", ");
            e.index.accept(this);
            printer.print(")");
        }

        public void visit(FixedRangeExpr e) {
            printer.print("new FixedRangeExpr(");
            e.expr.accept(this);
            printer.print(", " + e.low_bit + ", " + e.high_bit + ')');
        }

        public void visit(CallExpr e) {
            printer.print("new CallExpr(" + StringUtil.quote(e.method) + ", ");
            generateExprList(e.args);
            printer.print(")");
        }

        public void visit(ReadExpr e) {
            throw Util.unimplemented();
        }

        public void visit(ConversionExpr e) {
            printer.print("new ConversionExpr(");
            e.expr.accept(this);
            printer.print(", " + StringUtil.quote(e.typeRef) + ", ");
            printer.print(")");
        }

        public void visit(Literal.BoolExpr e) {
            printer.print("new Literal.BoolExpr(" + e.value + ')');
        }

        public void visit(Literal.IntExpr e) {
            printer.print("new Literal.IntExpr(" + e.value + ')');
        }

        public void visit(Literal.EnumVal e) {
            throw Util.unimplemented();
        }

        public void visit(UnOpExpr e) {
            generate(e, "UnOpExpr");
        }

        public void visit(VarExpr e) {
            String name = e.variable.toString();
            Operand op = operands.get(name);
            if (op != null) {
                generateOperandUse(op);
            } else {
                Integer i = getRegister(name);
                if (i != null) {
                    printer.print("new Literal.IntExpr(" + i.intValue() + ')');
                } else
                    generateVarUse(e);
            }
        }

        public void visit(DotExpr e) {
            throw Util.unimplemented();
        }

        private void generateVarUse(VarExpr e) {
            if ("nextPC".equals(e.variable.toString()))
                printer.print("new Literal.IntExpr(nextPC)");
            else
                printer.print("new VarExpr(" + StringUtil.quote(e.variable) + ')');
        }

        private void generateOperandUse(Operand op) {
            if (op.integer) {
                printer.print("new Literal.IntExpr(i." + op.name + ')');
            } else {
                printer.print("new Literal.IntExpr(i." + op.name + ".getNumber())");
            }
        }


        protected Integer getRegister(String name) {
            return registerMap.get(name);
        }

    }
}
