// $Id: SpeechLabel.java,v 1.2 2003/12/04 20:04:24 cadlerun Exp $
// SpeechLabel.java -- 
// Author: Aaron Adler <cadlerun@fracas.ai.mit.edu> 
// Copyright: Copyright (C) 2002 by Aaron Adler 
// Created: <Thu Dec  4 14:06:48 2003> 

package rec.ui;

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.MouseEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseListener;
import java.util.Timer;
import java.util.TimerTask;
import java.util.ArrayList;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.event.MouseInputAdapter;
import javax.swing.event.PopupMenuListener;
import javax.swing.event.PopupMenuEvent;
import rec.util.ClassFinder;

/**
 * This class is a special kind of label that has a pop down menu
 * containing the recent speech phrases. It also indicates a new
 * speech phrase by animating its label.
 *
 */
public class SpeechLabel extends JLabel {

  private JPopupMenu m_pop; // the popup menu
  private ArrayList m_recentSpeech; // recent speech phrases
  private String m_arcState = ""; // the state of the arcs in the icon
  private boolean m_popupOpen = false; // is the popup window open
  private AnimateSpeechTask m_speechTask; // animation task
  
  private final int RECENT_SIZE = 5; // how many items to keep in the recent menu
 
  public SpeechLabel() {
    // set up the label
    super();
    setPreferredSize(new Dimension(50,36));
    setToolTipText("Show recent speech utterances");

    m_recentSpeech = new ArrayList();

    // create the popup menu
    m_pop = new JPopupMenu();

    // this listener updates the icon to reflect that the popup is open or closed
    m_pop.addPopupMenuListener(new PopupMenuListener() {
	public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
	  m_popupOpen = true;
	  updateSpeechIcon();
	  // popup open 
	}
	public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
	  m_popupOpen = false;
	  updateSpeechIcon();
	  // popup closed
	}
	public void popupMenuCanceled(PopupMenuEvent e) {}
      } );

    // put "(none)" in the menu to start
    JMenuItem item = new JMenuItem("(none)");
    item.setEnabled(false);
    m_pop.insert(item, 0);

    // display the correct icon
    updateSpeechIcon();

    // add the mouse popup listener
    MouseListener popupListener = new PopupListener(m_pop);
    addMouseListener(popupListener);

    // add the listener for to display the popup menu at the correct
    // location
    final SpeechLabel myself = this;
    addMouseListener( new MouseInputAdapter() {
      public void mousePressed( MouseEvent e )
	{
	    m_pop.show(myself, e.getX(), e.getY());
	}
    } );
  }

  
  //////////////////////////////////////////

  /**
   * Receives new speech strings. Alerts the user and allows them to
   * see the text. If speech is not activated, does nothing.
   */
  public synchronized void addSpeech(String speech) {
    // first check to see if this the first speech event so we can
    // remove "(none)" from the menu
    if (m_recentSpeech.size() == 0) {
      try {
	m_pop.remove(0);
      }
      catch (IllegalArgumentException e) {
	// tried to remove menu item that wasn't there
      }
    }
      
    // add speech string to appropriate structure
    speech = removePauses(speech);
    m_recentSpeech.add(speech);

    // update the menu
    JMenuItem item = new JMenuItem(speech);
    item.setEnabled(false);
    m_pop.insert(item, 0);

    // only keep the most recent RECENT_SIZE speech phrases
    if (m_recentSpeech.size() > RECENT_SIZE) {
      m_recentSpeech.remove(0);
      try {
	m_pop.remove(RECENT_SIZE - 1);
      }
      catch (IllegalArgumentException e) {
	// tried to remove menu item that wasn't there
      }
    }

    // alert user via animated speech icon
    Timer timer = new Timer();
    m_speechTask = new AnimateSpeechTask();
    timer.scheduleAtFixedRate(m_speechTask,0,500);
    
  }


  ///////////////////////////////////////
  
  /**
   * updates actual image based on m_arcState and m_popupOpen to
   * account for arc state and opened/close state of the pulldown menu
   */
  private synchronized void updateSpeechIcon() {
    // update the arc display
    String imgName = "audio"+m_arcState;
    if (m_popupOpen) imgName = imgName+"-open";
    imgName = imgName+".png";

    ImageIcon audio = ClassFinder.getIcon(imgName);
    setIcon(audio);
  }

  /**
   * gets next string for audio.  strings are to transition audio
   * graphic where 1 2 and 3 represent the active arcs
   */
  private synchronized String getNextArc(String curArc, boolean newArc) {
    // the next arc depends on whether there is a new arc or not
    String newStr = "";
    
    if (curArc.equals(""))    newStr = "";
    if (curArc.equals("1"))   newStr = "2";
    if (curArc.equals("2"))   newStr = "3";
    if (curArc.equals("3"))   newStr = "";

    // start at 1 if there is a new arc
    if (newArc) newStr = "1";

    return newStr;
  }

  /**
   * remove <pause1> and <pause2> from string
   */
  private String removePauses(String s) {
    String tmp = s;   
    int index = tmp.indexOf("<pause");
    
    while (index != -1) {
      tmp = tmp.substring(0,index) + tmp.substring(index+8);
      index = tmp.indexOf("<pause");
    }

    return tmp;
  }

  
  ///////////////////////////
  
  // listener for the popup menu
  class PopupListener extends MouseAdapter {
    JPopupMenu popup;

    PopupListener(JPopupMenu popupMenu) {
      popup = popupMenu;
    }

    public void MousePressed(MouseEvent e) {
      maybeShowPopup(e);
    }

    public void MouseReleased(MouseEvent e) {
      maybeShowPopup(e);
    }

    private void maybeShowPopup(MouseEvent e) {
      if (e.isPopupTrigger()) {
	popup.show(e.getComponent(), e.getX(), e.getY());
      }
    }
  }
  
  /**
   * This version of TimerTask just ensures that the the task is 
   * executed on the event dispatching thread.  Subclasses must
   * override doRun() instead of run().
   */
  private class AnimateSpeechTask extends TimerTask {
    public AnimateSpeechTask() 
    {
      if( m_speechTask != null ) {
        m_speechTask.cancel();
      }

      m_arcState = getNextArc(m_arcState, true);
    }
    
    
    public void doRun() 
    {
      // update the arc display
      updateSpeechIcon();

      // see if we are done
      if (m_arcState == "") {
	if (m_speechTask != null) {
	  m_speechTask.cancel();
	  m_speechTask = null;
	}
      }
      else {
	// not done, update the arc state;
	m_arcState = getNextArc(m_arcState, false);
      }      
    }
    
    public void run() {
      if (!EventQueue.isDispatchThread()) {
        EventQueue.invokeLater(this);
      } else {
        doRun();
      }
    }
  }
  
}

