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

// $Id: DynamicWatchController.java,v 1.2 2003/12/04 20:01:37 cadlerun Exp $
// WatchController.java -- 
// Author: Aaron Adler <cadlerun@fracas.ai.mit.edu> 
// Copyright:  Copyright (C) 2003 by MIT.  All rights reserved.
// Created: <Thu Oct  3 22:56:00 2002> 

package rec.multi;

import javax.swing.JFrame;
import javax.swing.JTabbedPane;
import java.awt.Point;
import java.awt.Container;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.BorderLayout;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import edu.mit.tools.watch.display.DataLabels;
import edu.mit.tools.watch.display.TimelineDisplay;
import edu.mit.tools.watch.dataExtraction.ExtractorException;
import edu.mit.tools.watch.timeline.TimelineData;
import edu.mit.tools.watch.Logging;
import edu.mit.tools.watch.Utils;

import java.util.Iterator;

import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
import java.net.URL;

import rec.sys.Widget;

/***
 * This class acts as a controller for the application. It takes
 * messages from all the parts and does the appropriate thing.
 */

public class DynamicWatchController {

  private static final Logger LOG = Logging.DYNAMIC_WC;
  
  private final int M_WATCH_TIME_UNITS_PER_SECOND = 30;
  
  private DataLabels m_dl;
  private DynamicTimelineMenu m_menu;

  private JFrame m_frame;
  private JTabbedPane m_tabPane;
  private Container m_contentPane;
  private Container m_groupPane;
  private DynamicDrawTimeline m_drawTimeline;
  private GroupListDraw m_drawGL;

  private final String m_WINDOW_NAME = "WATCH";

  private long m_timeAdjustment = 0;
  private DynamicExtractor m_extractor;

  private SpeechAnalyzer m_speechAnalyzer;
  
  private boolean m_debugOutput = false;

  public DynamicWatchController (boolean debugOutput) {
    m_debugOutput = debugOutput;
    
    // start the logging
    URL logConfig = Logging.class.getResource("my-logger.lcf");
    if( logConfig == null ) {
      BasicConfigurator.configure();
      Logger.getRootLogger().setLevel( Level.WARN );
    }
    else {
      PropertyConfigurator.configure( logConfig );
    }

    LOG.debug("Starting instance of dynamic watch controller");
    initialize();
  }

  public void initialize() {

    if (m_debugOutput) {
      // only set up the window if we are displaying it!!!
      
      // set up the frame
      m_frame  = new JFrame(m_WINDOW_NAME);
      m_frame.addWindowListener(new WindowAdapter() {
	  public void windowClosing(WindowEvent e) {quitTimeline();}
	});
      
      // DataLabels
      m_dl = new DataLabels();

      // set up the timeline menu
      LOG.debug("Set up the timeline menu");
      m_menu = new DynamicTimelineMenu(this);
      
      LOG.debug("Set up the content frame");
      m_contentPane = new Container();
      m_contentPane.setLayout(new BorderLayout());
      m_contentPane.add(m_menu, BorderLayout.NORTH);
      
      m_groupPane = new Container();
      m_groupPane.setLayout(new BorderLayout());
      
      m_tabPane = new JTabbedPane();
      m_frame.setContentPane(m_tabPane);
      m_tabPane.add("Timeline", m_contentPane);
      m_tabPane.add("Groups", m_groupPane);

      m_frame.pack();
      LOG.debug("show the window");
      m_frame.setVisible(true);
    }
    
    LOG.debug("set up the dynamic extractor");
    m_extractor = new DynamicExtractor(m_debugOutput);
    try {
      m_extractor.loadRules();
    } catch (ExtractorException e) {
      LOG.error("Error loading the rule system: "+e.toString());
      if (m_debugOutput)
	Utils.errorMsg(m_frame, "Error loading rules in rule system.", e.toString());
    }

    // set up the speech analyzer
    m_speechAnalyzer = new SpeechAnalyzer(this);
  }

  public void quitTimeline() {
    // this function quits. This central location allows us to save
    // any settings if we need to.

    // just exit for now
    System.exit(0);
  }

  public void aboutTimeline() {
    // this function displays a dialog box with info about the program.
    LOG.info("showing info about WATCH");
    Utils.aboutMsg(m_frame);
  }

  public void addSketchEvent(long startT, long endT, String objType, String actionType, String text, int objID, Widget topWidget)
  {
    // Add a sketch event to the timeline and the speech analyzer
    
    LOG.debug("Adding sketch event "+startT+" to "+endT+" type "+objType+" action "+actionType+" text "+text+" objID "+objID);
    try {
      int adjStart = adjustTime(startT);
      int adjEnd = adjustTime(endT);
      m_extractor.addSketchEvent(adjStart,adjEnd, objType, actionType, text, objID);

      // add the widget to the speech analyzer
      m_speechAnalyzer.addWidget(adjStart, adjEnd, topWidget);

      runUpdates();
    } catch (ExtractorException e) {
      LOG.error("Error adding sketch event: "+e.toString());
      if (m_debugOutput)
	Utils.errorMsg(m_frame, "Error running rule system in add sketch event.", e.toString());
    }
  }
  
  public void addSpeechEvent(long startT, long endT, String text)
  {
    // Add a speech event to the timeline and the speech analyzer
    
    LOG.debug("Adding speech event "+startT+" to "+endT+" text "+text);
    try {
      m_extractor.addSpeechEvent(adjustTime(startT),adjustTime(endT), text);

      m_speechAnalyzer.addSpeechEvent(adjustTime(startT),adjustTime(endT), text);
      
      runUpdates();
    } catch (ExtractorException e) {
      LOG.error("Error adding speech event: "+e.toString());      
      if (m_debugOutput)
	Utils.errorMsg(m_frame, "Error running rule system in add speech event.", e.toString());
    }
  }

  public void removeSketchEvent(long startT, long endT, String objType, String actionType, String text)
  {
    LOG.debug("Removing sketch event "+startT+" to "+endT+" type "+objType+" action "+actionType+" text "+text);
    try {
      m_extractor.removeSketchEvent(adjustTime(startT),adjustTime(endT), objType, actionType, text);
      runUpdates();
    } catch (ExtractorException e) {
      LOG.error("Error removing sketch event: "+e.toString());      
      if (m_debugOutput)
	Utils.errorMsg(m_frame, "Error running rule system in remove sketch event.", e.toString());
    }
  }
  
  public void removeSpeechEvent(long startT, long endT, String text)
  {
    LOG.debug("Removing speech event "+startT+" to "+endT+" text "+text);      
    try {
      m_extractor.removeSpeechEvent(adjustTime(startT),adjustTime(endT), text);
      runUpdates();
    } catch (ExtractorException e) {
      LOG.error("Error removing speech event: "+e.toString());            
      if (m_debugOutput)
	Utils.errorMsg(m_frame, "Error running rule system in remove speech event.", e.toString());
    }
  }

  private int adjustTime(long time)
  {
    // adjust time to zero for the first event recieved.
    if (m_timeAdjustment == 0) {
      // time hasn't been adjusted yet
      m_timeAdjustment = time - 34; // adjust so doesn't start at 0, avoids some problems
    }

    int timeInMillis = (int)(time - m_timeAdjustment);

    // now we need to divide to get the time in some time interval
    return (timeInMillis / (1000 / M_WATCH_TIME_UNITS_PER_SECOND));
  }

  private void runUpdates()
  {
    runSpeech();
    if (m_debugOutput)
      updateDisplay();
  }
  
  private void runSpeech()
  {
    LOG.debug("running speech");
    
    Iterator results = m_extractor.getResults();
    
    // now create a data timeline
    TimelineData data = new TimelineData(results);

    //set up the EventGroup
    EventGroup eventGroup = new EventGroup(data.getTimelineData());

    // analyze the speech if needed
    if (m_speechAnalyzer.hasSpeechToAnalyze()) {
      m_speechAnalyzer.analyze(eventGroup);
    }
  }
  
  private void updateDisplay()
  {
    // update the display
    
    LOG.debug("updating display");
    
    Iterator results = m_extractor.getResults();
    
    // now create a data timeline
    TimelineData data = new TimelineData(results);
    Iterator dataE = data.getTimelineData();

    // now set up the display data
    TimelineDisplay disp = new TimelineDisplay();
    disp.loadTimelineData(dataE, m_dl);

    //set up the EventGroup
    EventGroup eventGroup = new EventGroup(data.getTimelineData());

    // set up the group list draw
    GroupListDraw drawGL = new GroupListDraw(eventGroup.iterator(), m_speechAnalyzer);

    // set up the timeline frame
    DynamicDrawTimeline drawTimeline = new DynamicDrawTimeline(disp);
    drawTimeline.setBackground(Color.white);

    if (m_drawTimeline != null)
    {
      LOG.debug("replacing old content");
      
      m_contentPane.remove(m_drawTimeline);

      m_groupPane.remove(m_drawGL);

      // set the view to be the same as it was before
      Point oldView = m_drawTimeline.getViewCorner();
      drawTimeline.setViewCorner(oldView);

      // set the window size to be the same as it was before
      Dimension oldSize = m_drawTimeline.getSize();
      drawTimeline.setSize(oldSize);
    }
    
    m_contentPane.add(drawTimeline, BorderLayout.CENTER);
    m_drawTimeline = drawTimeline;
      
    m_groupPane.add(drawGL);
    m_drawGL = drawGL;

    LOG.debug("showing new window");
    m_frame.setTitle(m_WINDOW_NAME+" -- Dynamic Input");
    m_frame.pack();
  }

}

