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

// -*-Mode: Java-*- 
// $Id: InternalRule.java,v 1.5 2003/03/06 00:40:39 moltmans Exp $
// InternalRule.java -- 
// Author: Michael Oltmans <moltmans@ai.mit.edu> 
// Created: <Wed May 03 16:33:55 2000> 
// Time-stamp: <2000-05-29 15:04:42 moltmans> 
package rec.tms;

import java.util.*;
import rec.util.*;

/**
 **/
class InternalRule
{
  private static int m_idCounter = 0;

  private JTRE m_jtre;
  private int m_id;
  /** The top level Rule defined by user **/
  private Rule m_rule;
  /** Bindings for previous triggers--elements are Datum. **/
  private LinkedList m_bindings;
  /** Triggers for the rest of the rule--elements are Datum. **/
  private LinkedList m_triggers;
  /**
   * List of DBClass objects.  A fact must be of one of these types to
   * fire the rule.
   **/
  private LinkedList m_classes;

  /**
   * @param rule the user defined rule this is derived from
   * @param bindings list of bindings for earlier triggers--elements
   *                 are datums
   * @param triggers list of triggers for the rest of the
   *                 rule--elements are Strings
   **/
  InternalRule( JTRE jtre, Rule rule, LinkedList bindings,
		LinkedList triggers )
  {
    m_jtre = jtre;
    m_id = m_idCounter++;
    m_rule = rule;
    Assert.check( !triggers.isEmpty(),
		  "Attempt to generate rule with no triggers" );
    m_triggers = new LinkedList( triggers );
    m_bindings = new LinkedList( bindings );
    m_classes = new LinkedList();
    
    Object obj = m_triggers.removeFirst();

    if( obj instanceof String[] ) {
      String[] strings = (String[])obj;
      for( int i=0; i<strings.length; i++ )
	m_classes.add( m_jtre.getDBClass( strings[i] ) );
    }
    else
      m_classes.add( m_jtre.getDBClass( (String)obj ) );
    
//    m_class = m_jtre.getDBClass( (String)m_triggers.removeFirst() );
//      System.out.println( "New Rule: " + this );
//      System.out.println( "    bindings: " + m_bindings );
//      System.out.println( "    triggers: " + triggers );
  }
  
  /**
   * @return The classes of inputs to which this rule applies.  The
   *         list elements are DBClass objects.
   **/
  public final List getDBClasses() 
  {
    return m_classes;
  }

  public boolean match( Datum datum ) 
  {
    LinkedList bindings = Datum.datumsToFacts( m_bindings, datum );
    try {
      boolean result = m_rule.match( bindings );
      return result;
    }
    catch( Exception e ) {
      e.printStackTrace();
      Assert.note( "Error running rule: " + this );
      return false;
    }
  }

  public void run( Datum datum ) 
  {
    if( datum.getNode().isOut() ) {
      datum.getNode().addInRule( new RuleApplication( this, datum ) );
    }
        
    // if this is the lowest nesting of rules run the parent rule
    // otherwise create an internal rule to match the next trigger
    else if( m_triggers.isEmpty() ) {
      LinkedList bindings = Datum.datumsToFacts( m_bindings, datum );
      m_rule.run( m_jtre, bindings );
    }
    else {
      LinkedList triggers = new LinkedList( m_triggers );
      LinkedList bindings = new LinkedList( m_bindings );
      bindings.addLast( datum );

      InternalRule r = new InternalRule( m_jtre, m_rule,
					 bindings, triggers );
      m_jtre.insertRule( r );
    }
  }

  public String toString() 
  {
    return "<internalrule: " + m_rule + " " + m_bindings + " " + ">";
  }
  
    
}
