package code.lang;

import code.instr.*;
import code.term.*;
import code.space.*;
import code.stuff.*;
import code.symbols.*;
import code.type.*;
import code.modules.*;
import code.loader.*;
import code.table.*;
import static code.type.PredefinedTypes.*;
import static code.type.TypeFactory.*;

import java.util.*;

/**
 *  This class implements the arithmentic and relational 
 *  binary operators on the builtin integers. 
 *  Their identifiers are: <PRE>
 *      +, -, *, div, mod
 *      <, <=, >, >=
 *  </PRE>
 *
 *@author     Sergio Antoy
 *@since      Feb 28, 2003
 */

public class IntModule extends CurryModule {

    // public final static String moduleName = "$Int";
    public final static String moduleName = "Prelude";

    public IntModule() {
        super(moduleName, new Vector(), null, 0, createSymbols(), false);
    }

    private final static TypeSymbol intTypeSymbol
	= new TypeSymbol(moduleName, intTypeName, 0, Symbol.Public);

    private static OperationSymbol makeOp(String symbolName, 
					  int associativity, 
					  int precedence, 
					  TypeExpression type, 
					  Instruction instruction) {
        return
                new OperationSymbol(moduleName, 
				    symbolName, 
				    0,
				    2, 
				    associativity, 
				    precedence, 
				    Symbol.Public, 
				    type, 
				    new Instruction[]{
                    new Load((byte) 0),
                    new Branch(new Instruction[][]{
                        Residuate.residuateSeq,
                        Fail.failSeq,
                        new Instruction[]{
                            new Load((byte) 1),
                            new Branch(new Instruction[][]{
                                Residuate.residuateSeq,
                                Fail.failSeq,
                                new Instruction[]{
                                    instruction,
                                    Replace.singleton
                                },
                            }),
                        },
                    }),
                });
    }


    private final static TypeExpression arithType
        = makeFunctionType(intType, makeFunctionType(intType, intType));

    private final static TypeExpression relatType
        = makeFunctionType(intType, makeFunctionType(intType, boolType));

    private final static Vector<Symbol> createSymbols() {
        Vector<Symbol> v = new Vector<Symbol>();
        v.add(intTypeSymbol);
        v.add(makeOp("+", DataSymbol.Left, 6, arithType, 
		     new PrimIntCode(PrimIntCode.ADD)));
        v.add(makeOp("-", DataSymbol.Left, 6, arithType, 
		     new PrimIntCode(PrimIntCode.SUB)));
        v.add(makeOp("*", DataSymbol.Left, 7, arithType, 
		     new PrimIntCode(PrimIntCode.MUL)));
        v.add(makeOp("div", DataSymbol.Left, 7, arithType,
		     new PrimIntCode(PrimIntCode.DIV)));
        v.add(makeOp("mod", DataSymbol.Left, 7, arithType,
		     new PrimIntCode(PrimIntCode.MOD)));
        v.add(makeOp("<", DataSymbol.NonAssoc, 4, relatType,
		     new PrimIntCode(PrimIntCode.LT)));
        v.add(makeOp("<=", DataSymbol.NonAssoc, 4, relatType,
		     new PrimIntCode(PrimIntCode.LEQ)));
        v.add(makeOp(">", DataSymbol.NonAssoc, 4, relatType,
		     new PrimIntCode(PrimIntCode.GT)));
        v.add(makeOp(">=", DataSymbol.NonAssoc, 4, relatType,
		     new PrimIntCode(PrimIntCode.GEQ)));
        return v;
    }

    private static class PrimIntCode implements Instruction {
        private final static int ADD = 0;
        private final static int SUB = 1;
        private final static int MUL = 2;
        private final static int DIV = 3;
        private final static int MOD = 4;
        private final static int LT = 5;
        private final static int LEQ = 6;
        private final static int GT = 7;
        private final static int GEQ = 8;

        private final int op;

        private PrimIntCode(int op) { this.op = op; }

	private final static String preludeName = "Prelude";
	private final static int trueId 
	    = ModuleTable.getId(preludeName, "True");
	private final static int falseId
	    = ModuleTable.getId(preludeName, "False");
	private final static Term termTrue
	    = new Term(trueId, Term.noArgs);
	private final static Term termFalse
	    = new Term(falseId, Term.noArgs);

        public void execute(Computation computation) {
            Term term = computation.getTerm();
            // term is of the form "int op int" or we wouldn't be here
            Term[] argument = term.getArgument();
            int left = ((TermImplInt) argument[0].getRepresentation()).value;
            int right = ((TermImplInt) argument[1].getRepresentation()).value;
            Term result;
            switch (op) {
                case ADD:
                    result = new Term(left + right);
                    break;
                case SUB:
                    result = new Term(left - right);
                    break;
                case MUL:
                    result = new Term(left * right);
                    break;
                case DIV:
                    result = right == 0
			? SuccessModule.termFail 
			: new Term(left / right);
                    break;
                case MOD:
                    result = right == 0
			? SuccessModule.termFail
			: new Term(left % right);
                    break;
                case LT:
                    result = left < right ? termTrue : termFalse;
                    break;
                case LEQ:
                    result = left <= right ? termTrue : termFalse;
                    break;
                case GT:
                    result = left > right ? termTrue : termFalse;
                    break;
                case GEQ:
                    result = left >= right ? termTrue : termFalse;
                    break;
                default:
                    throw new RuntimeException("Bad builtin integer operation id");
            }
            Space.instance.current = result;
        }
	
        private final static String [] name = {
            "+", "-", "*", "div", "%", "<", "<=", ">", ">=", "==",
        };

        public String printAsTxtLoadable() {
            return "external function \"" + name[op] + "\"";
        }


    }

}

