import java.util.*;
import java.io.*;

import edu.mit.six825.bn.functiontable.*;

/**
 * A data set implementation
 *
 * @author nocturne
 */
public class BNData {
    private FunctionVariableSet _vars;
    private List _data;

    private boolean USE_COUNT_CACHE = true;
    private Map/*HashableAssignment, Integer*/ countCache;

    /*
     * Sole constructor.
     */
    public BNData(FunctionVariableSet vars) {
	_vars = vars;
	_data = (List) new ArrayList();

	countCache = new HashMap();
    }

    public String toString() {
	String answer = "";

	for (Iterator i = _data.iterator(); i.hasNext(); ) {
	    answer += i.next().toString() + "\n";
	}
	return(answer);
    }

    /*************** Accessors ****************/

    /*
     * How many data points do we have?
     */
    public int size() {
	return(_data.size());
    }

    /*
     * How many data points are consistent with the given assignment?
     *
     * This is a quick and dirty implementation. The lookup cache
     * helps tremendously with its efficiency...
     */
    public int countMatching(Assignment ass) {

	if (USE_COUNT_CACHE) {
	    // Very sadly, the Assignment implementation throws an
	    // exception if you attempt to compare equality of
	    // assignments which involve different sets of variables.
	    //
	    // So we have to wrap our assignments in a class which
	    // provides the kind of equality we care about for
	    // purposes of a HashMap (and which definitely yields a
	    // reasonable hashcode() as well).
	    HashableAssignment ha = new HashableAssignment(ass);

	    if (countCache.containsKey(ha)) {
		// System.out.println("Cache hit for " + ass);
		Integer answer = (Integer)countCache.get(ha);
		return (answer.intValue());
	    } else {
		// System.out.println("Cache miss for " + ass);
		int tally = countMatchingNoCache(ass);
		countCache.put((Object)ha, new Integer(tally));
		return(tally);
	    }
	} else {
	    return countMatchingNoCache(ass);
	}
    }

    public int countMatchingNoCache(Assignment ass) {
	int tally = 0;
	for (Iterator dit = _data.iterator(); dit.hasNext(); ) {
	    // in b.congruent(a), a.variables is a subset of b.variables
	    if(((Assignment)dit.next()).congruent(ass)) {
		tally++;
	    }
	}
	return(tally);
    }


    /*************** Mutators *****************/

    /*
     * Add a single datum to this object.
     *
     * The specified assignment represents a data point, and ought to
     * contain an assigned value for all of variables recorded by
     * BNData. There's nothing that enforces this, however.
     */
    public void add(Assignment d) {
	_data.add(d);
    }

    /* Assumes you have independent code that sets up the BN structure. */
    public void readFile(String filename) {

	List varlist = (List) new ArrayList();

	/* Everything here has a boolean domain; take advantage of that. */
	Comparable [] numToDomain = new Comparable[] {ComparableBoolean.FALSE,
						      ComparableBoolean.TRUE};

	// This is intended as a map from index-in-data-line to
	// {index in a values array as needed by new-Assignment()}
	int [] AssIndexOfFilePos = new int[_vars.size()];

	try {
	    BufferedReader in = new BufferedReader(new FileReader(filename));
	    String line;

	    // Read in the first line, and determine variable ordering
	    if ((line = in.readLine()) != null) {
		String[] toks = line.split("\\s");

		tokenloop:
		for (int x=0; x<toks.length; x++) {
		    for (int i=0; i < _vars.size(); i++) {
			if (toks[x].equals(_vars.getVariable(i).toString())) {

			    varlist.add(_vars.getVariable(i));

			    // XXX FIXME this might be bogus
			    AssIndexOfFilePos[x] = i;

			    continue tokenloop;
			}
		    }
		    throw new IllegalArgumentException("Unrecognized var /"
						       + toks[x] + "/");
		}
	    } else {
		throw new IOException("Could not read data");
	    }

	    // Read actual data
	    while((line = in.readLine()) != null) {
		String[] toks = line.split("\\s");

		Comparable [] values = new Comparable[_vars.size()];

		for (int x=0; x<toks.length; x++) {
		    int valIndex = AssIndexOfFilePos[x];
		    if (toks[x].equals("1")) {
			values[valIndex] = ComparableBoolean.TRUE;
		    } else if (toks[x].equals("0")) {
			values[valIndex] = ComparableBoolean.FALSE;
		    } else {
			throw new IllegalArgumentException("Unrecognized val /"
						       + toks[x] + "/");
		    }
		}

		Assignment datum = new Assignment(_vars, values);
		_data.add(datum);
	    }
	    in.close();
	}
	catch (IOException e) {
	    System.out.println("ERROR: " + e);
	    System.exit(1);
	}
    }

    public Iterator iterator() {
        return _data.iterator();
    }
}    
