MusicUIAgentImpl.java  
/**
 ** This class creates a user interface for MusicPlayingAgents.
 ** It supports the standard play/stop/pause/etc commands
 **/

package net.hivecell.hive.agent.desktop;

import net.hivecell.hive.agent.Agent; 
import net.hivecell.hive.agent.AgentImpl;
import net.hivecell.hive.agent.AgentInitializationException;
import net.hivecell.hive.agent.EventReceivingAgent;
import net.hivecell.hive.agent.EventSendingAgentImpl; 
import net.hivecell.hive.agent.cell.AgentMonitoringAgent;
import net.hivecell.hive.shadow.ShadowNotFoundException;
import net.hivecell.hive.shadow.cell.ComponentManagerShadow;
import net.hivecell.hive.support.IconLoader;
import net.hivecell.hive.support.CellAddress;
import net.hivecell.hive.support.Debug;
import net.hivecell.hive.support.PPM;
import net.hivecell.hive.Global;

import java.util.*;
import java.io.InputStream;
import java.io.IOException;
import java.rmi.RemoteException;

import java.awt.event.*;
import java.awt.*;
import java.awt.Image;
import java.awt.image.ImageObserver;
import java.awt.FlowLayout;
import java.awt.Rectangle;


import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.Color;



public class MusicUIAgentImpl 
extends EventSendingAgentImpl 
implements MouseListener {

    transient protected ComponentManagerShadow acms;

    // because the mobile agent output stream is serializing the MPA
    // if we hold a real reference to it, this is a work around that
    // will stay in place until java 2
    protected MusicPlayingAgent ma;
 
    Vector playList = new Vector();
    int currentSong = 0;

    // GUI stuff
    transient protected Frame frame;     // the frame that we are putting everything into
    transient protected HiveAMPCanvas panel;     // the master level panel
    transient protected Graphics panelG;  // Graphics to allow us to draw onto the panel

    protected PPM skin;
    
    protected Hashtable buttons = new Hashtable();  // Hash of the button rectangles.
    //    protected Rectangle rect;  // Bounding rectangle of the buttons.

    public MusicUIAgentImpl() throws RemoteException {
        super();
    }

    public void setMusicPlayingAgent( MusicPlayingAgent ma ) { this.ma = ma; }
    
    public void doLocalSetup() throws AgentInitializationException  {
    super.doLocalSetup();

        try {
            InputStream is = getClass().getResourceAsStream("HiveAMP.ppm");
        skin = (new PPM(is));
        } catch (IOException e) {
        throw new AgentInitializationException("No skin available for HiveAMP!");
    }

    // get our handle to our agent component manager shadow
    try {
        acms = getComponentManagerShadow();
    } catch( ShadowNotFoundException error ) {
        throw new AgentInitializationException("this agent cannot start on a cell that does not have graphical capabilities.");
    }

    // Hard code the button placement for now.  (I realize this will never be made configurable :)
    buttons.put("rrwd", new Rectangle(101, 53, 22, 18));
    buttons.put("stop", new Rectangle(123, 53, 21, 18));
    buttons.put("play", new Rectangle(145, 53, 21, 18));
    buttons.put("ffwd", new Rectangle(167, 53, 21, 18));

    //        skin = new JLabel(new ImageIcon(skinBytes));
            
    panel = new HiveAMPCanvas();
        panel.setSkinImage(skin.getImage());
    panel.addMouseListener(this);

    frame = (Frame)acms.acceptComponentFrom( this, panel, "HiveAMP");
    }

    public void addListener( EventReceivingAgent subscriber ) {
    super.addListener(subscriber);

    if (subscriber instanceof MusicPlayingAgent) {
            setupMusicPlayingAgent((MusicPlayingAgent)subscriber);
    }
    else
        Debug.warning("Agent '" + subscriber + "' is not of type MusicPlayingAgent.");
    }


    /** Set up a connection to the given music playing agent.
     ** Set the ma variable, grab the song list.
     **/
    protected void setupMusicPlayingAgent(MusicPlayingAgent ma) {
        this.ma = ma;
        myCell.noteAgentMessage(this, "getSongList", this, ma);
        try {
            playList = ma.getSongList();
            myCell.report(this, "Found " + playList.size() + " songs.");
        } catch (Exception ex) {
            Debug.warning(ex);
            myCell.report(this, "Couldn't load song list.");
        }
    }
    
    public void disconnectFrom() 
    throws RemoteException {
    ma = null;
    playList = null;
    }

    public void doBehavior() { 
    if( this.ma != null )
        addListener( (EventReceivingAgent)this.ma );


    /* NASTY HACK:
    while (!timeToStop) {
        displaySongTitle(songTitle);
        try {
        Thread.sleep(100);
        } catch (InterruptedException e) {}

    }
    */

    /* Uncomment this if you need to re-do the placement of the buttons.
    while (!timeToStop) {

        Enumeration en = buttons.keys();
        while (en.hasMoreElements()) {
        Rectangle rect = (Rectangle)buttons.get(en.nextElement());
        panelG.setColor(Color.green);
        panelG.fillRect(rect.x, rect.y, rect.width, rect.height);
        }

        try {
        Thread.sleep(500);
        } catch (InterruptedException e) {}
    }
    */
    waitUntilDeath();
    }

    public void doLocalCleanup() { 

    super.doLocalCleanup();
    frame.dispose();
    }


    // When the user lets go of the mouse, then we handle the drag
    public void mouseReleased(MouseEvent event) {
    
    try {
        if ( ((Rectangle)buttons.get("stop")).contains(event.getX(), event.getY()) ) {
        if( ma != null ) {
            myCell.noteAgentMessage(this, "stop", this, ma);
            ma.stop();
                    panel.displaySongTitle("<stopped>");
        }
        }
        
        else if ( ((Rectangle)buttons.get("play")).contains(event.getX(), event.getY()) ) {

        Object song = getCurrentSong();
        if( ma != null && song != null ) {
            myCell.noteAgentMessage(this, "play", this, ma);

            panel.displaySongTitle(ma.getSongTitle( song ));

            ma.play( song );
        }
        }
        
        else if ( ((Rectangle)buttons.get("ffwd")).contains(event.getX(), event.getY()) ) {
        Object curSong = getCurrentSong();
        Object song = getNextSong();
        
        if( ma != null && song != null && song != curSong ) {
            myCell.noteAgentMessage(this, "stop", this, ma);
            ma.stop();
            myCell.noteAgentMessage(this, "play", this, ma);

            panel.displaySongTitle(ma.getSongTitle( song ));

            ma.play( song );
        }
        }
        
        else if ( ((Rectangle)buttons.get("rrwd")).contains(event.getX(), event.getY()) ) {
        Object curSong = getCurrentSong();
        Object song = getPrevSong();
        
        if( ma != null && song != null && song != curSong ) {
            myCell.noteAgentMessage(this, "stop", this, ma);
            ma.stop();
            myCell.noteAgentMessage(this, "play", this, ma);

            panel.displaySongTitle(ma.getSongTitle( song ));

            ma.play( song );
        }
        }

        else {
                Debug.warning("Mouse click where we didn't expect it.");
        }

    } catch( RemoteException error ) {
        Debug.error( "remote exception while trying to send commands to the music agent", error );      
    }
    }


    /** We don't care about these events */
    public void mouseClicked(MouseEvent event) {}
    public void mousePressed(MouseEvent event) {}
    public void mouseEntered(MouseEvent event) {}
    public void mouseDragged(MouseEvent event) {}
    public void mouseExited(MouseEvent event) {}





    public Object getNextSong() {
    if (playList.size() == 0) return null;

    if (currentSong + 1 < playList.size())
        return (playList.elementAt(++currentSong));
    return (playList.elementAt(currentSong));
    }

    public Object getPrevSong() {
    if (playList.size() == 0) return null;

    if (currentSong > 0)
        return (playList.elementAt(--currentSong));
    return (playList.elementAt(currentSong));
    }

    public Object getCurrentSong() {
    if (playList.size() == 0) return null;

    if (currentSong >= 0 && currentSong < playList.size())
        return (playList.elementAt(currentSong));
    return (null);
    }



}

class HiveAMPCanvas extends Canvas implements ImageObserver {
    Image skin;
    Rectangle titleClip = new Rectangle(103, 15, 84, 10);
    String currentTitle = "HiveAMP";

    public void setSkinImage(Image skin) {
        this.skin = skin;
    }
    
    public HiveAMPCanvas() {
    setBackground(Color.white);
        setSize(220, 139);
        setVisible(true);
    }
                
    public void paint(Graphics g) {
        if (g == null)
            Debug.notice("Null graphics, skipping");
        else {
            g.drawImage(skin, 0, 0, this);
            displaySongTitle(currentTitle);
        }
    }

    public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) {
        if( ( infoflags & ImageObserver.ALLBITS ) != 0 ) {
            paint(this.getGraphics());
            return false;
        }
        return true;
    }

    public void displaySongTitle(String title) {
        if (title == null || title.equals(""))
            return;

        currentTitle = title;

        Graphics panelG = this.getGraphics();
    String songTitle = title;

        panelG.setClip(titleClip);
    panelG.setColor(Color.white);
    panelG.fillRect(titleClip.x, titleClip.y, titleClip.width, titleClip.height);
    panelG.setColor(Color.black);
        panelG.setFont(Global.defaultFont);

    String croppedSongTitle = songTitle;
        // COMMENTED OUT
    if (false && songTitle.length() > 14) {
        croppedSongTitle = songTitle.substring(0, 10) + "...";
    }

        // panelG.setColor(Color.green);
        // panelG.fillRect(0, 0, 500, 500);
    panelG.setColor(Color.black);
    panelG.drawString(croppedSongTitle, 103, 24);
    }

}