package ve;

import java.util.*;

/**
 * A BeliefNetwork contains a tuple of Variables (each with a domain)
 * and a set of conditional probability tables, here represented as a
 * {@link FactorCPT}.

 * @author David Poole
 * @version 0.1 2001-01-29
 **/
public class BeliefNetwork {
    /**
     * The variables of the belief network, in order. The ordering of
     * indexes to that array should be consistent with the ordering of
     * the variables, but the indexes don't have to coincide with the
     * IDs of the variables. The value variable.length does not give the
     * actual number of variables; use getNumVariables to determine
     * the number of variables. 
     **/
    private Variable[] variables;

    /**
     * get the variables of the belief network, in order. The ordering of
     * indexes to that array should be consistent with the ordering of
     * the variables, but the indexes don't have to coincide with the
     * IDs of the variables. The value variable.length does not give the
     * actual number of variables; use getNumVariables to determine
     * the number of variables. 
     **/
    public Variable[] getVariables(){
	return variables;
    }

    /**
     * The conditional probability tables asssociated with the
     * variables. The factor associated with probabilityFactors[i]
     * represents the conditional probability table of variables[i].  
     **/
    protected Factor[] probFactors;

    /**
     * return the conditiona probability tables asssociates with the
     * variables. The factor associated with probabilityFactors[i]
     * represents the conditional probability table of variables[i].  
     **/
    public Factor[] getProbFactors() {
	return probFactors;
    }

    /*
     * The number of random variables in the belief network.
     */
    protected int numVariables;


    /*
     * returns number of random variables in the belief network.
     */
     public int getNumVariables() {
	return numVariables;
    }

    private final int maxDomainSize=100;
    /** maximum number of variables in an input factor. */
    private final int maxFactorSize=20;

    /** maps the string name to the variable. */
    //private HashMap stringToVar = new HashMap();
    private Hashtable stringToVar = new Hashtable();    //to be Java 1.1 compatible

    /** returns the map from the string name to the variable. */
    public Hashtable getStringToVar() {
	return stringToVar;
    }

    /**
     * constructs a belief network from the extended CIspace
     * representation.  The extended representation lets us have
     * indexes or names as the parents. It assumes that the names are
     * not numbers (otherwise it assumes they are the indexes).
     *
     * @param s string representation of the belief network
     * @param maxVariables the maximum number of variables allowed 
     **/
    public BeliefNetwork(String s,int maxVariables){
	probFactors = new Factor[maxVariables];
	variables = new Variable[maxVariables];
	StringTokenizer graphTokenizer = new StringTokenizer(s,";");
        while ( graphTokenizer.hasMoreTokens() ) {
	    String inString = graphTokenizer.nextToken();
	    inString = inString.trim();
	    if (inString.indexOf("endBayesGraph") == -1) {
		String dataString = inString.substring (5,inString.length());
		StringTokenizer nodeTokenizer = 
		    new StringTokenizer(dataString,"/");
		String nodeName = nodeTokenizer.nextToken();
		String xpos = nodeTokenizer.nextToken();
		String ypos = nodeTokenizer.nextToken();
		String domainString = nodeTokenizer.nextToken();
		variables[numVariables]= new Variable(nodeName,stringToStringArray(domainString,maxDomainSize));
		stringToVar.put(nodeName,variables[numVariables]);
		String parentsString = nodeTokenizer.nextToken();
		String probsString = nodeTokenizer.nextToken();
		probFactors[numVariables] = 
		    removeRedundant(
				    new FactorCPT(
						  stringToParents(parentsString,numVariables,maxFactorSize),
						  probsString)   ,
				    variables[numVariables]
				    );
		if (! sorted(probFactors[numVariables].getVariables())) {
		    Variable[] vars=new Variable[probFactors[numVariables].getVariables().length];
		    for (int i=0; i<probFactors[numVariables].getVariables().length;i++) {
			vars[i]=probFactors[numVariables].getVariables()[i];
		    }
		    //Arrays.sort(vars);
		    DecisionNetwork.mysort(vars);    // Java 1.1 compatible
		    probFactors[numVariables]=new 
			FactorReorder(probFactors[numVariables],vars);
		}
		numVariables++;
	    }
	}
    }

    static private boolean sorted(Variable[] varray) {
	for (int i=1; i<varray.length; i++) {
	    if (varray[i-1].getId() >= varray[i].getId()) {
		return false;
	    }
	}
	return true;
    }

    static private String[] stringToStringArray(String str, int maxSize) {
	String[] tempResult = new String[maxSize];
	int lpar = str.indexOf('[');
	int rpar = str.indexOf(']');
	StringTokenizer tokenizer = new StringTokenizer(str.substring(lpar+1,rpar),",");
	int size=0;
	while (tokenizer.hasMoreTokens()) {
	    tempResult[size++]=tokenizer.nextToken().trim();
	}
	String[] result = new String[size];
	for (int i=0; i<size; i++) {
	    result[i]=tempResult[i];
	}
	return result;
    }

    /**
     * given the string, return the parents
     *
     * @param str the string containing the parents' index or name
     * @param index the index of the current variable
     * @param maxSize the maximum factor size
     **/
    private Variable[] stringToParents(String str, int index, int maxSize) {
	Variable[] tempResult = new Variable[maxSize];
	int lpar = str.indexOf('[');
	int rpar = str.indexOf(']');
	StringTokenizer tokenizer = new StringTokenizer(str.substring(lpar+1,rpar),",");
	int size=0;
	while (tokenizer.hasMoreTokens()) {
	    String nextToken = tokenizer.nextToken().trim();
	    try {
		tempResult[size]=variables[Integer.parseInt(nextToken)];
	    }
	    catch( Exception e) {   // not an integer; parent is the Name.
		// This should probably check the exception to make sure 
		// it is of the right type.
		Variable par = (Variable) stringToVar.get(nextToken);
		if (par==null) {  // should throw exception
		    System.out.println("Parent of node "+variables[index].getName()+" is undefined.");
		}
		tempResult[size]=par;
	    }
	    size++;
      	}
	Variable[] result = new Variable[size+1];
	for (int i=0; i<size; i++) {
	    result[i]=tempResult[i];
	}
	result[size]=variables[index];
	return result;
    }
    
    static Factor removeRedundant(Factor fac, Variable var) {
	int numVars = fac.getVariables().length;
	Variable[] redundantVars = new Variable[fac.getVariables().length];
	int numRedundantVars=0;
	for (int i=0; i < numVars; i++) {
	    if (fac.getVariables()[i] != var &&
		fac.isRedundant(fac.getVariables()[i])) {
		//  System.out.println("   Variable "+fac.getVariables()[i].getName()+" is redundant");
		redundantVars[numRedundantVars++]=fac.getVariables()[i];
	    }
	}
	if (numRedundantVars==0) {
	    return fac;
	}
	else {
	    Variable[] newVars = new Variable[numRedundantVars];
	    int[] vals = new int[numRedundantVars];
	    System.out.print("Redundant Variables in Definition of "+var.getName()+":");
	    for (int i=0; i< numRedundantVars; i++) {
		newVars[i]=redundantVars[i];
		System.out.print(" "+redundantVars[i].getName());
	    }
	    System.out.println(".");
	    return new FactorObserved(fac,newVars,vals);
	}
    }

    /**
     * constructs a belief network for the corresponding variables
     **/
    public BeliefNetwork(Variable[] vars, int numVars) {
	variables = vars;
	numVariables = numVars;
    }

    /**
     * Prints the belief network
     **/
    public void print() {
	for (int i=0; i<this.getNumVariables(); i++) {
	    System.out.println("\n Factor #"+i+":");
	    this.getProbFactors()[i].print();
	}
    }
    /**
     * Prints a summary of the belief network
     **/
    public void printBrief() {
	System.out.println("\n*** Belief Network ***");
	int count=0;
	for (int i=0; i<this.getNumVariables(); i++) {
	    System.out.print("Factor #"+i+": [");
	    this.getProbFactors()[i].printVariables();
	    System.out.println("]. Size="+this.getProbFactors()[i].size());
	    count+=this.getProbFactors()[i].size();
	}
	System.out.println("Number of factors: "+this.getNumVariables()+". Numer of parameters:"+count);
    }
}


