/**
 * Validate SFAL expressions and statements.
 * 
 * @author Antonio Cruz adc2104@columbia.edu
 * @version $Id: Validator.java,v 1.16 2007/12/17 02:49:53 admin Exp $
 */

import java.util.*;
import antlr.*;


public class Validator extends Meta implements SfalWalkerTokenTypes {
	
	boolean isValidating = false;
	public Validator(boolean validating) {
		isValidating = validating;
	}
	
	/**
	 * Validate a statement with one expression
	 * @param root - Expression node type
	 * @param nodes - A list of the Expression node's children nodes.
	 * @return
	 */
	boolean validate(int root, Expr e, SymbolTable top) 
	throws RecognitionException
	{
		if (!isValidating) return true;
		return validate(root, e, null, top);
	}
	
	/**
	 * Validate a statement with two expressions
	 * @param root
	 * @param e
	 * @param e2
	 * @param top
	 * @return
	 */
	boolean validate(int root, Expr e1, Expr e2, SymbolTable top) 
	throws RecognitionException
	{
		if (!isValidating) return true;
		
		switch(root) {
			case ASGN:
				/* Make sure e1 and e2 AS types are equal 
				 * Depending on node type, get AS type accordingly from SymbolTable,
				 * function mappings, or node mappings.
				 */
				
				if (e1 == null || e2 == null) return false;
				
				// SFAL node types, and AS types
				String t1 = e1.type, t2 = e2.type, t1_AS_type, t2_AS_type;
				
				if (t1.equals("ID")) {
					SymbolTable.SymTabEntry entry =
						(SymbolTable.SymTabEntry) top.get(e1.value);
					t1_AS_type = entry.type;
				}
				else if (t1.equals("FUNC_CALL")) {
					t1_AS_type = (String) Meta.FUNC_TO_TYPE.get(e1.value);
				}
				else {
					t1_AS_type = (String) Meta.NODE_TO_TYPE.get(t1);
				}
				
				if (t2.equals("ID")) {
					SymbolTable.SymTabEntry entry =
						(SymbolTable.SymTabEntry) top.get(e2.value);
					t2_AS_type = entry.type;
				}
				else if (t2.equals("FUNC_CALL")) {
					t2_AS_type = (String) Meta.FUNC_TO_TYPE.get(e2.value);
				}
				else {
					t2_AS_type = (String) Meta.NODE_TO_TYPE.get(t2);
				}
				
				
				if (t1_AS_type == null 
						|| t2_AS_type == null 
						|| !t1_AS_type.equals(t2_AS_type)) 
				{
					
					/* This is a BUG */
					if ((t1_AS_type != null && t2_AS_type != null) && 
						!(t1_AS_type.equals("Sprite") && t2_AS_type.equals("Number")))
					{
						e1.error="Incompatible types: e1: " 
							+ e1.type  + " e2: " + e2.type;
					}
				}
			
				
				
				return e1.error==null;
				
			case MULT:
			case DIV:
			case MOD:	
			case PLUS:
			case MINUS:
				
				if (e1 != null && e2 == null) {
					
					/* rvalue here is an arith expression, e.g. 2+2 */
					
					if (!(getType(e1.type).equals("Number"))) {
						e1.error = "BAD ARITH r_value: " + e1.toString();
						return false;
					}
				}
				else if (e2 != null) {
					
					// SFAL node types, and AS types
					t1 = e1.type;
					t2 = e2.type; 
					t1_AS_type = null;
					t2_AS_type = null;
					
					if (t1.equals("ID")) {
						SymbolTable.SymTabEntry entry =
							(SymbolTable.SymTabEntry) top.get(e1.value);
						t1_AS_type = entry.type;
					}
					else if (t1.equals("FUNC_CALL")) {
						t1_AS_type = (String) Meta.FUNC_TO_TYPE.get(e1.value);
					}
					else {
						t1_AS_type = (String) Meta.NODE_TO_TYPE.get(t1);
					}
					
					if (t2.equals("ID")) {
						SymbolTable.SymTabEntry entry =
							(SymbolTable.SymTabEntry) top.get(e2.value);
						t2_AS_type = entry.type;
					}
					else if (t2.equals("FUNC_CALL")) {
						t2_AS_type = (String) Meta.FUNC_TO_TYPE.get(e2.value);
					}
					else {
						t2_AS_type = (String) Meta.NODE_TO_TYPE.get(t2);
					}
					
					
					if (!t1_AS_type.equals(t2_AS_type)) {
						// if (t1_AS_type != null && t2_AS_type != null) {
							e1.error="Incompatible types: e1: " 
									+ e1.type  + " e2: " + e2.type;
						// }
					}
				}
				
				return e1.error==null;
				
			case FUNC_CALL:
				 /* Make sure the function exists, and there are the right number
                 * and type of function arguments.
                 */
				
				String functionArgs = (String) SFAL_FUNCTIONS.get(e1.value);
				
				String[] args = null;
                if (functionArgs.indexOf(",") != -1) args = functionArgs.split(",");
                else {
                	// handle makegroup and globalnumber which have no args
                	if (!functionArgs.equals("None")) {
                		
                		// functions w/ one arg
                		args = new String[] {functionArgs};
                	}
                	else { 
                		args = new String[] {};
                	}
                }
                
				return functionArgs!=null && (args.length == e1.values.size());
				
		}
		return false;
	}
	
	/**
     * Validate a loop statement
     * @param l
     * @return
     */
    boolean validateLoop(List l) {
    	if (!isValidating) return true;
    	
    	
        try {
            return l.size()==3
            && ((Expr)l.get(0)).type.equals("Number") 
            && ((Expr)l.get(1)).type.equals("Number")
            && ((Expr)l.get(2)).type.equals("ID");
        }
        catch (Exception e) {
        	System.err.println(e);
            return false;
        }
    }
	
	/* Try and get Node type mapping first */
	String getType(String t) {
		if (t==null) return "";
		String mapping = (String) NODE_TO_TYPE.get(t);
		if (mapping != null) return mapping;
		else return t;
	}
}
