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

// $Id: Notebook.java,v 1.26 2005/02/17 21:40:57 moltmans Exp $
// Notebook.java -- 
// Author: Michael Oltmans <moltmans@ai.mit.edu> 
// Created: <Mon May 08 12:51:55 2000> 
// Time-stamp: <2005-02-16 13:59:05 moltmans> 
package rec.annotate.rules;

import javax.swing.*;
import java.util.*;
import rec.sys.*;
import rec.util.*;
import rec.simulate.widgets.*;
import rec.simulate.*;
import rec.tms.*;
import rec.speech.*;
import rec.annotate.gui.*;
import rec.annotate.widgets.*;
import rec.annotate.facts.*;
import rec.annotate.lisp.LispTMS;
import rec.annotate.lisp.Lisp;
import com.franz.jlinker.JavaLinkDist;
import rec.annotate.lisp.LispObj;

/**
 **/
public class Notebook 
{
  private LispTMS m_tms;
  public static Surface m_surface;
  private AnnotationBrowser m_browser;
  private CausalStructure m_causes;
  private HashMap m_annotationMap = new HashMap();
  private HashMap m_symbolMap = new HashMap();
  private int m_idCounter = 0;
  private int m_timer = 0;

  public Notebook( Surface s )
  {
    m_surface = s;
    m_tms = new LispTMS(s.getSystemProperty("lisp-exe"));
    m_tms.loadFile("assistance.lsp");
    m_tms.loadFile("answer.lsp");
    m_tms.readEval("(make-assistance-tms)");
    m_tms.loadFile("rules.lsp");
    //    m_tms.readEval("d
    m_tms.setq("*assistance-notebook*", JavaLinkDist.newDistOb(this));
    //    m_causes = new CausalStructure( this, m_tms );


  }

  public void assertDesign()
  {
    Iterator it = m_surface.getSurfaceWidgets().iterator();
    Widget o;

    if( isGravityOn() ) {
      assume( "(gravity)", ":REC-SYSTEM" );
    }

    while( it.hasNext() ) {
      o = (Widget)it.next();
      if (o instanceof WSimBody ) {
	assumeSimObj(o, "body");
      }
      if (o instanceof WSimAnchor ) {
	assumeSimObj(o, "anchor");
      }
      if (o instanceof WSimMotor ) {
        assumeSimObj(o, "motor");
      }
      if (o instanceof WSimTorque ) {
        assumeSimObj(o, "torque");
      }
      if (o instanceof WSimSpringDamper ) {
        assumeSimObj(o, "springdamper");
      }
      if (o instanceof WSimRotationalDamper ) {
	  assumeSimObj(o, "rotationaldamper");
      }
      if (o instanceof WSimSpring ) {
	assumeSimObj(o, "spring");
      }
      if (o instanceof WSimPinJoint ) {
	assumeSimObj(o, "pin-joint");
      }
      if (o instanceof WSimRod ) {
	assumeSimObj(o, "rod");
      }
      if (o instanceof WSimPulley ) {
	assumeSimObj(o, "pulley");
      }
      if (o instanceof WSimConstraint) {
	assumeConstraint((WSimConstraint)o);
      }
      
    }

	//    displayBrowser();
	//    updateNotes();
  }

  private void assumeSimObj(Widget o, String type) 
  {
    String symbol = getSymbol(o);
    String name = o.getEnglishName();
    if ( ! (name == null || name.equals("")) ) {
      assume(new String[] {"name",
			   Lisp.quotes(o.getEnglishName()),
			   symbol},
	     ":REC-SYSTEM");
    }
    
    assume(new String[] {type, symbol}, ":REC-SYSTEM");
  }
  
  private void assumeConstraint(WSimConstraint constraint) 
  {
    int num = constraint.numAttached();
    String symbol = getSymbol(constraint);
    List bodies = constraint.getBodies();
    
    Iterator it = bodies.iterator();
    while( it.hasNext() ) {
      Object obj = (Object)it.next();
      assume(new String[] {"attaches", symbol, getSymbol(obj)},
	     ":REC-SYSTEM");
    }
    if (bodies.size() < 2) {
      assume(new String[] {"attaches", symbol, "background"},
	     ":REC-SYSTEM");
    }
    
  }

  public void assumeTimeStamp(Object o) 
  {
    assume(new String[] {"time", getSymbol(o), String.valueOf(m_timer++)},
	   ":REC-SYSTEM");
  }

  public void displayBrowser() 
  {
    m_browser = new AnnotationBrowser( this );
    m_browser.startBrowser();
  }

  public void addRule( AnnotateRule rule )
  {
    rule.setSurface( m_surface );
    //    m_tms.insertRule( rule );
  }
  
  public void myassert( Fact fact, String reason,
		      LinkedList  antecedents )
  {
    DebugWindow.addLine("TMS", "Unimplimented Assert call");
    
//      Assert.notNull( a, "Attempt to assert null fact" );
//      DebugWindow.addLine( "Time",
//  			 a.toEnglish() + " (" +
//  			 a.getStartTime() + ", " + a.getEndTime() + ")" );
    
//      m_tms.assert( a, reason, antecedents );
  }

//    public void assert(String fact, String reason)
//    {
//      m_tms.assert(fact, reason);
//    }

//    public void assert(String[] fact, String[] reason)
//    {
//      m_tms.assert(fact, reason);
//    }

  public void assume(String[] fact, String reason) 
  {
    m_tms.assume(fact, reason);
  }
  
  public void assume( String fact, String reason )
  {
//      DebugWindow.addLine( "Time",
//  			 a.toEnglish() + " (" +
//  			 a.getStartTime() + ", " + a.getEndTime() + ")" );
    m_tms.assume( fact, reason );
  }

  public void retract(String fact, String reason) 
  {
    m_tms.retract(fact, reason);
  }
  public void retract(String[] fact, String reason) 
  {
    m_tms.retract(fact, reason);
  }
  

  public void retract( Annotation a, String reason )
  {
    DebugWindow.addLine("Lisp", "retract: Not yet implimented");
    
    //    m_tms.retract( a, reason );
  }

  /**
   * This needs to be called after a round of assertions are made.
   **/
  public void updateNotes() 
  {
    m_tms.runInLisp("process-new-input");
    //    m_causes.update();
    if( m_browser != null ) {
      m_browser.updateNotes();
    }
  }

  /**
   * This method is called by the Lisp process to querry about the
   * enclosed querries...
   **/
  public boolean doJavaQuery(LinkedList list)
  {
    Iterator it = list.iterator();
    String str = (String)it.next();
//      DebugWindow.addLine("Lisp", "Java-query: " + str);
    if (str.toLowerCase().equals("arrow-path-close-by")) {
      WMotionArrow arrow = (WMotionArrow)lookupSymbol((String)it.next());
      WSimBody body1 = (WSimBody)lookupSymbol((String)it.next());
      WSimBody body2 = (WSimBody)lookupSymbol((String)it.next());
//        DebugWindow.addLine("Lisp",
//  			  "checking arrow-path-close-by: " +
//  			  "\narrow= " + arrow +
//  			  "\nbody1= " + body1 +
//  			  "\nbody2= " + body2);
      return arrow.passesBy(body1, body2);
    }
    Assert.error("Undefined query");
    return false;
  }

  public String getSymbol(Object object)
  {
    String symbol;

    if (m_annotationMap.containsKey(object)) {
      return (String)m_annotationMap.get(object);
    }

    if (object instanceof WSimObject) {
      symbol = ((WSimObject)object).getEnglishName();
      if ( symbol != null && ! symbol.equals("") ) {
	return getSymbol(object, symbol);
      }
    }

    symbol = object.getClass().getName();
    symbol = symbol.substring(symbol.lastIndexOf(".") + 1);
    symbol += "-" + m_idCounter++;
    return getSymbol(object, symbol);
  }

  private String getSymbol(Object object, String rootName) 
  {
    if ( rootName == null || rootName.equals("") ) {
      rootName = object.toString();
    }
    
    rootName = rootName.replace(' ', '-');
    rootName = rootName.replace('$', '-');
    rootName = rootName.replace('\'', '-');
    rootName = rootName.replace('`', '-');
    rootName = rootName.toUpperCase();
    
    if (m_annotationMap.containsKey(object)) {
      return (String)m_annotationMap.get(object);
    } // end of if ()
    else {
      m_symbolMap.put(rootName, object);
      m_annotationMap.put(object, rootName);
      return rootName;
    }
  }

  public Object lookupSymbol(String symbol)
  {
    return m_symbolMap.get(symbol); //.toUpperCase());
  }

  ////////////////////////////////////////////////////////////////////
  // fetch info from the DB and construct causal chains etc...

  /** @return a set of all the events (of various types) **/
  public Set getEvents()
  {
    HashSet events = new HashSet();
    Iterator it = Arrays.asList( Type.EVENT ).iterator();
    while( it.hasNext() )
      events.addAll( m_tms.fetchInFacts( (String)it.next() ) );
    return events;
  }

  public CausalStructure getCausalStructure() 
  {
    return m_causes;
  }

  public LispTMS getJTRE() 
  {
    return m_tms;
  }

  public LispTMS getTMS()
  {
    return m_tms;
  }
  
  public Body findBody( String name ) 
  {
    //    List bodies = m_tms.fetchInFacts( Type.BODY );
    LispObj obj = m_tms.readEval("(find-body-by-name \"" + name + "\")");
    
    String id = obj.getStringValue();

    return (Body)lookupSymbol(id);

//      Iterator it = bodies.iterator();
//      while( it.hasNext() ) {
//        Body body = (Body)it.next();
//        if( body.getName().equals( name ) ) {
//  	return body;
//        }
//      }
//      return null;
  }

  public boolean isGravityOn() 
  {
    return ((SimulateSurface)m_surface).isGravityOn();
  }

  /**
   * @return Signed number of annotations between these annotations;
   *         result is positive if a is before b and negative if b is
   *         before a.
   **/
  public int numAnnotations( Annotation a, Annotation b )
  {
    Class cls = ClassFinder.find( "rec.annotate.facts.Annotation" );
    return numBetween( a, b, cls );      
  }

  /**
   * @return Signed number of sentence between these annotations;
   *         result is positive if a is before b and negative if b is
   *         before a.
   **/
  public int numSentences( Annotation a, Annotation b )
  {
    Class cls = ClassFinder.find( "rec.annotate.facts.Sentence" );
    return numBetween( a, b, cls );
  }
  
  /**
   * @return the number of currently believed annotations between a
   * and b.  If b is before a then the number will be negative.  It
   * they are the same fact then it returns 0.
   **/
  private int numBetween( Annotation a, Annotation b,
			  Class type )
  {
    DebugWindow.addLine("Lisp", "Not Implimented: numBetween");

    if( a.sameFact( b ) )
      return 0;

    int direction = 1;
    
    // swap them if they are backwards
    if( a.getStartTime() > b.getStartTime() ) {
      Annotation temp = a;
      a = b;
      b = temp;
      direction = -1;
    }
    
    // do they overlap?
    if( a.getEndTime() > b.getStartTime() ) {
      return 0;
    }

    // widgets is in reverse chronological order
    LinkedList widgets = m_surface.getTimeOrderedElectedWidgets();
    Iterator it = widgets.iterator();

    // count the number of intervening widgets.
    int count = 0;

    while( it.hasNext() ) {
      Widget w = (Widget)it.next();
      // we have checked the interesting time period
      if( w.getTime() <= a.getEndTime() ) {
	return direction * count;
      }

      // check if between a and b
      if( (w.getTime() < b.getStartTime() ) &&
	  type.isInstance( w )
	  //	  && m_tms.isIn( ((WAnnotation)w).getAnnotationFact() )
	  )
      {
	count++;
      }
    }
    return direction * count;
  }

  public void shutdown() 
  {
    m_tms.shutdown();
  }

}
