package code.lang;

import code.instr.Instruction;
import code.instr.Load;
import code.instr.Replace;
import code.modules.CurryModule;
import code.symbols.*;
import code.type.*;
import static code.type.PredefinedTypes.*;
import static code.type.TypeFactory.*;

import java.util.Vector;

/**
 * This class implements a minimal Tuple Curry module.
 *
 * @author Jimeng Liu
 * @since Sept 18, 2003
 */

public class TupleModule extends CurryModule {

    public static final String moduleName = "Prelude";

    private static Vector<Symbol> symbols = new Vector<Symbol>(); 

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

    private static void createSymbols() {
        symbols.add(unitTypeSymbol);
        symbols.add(unitConstructor);
	// for functions fst and snd
	symbols.add(makeTupleConstructor(2));
	symbols.add(makeTupleTypeSymbol(2));
        symbols.add(fstOperation);
        symbols.add(sndOperation);
    }

    private final static TypeSymbol unitTypeSymbol 
	= new TypeSymbol(moduleName, unitTypeName, 0, Symbol.Public);

    private final static DataSymbol unitConstructor 
	= new ConstructorSymbol(moduleName,
				"()",
				DataSymbol.toplevel,
				0,
				DataSymbol.Failure + 1,
				DataSymbol.NonInfix,
				DataSymbol.NonInfix,
				Symbol.Public,
				unitType);


    //implement first function   fst (x,_) = x
    private final static DataSymbol fstOperation 
	= new OperationSymbol(moduleName,
			      "fst",
			      DataSymbol.toplevel,
			      1,
			      DataSymbol.NonInfix,
			      DataSymbol.NonInfix,
			      Symbol.Public,
			      makeFunctionType
			      (makeApplicationType
			       (getTypeName(2),
				makeTypeVariable("a"), 
				makeTypeVariable("b")), 
			       makeTypeVariable("a")), 
			      new Instruction[]{
				  new Load(new byte[]{0, 0}),
				  Replace.singleton,
			      });

    //implement second function  snd (_,y) = y
    private final static DataSymbol sndOperation 
	= new OperationSymbol(moduleName,
			      "snd",
			      DataSymbol.toplevel,
			      1,
			      DataSymbol.NonInfix,
			      DataSymbol.NonInfix,
			      Symbol.Public,
			      makeFunctionType
			      (makeApplicationType
			       (getTypeName(2),
				makeTypeVariable("a"),
				makeTypeVariable("b")),
			       makeTypeVariable("b")), 
			      new Instruction[]{
				  new Load(new byte[]{0, 1}),
				  Replace.singleton,
			      });

    public static ConstructorSymbol makeTupleConstructor(int n) {
        String name = getConstructorName(n);
        FunctionType type 
	    = makeTupleConstructorTypeExpression(n);
        return new ConstructorSymbol(moduleName,
				     name,
				     DataSymbol.toplevel,
				     n,
				     DataSymbol.Constructor,
				     DataSymbol.NonInfix,
				     DataSymbol.NonInfix,
				     Symbol.Public,
				     type);
    }


    /** Construct the type of the constructor of an n-tuple */
    private static FunctionType makeTupleConstructorTypeExpression(int n) {
	TypeVariable [] array = new TypeVariable [n];
	for (int i = 0; i < n; i++) array[i] = makeTypeVariable(i);
	TypeConstructorApplication tc 
	    = makeApplicationType(getTypeName(n), array);
	FunctionType te = makeFunctionType(array[n-1], tc);
	for (int i = n-2; i>=0; i--) te = makeFunctionType(array[i], te);
	return te;
    }


    /**
     *  Construct the unqualified name name of the constructor 
     *  of an n-tuple.  The name is "(,,,...,,,)"
     */
    public static String getConstructorName(int n) {
        // size must >= 2
	if (n < 2)
	    throw new RuntimeException("Building an inconsistent tuple "+n);
	else {
	    String name = "(";
	    for (int i = 0; i < n - 1; ++i) name += ",";
	    name += ")";
	    return name;
	}
    }

    /** 
     *  Construct the qualified name of the type symbol of an n-tuple
     *  The unqualified name is "(,,,...,,,)"
     */
    private static String getTypeName(int n) {
        String name = getConstructorName(n);
        return moduleName + "." + name;
    }

    /** Construct the type symbol of an n-tuple. */
    public static TypeSymbol makeTupleTypeSymbol(int n) {
        return new TypeSymbol(moduleName, getTypeName(n), n, Symbol.Public);
    }

}
