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

// $Id: TalkAboutCausality.java,v 1.4 2003/03/06 00:40:49 moltmans Exp $
// TalkAboutCausality.java -- 
// Author: Michael Oltmans <moltmans@ai.mit.edu> 
// Created: <Thu Jul 13 22:50:08 2000> 
// Time-stamp: <2000-07-20 18:08:18 moltmans> 
package rec.annotate.answers;

import java.util.*;
import rec.sys.*;
import rec.util.*;
import rec.speech.*;
import rec.annotate.widgets.*;
import rec.annotate.facts.*;
import rec.annotate.rules.*;

/**
 **/
public class TalkAboutCausality
{
  private Notebook m_notes;

  public TalkAboutCausality( Notebook notes )
  {
    m_notes = notes;
  }

  /** @return true if any of options are in container **/
  private static boolean containsAny( Collection options,
				      Collection container )
  {
    Iterator items = options.iterator();
    while( items.hasNext() ) {
      Object item = items.next();
      if( container.contains( item ) ) {
	return true;
      }
    }
    return false;
  }
  
  // The real problem is the coincedence of causality and sub events
  // we want to have it say the cause and effect of an event but not
  // say: "when a moves b moves" and "a pushes b" but we want to make
  // sure to describe the causes and effects of both of these.

  // is this an issue of setting the correct causal links or is it
  // really a description problem

  // want to make sure that we only use sub events if the super events
  // do not include the causes and effects

  // if event is sub event find the cause/effect of super event check
  // if there is something in the cause/effect of the sub event that
  // hasn't been covered yet.

  public void answer( Collection events )
  {
    LinkedList list = new LinkedList();

    // this is so that we go through parent events before child events
    Iterator it = events.iterator();
    while( it.hasNext() ) {
      CanonicalEvent event = (CanonicalEvent)it.next();
      // this is a sub event so use the parents instead
      if( event.isSubEvent() ) {
	list.addAll( 0, event.getParentEvents() );
      }
      // add this regardless, this helps make sure that we get all the
      // causes and effects mentioned
      list.addLast( event );
    }

    List answers = new LinkedList();
    // now go through the list and find causes and effects for each
    // event and keep track of which ones we've talked about...
    HashSet mentioned = new HashSet();
    it = list.iterator();
    while( it.hasNext() ) {
      CanonicalEvent event = (CanonicalEvent)it.next();
      String answer = answer( event, mentioned );
      if( answer.length() > 0 ) {
	answers.add( answer );
      }
    }

    it = answers.iterator();
    while( it.hasNext() ) {
      Say.say( (String)it.next() );
      if( it.hasNext() ) {
	Say.say( "also" );
      }
    }
  }
  

//      Set surrounding = new HashSet( events );
//      Iterator it = events.iterator();

//      // combine the sets of events and eliminate the sub events if
//      // all of the sub events and the super event is mentioned
//      while( it.hasNext() ) {
//        CanonicalEvent event = (CanonicalEvent)it.next();
//        surrounding.addAll( event.getCauses() );
//        surrounding.addAll( event.getEffects() );
//      }

//      Collection noSubs = eliminateSubEvents( surrounding );
//      //    Collection set = new HashSet( events );
    
//      it = events.iterator();
//      while( it.hasNext() ) {
//        CanonicalEvent event = (CanonicalEvent)it.next();
//        answer( event, noSubs );
//        if( it.hasNext() ) {
//  	Say.say( " also " );
//        }
//      }
//    }    

  /**
   * Talk a bit about the causes and effects of this event.
   *
   * @param event the event to talk about
   * @param mentioned a collection of events that we have already
   *                  talked about and don't need to be included.
   *                  This set is updated with events mentioned here
   **/
  public String answer( CanonicalEvent event, Set mentioned )
  {
    Set causes = new HashSet( event.getCauses() );
    Set effects = new HashSet( event.getEffects() );

    causes.removeAll( mentioned );
    effects.removeAll( mentioned );

    StringBuffer result = new StringBuffer();

    // no causes or effects or event
    if( causes.isEmpty() && effects.isEmpty() &&
	mentioned.contains( event ) )
    {
      return "";
    }
    // no causes or effects
    if( causes.isEmpty() && effects.isEmpty() ) {
      result.append( event.toEnglish() );
      mentioned.add( event );
      mentioned.addAll( event.getSubEvents() );
    }
    // some of each
    else if( !causes.isEmpty() && !effects.isEmpty() ) {
      result.append( "When " + listEvents( causes, mentioned ) +
		     " then " );
      result.append( event.toEnglish() );
      result.append( " and in turn " +
		     listEvents( effects, mentioned ) );
      mentioned.add( event );
      mentioned.addAll( event.getSubEvents() );
    }
    // just effects
    else if( !effects.isEmpty() ) {
      result.append( "When " + event.toEnglish() + " then " );
      result.append( listEvents( effects, mentioned ) );
      mentioned.add( event );
      mentioned.addAll( event.getSubEvents() );
    }
    // just causes
    else {
      result.append( "When " + listEvents( causes, mentioned ) +
		     " then " );
      result.append( event.toEnglish() );
      mentioned.add( event );
      mentioned.addAll( event.getSubEvents() );
    }
  
    return result.toString();
  }

  private String listEvents( Collection events, Set mentioned )
  {
    // set of elements we already mentioned here.
    HashSet set = new HashSet();
    // list of things to generate stuff for
    LinkedList list = new LinkedList();
    Iterator it = events.iterator();
    while( it.hasNext() ) {
      CanonicalEvent event = (CanonicalEvent)it.next();
      if( event.isSubEvent() ) {
	Set parents = new HashSet( event.getParentEvents() );
	parents.removeAll( set );

	Iterator elders = parents.iterator();
	while( elders.hasNext() ) {
	  CanonicalEvent parent = (CanonicalEvent)elders.next();
	  list.addLast( parent );
	  set.add( parent );
	  set.addAll( parent.getSubEvents() );
	}
      }
      else {
	list.addLast( event );
	set.add( event );
	set.addAll( event.getSubEvents() );
      }
    }

    // update the main list of things we've talked about
    mentioned.addAll( set );

    StringBuffer result = new StringBuffer();
    while( !list.isEmpty() ) {
      CanonicalEvent event = (CanonicalEvent)list.removeFirst();
      // one last check that we aren't repeating ourselves...
      if( !list.contains( event ) ) {
	result.append( event.toEnglish() );    
	if( !list.isEmpty() ) {
	  result.append( " and " );
	}
      }
    }
    
    return result.toString();
  }

  /**
   * We don't want to mention an event and its sub events.  So if
   * there is an event and all of its sub events then we remove the
   * sub events from the collection.
   **/
  private Collection eliminateSubEvents( Collection events ) 
  {
    // list of kids that can be removed
    Collection results = new HashSet( events );

    Iterator it = events.iterator();
    while( it.hasNext() ) {
      CanonicalEvent event = (CanonicalEvent)it.next();
      Collection subEvents = event.getSubEvents();
      // this may or may not work as expected if there are sub sub
      // events...
      if( !subEvents.isEmpty() && events.containsAll( subEvents ) ) {
	results.removeAll( subEvents );
      }
    }
    return results;
  }
}
