// (c) MIT 2003.  All rights reserved.

// $Id: DynamicExtractor.java,v 1.2 2003/12/04 20:01:37 cadlerun Exp $
// Extractor.java -- 
// Author: Aaron Adler <cadlerun@fracas.ai.mit.edu> 
// Copyright: Copyright (C) 2003 by MIT.  All rights reserved.
// Created: <Mon Aug 26 12:48:25 2002> 

package rec.multi;

import edu.mit.tools.watch.dataExtraction.ExtractorException;
import edu.mit.tools.watch.dataExtraction.ExFactFunctions;
import edu.mit.tools.watch.dataExtraction.ExStringFunctions;

import jess.Rete;
import jess.Jesp;
import jess.JessException;
import jess.Fact;
import jess.Value;
import jess.Context;

import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.io.File;
import java.io.FileReader;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;

import edu.mit.tools.watch.Utils;
import edu.mit.tools.watch.Logging;

import org.apache.log4j.Logger;

public class DynamicExtractor {

  private static Logger LOG = Logging.DYN_EXTRACTOR;
  
  private int m_sketchNum = 0;
  private int m_speechNum = 0;

  private Hashtable m_facts;
  private Hashtable m_sketchIds;
  
  private Rete m_r;
  private Jesp m_jesp;

  // This class will allow new input into the rule system dynamically.
  // So as speech and sketching events occur they can be added to the
  // system. There is the issue of removing a sketched object if
  // it becomes part of another object

  // ** TODO: deal with object removal. 
  
  public DynamicExtractor (boolean debugOutput) {
    // initialize the rete engine
    m_r = new Rete();
    m_r.addUserpackage(new ExStringFunctions());
    m_r.addUserpackage(new ExFactFunctions());

    try {
      if (debugOutput) {
	LOG.info("Enabling watch output");
	m_r.executeCommand("(watch all)");
      }
    } catch (JessException e) {}
    m_facts = new Hashtable();
    m_sketchIds = new Hashtable();
  }


  public void loadRules() throws ExtractorException {
    // loads the rules
    loadRulesSet1();
    // load the second set of rules
    loadRulesSet2();
    // load the third set of rules
    loadRulesSet3();
  }

  public Iterator getResults()
  {
    return m_r.listFacts();
  } 


  public void addSketchEvent(int startT, int endT, String objType, String actionType, String text, int objID)
    throws ExtractorException
  {
    int newId = m_sketchNum;
    Integer oldId = (Integer)m_sketchIds.get(text);
    if (oldId != null)
      newId = oldId.intValue();
    else {
      m_sketchIds.put(text, new Integer(m_sketchNum));
      m_sketchNum += 1;
    }
    
    StringBuffer str = new StringBuffer();
    str.append("(assert (sketch ");
    str.append("(text \""+text+"\") ");
    str.append("(startTime "+startT+") ");
    str.append("(endTime "+endT+") ");
    str.append("(idNum "+newId+") ");
    str.append("(objType \""+objType+"\") ");

    str.append("(objID "+objID+") ");
    
    str.append("(actionType "+actionType+") ");  
    str.append("))");
    
    String strID = startT+endT+objType+actionType+text;
    addNewEvent(str.toString(), strID);
  }
  
  public void removeSketchEvent(int startT, int endT, String objType, String actionType, String text)
    throws ExtractorException
  {
    String strID = startT+endT+objType+actionType+text;
    removeEvent(strID);
  }
  
  public void addSpeechEvent(int startT, int endT, String text)
    throws ExtractorException
  {
    StringBuffer str = new StringBuffer();

    str.append("(assert (speech ");
    str.append("(text \""+text+"\") ");
    str.append("(startTime "+startT+") ");
    str.append("(endTime "+endT+") ");
    str.append("(idNum "+m_speechNum+") ");
    str.append("))");

    m_speechNum += 1;
    
    String strID = startT+endT+text;
    addNewEvent(str.toString(), strID);
  }

  public void removeSpeechEvent(int startT, int endT, String text)
    throws ExtractorException
  {
    String strID = startT+endT+text;
    removeEvent(strID);
  }
  
  // --- private methods ---

  private void loadFile(String fileN) throws ExtractorException {
    // loads a file, and throws an ExtractorException if there is an error

    try {
      LOG.debug("trying to load file "+fileN);
      BufferedReader fileR = Utils.getFile(null, fileN, Utils.JESS_FILE_SUFFIX);

      // load the contents of the file
      m_jesp = new Jesp(fileR, m_r);
      m_jesp.parse(false);

    } catch (JessException e2) {
      LOG.error("Error parsing file " + e2.toString());
      throw new ExtractorException("Error parsing file: "+fileN+" Jesp error: "+e2);
    } catch (IOException e) {
      LOG.error("IO error "+e.toString());
      throw new ExtractorException(fileN +" is missing " + e + " \n");
    }
  }

  private void loadRulesSet1() throws ExtractorException {
    // loads the FIRST set of rules. Throws and ExtractorException if
    // there is an error.

    LOG.debug("Loading first set of files");
    loadFile(Utils.globalPath()+"dataExtraction.initialize");
    loadFile(Utils.globalPath()+"dataExtraction.templates");
    loadFile(Utils.globalPath()+"dataExtraction.functions");
    loadFile(Utils.globalPath()+"dataExtraction.gap_rules_tms");
    loadFile(Utils.globalPath()+"dataExtraction.text_rules_tms");
  }
  
  private void loadRulesSet2() throws ExtractorException {
    // loads the SECOND set of rules. Throws and ExtractorException if
    // there is an error.

    LOG.debug("Loading second set of files");
    loadFile(Utils.globalPath()+"dataExtraction.group_rules_tms");
  }

  private void loadRulesSet3() throws ExtractorException {
    // loads the THIRD set of rules. Throws and ExtractorException if
    // there is an error.

    LOG.debug("Loading third set of files");
    loadFile(Utils.globalPath()+"dataExtraction.sketch_unit_rules");
    loadFile(Utils.globalPath()+"dataExtraction.break_rules");

  }



  private void addNewEvent(String jessString, String strID) throws ExtractorException
  {
    // add the string to the rule system and then run the rule system.
    try {
      // add the command
      LOG.debug("SENDING JESS: "+jessString);
      Value val = m_r.executeCommand(jessString);
      Context c = m_r.getGlobalContext();
      int factID = val.factValue(c).getFactId();
      if (factID != -1) {
	m_facts.put(strID, new Integer (factID));
	LOG.debug("FactID "+factID);
	// run the rete
	LOG.debug("running rete");
	m_r.run();
      }
    } catch (JessException e) {
      LOG.error("JESS error while running: "+e.toString());
      throw new ExtractorException("JESS error while running! "+e);
    }
  }

  private void removeEvent(String strID) throws ExtractorException
  {
    // add the string to the rule system and then run the rule system.
    try {
      // remove command
      Integer factID = (Integer)m_facts.get(strID);
      String jessString = "(remove-string ?*dep* "+factID+")";
      //String jessString = "(retract "+factID+")";
      LOG.debug("SENDING JESS: "+jessString);
      m_r.executeCommand(jessString);      

      // run the rete
      LOG.debug("running rete");
      m_r.run();
    } catch (JessException e) {
      LOG.error("JESS error while running: "+e.toString());
      throw new ExtractorException("JESS error while running! "+e);
    }
  }
  
  private void printResults(Enumeration i1)
  {
    // print results
    while (i1.hasMoreElements())
    {
      LOG.debug(i1.nextElement());
    }
  }
  
}
    

    
