/*
 * @(#)PlayerCanvas.java	1.5 04/03/10
 *
 * Copyright (c) 2000-2004 Sun Microsystems, Inc. All rights reserved.
 * PROPRIETARY/CONFIDENTIAL
 * Use is subject to license terms
 */

package example.audiodemo;

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import javax.microedition.rms.*;
import javax.microedition.media.*;
import javax.microedition.media.control.*;

import java.io.*;

/**
 * The component for AudioPlayer.
 * It will create a player for the selected url, play and display
 * some properties of the player.
 *
 * Use star key to increase the volume, pound key to decrease the volume
 *
 **/
public class PlayerCanvas extends Canvas implements Runnable, CommandListener {

    private static final String RECORD_STORE_NAME = "adrms";
    private Player player;
    private Thread dThread;
    private Object dThreadLock = new Object();
    private Object pauseLock = new Object();
    private boolean interrupted, paused;

    private Image logo = null;

    private Display parentDisplay;
    private Command backCommand  = new Command("Back",
					       Command.BACK, 1);
    private Command playCommand  = new Command("Play",
					       Command.ITEM, 1);
    private Command pauseCommand = new Command("Pause",
					       Command.ITEM, 10);
    private String title, url;
    private String mtime;


    public PlayerCanvas(Display parentDisplay) {
	super();
	this.parentDisplay = parentDisplay;
	initialize();
    }

    private void initialize() {
	this.addCommand(backCommand);
	this.addCommand(pauseCommand);

	setCommandListener(this);
	try {
	    logo = Image.createImage("/icons/logo.png");
	} catch (Exception ex) {
	    logo = null;
	}
	if (logo == null)
	    System.out.println("can not load logo.png");

    }

    /*
     * simple implementation, not reflected actual state
     * of player.
     */
    public void commandAction(Command c, Displayable s) {
	if (s == this) {
	    if (c == backCommand) {
		stopSound();
		removeCommand(playCommand);
		addCommand(pauseCommand);
		parentDisplay.setCurrent(AudioPlayer.getList());
	    } else if (c == playCommand) {
		playSound();
		removeCommand(playCommand);
		addCommand(pauseCommand);
	    } else if (c == pauseCommand) {
		pauseSound();
		removeCommand(pauseCommand);
		addCommand(playCommand);
	    }
	}
    }

    public void setParam(String url) {
	this.url = url;
	int idx = url.lastIndexOf('/');
	title = url.substring(idx+1);
    }

    public void playSound() {
	if (title == null || url == null) {
	    return;
	}

	// player was paused
	if (player != null) {

	    // wake up paused thread
	    synchronized(pauseLock) {
		paused = false;
		pauseLock.notify();
	    }

	    try {
		player.start();
	    } catch (MediaException me) {
		me.printStackTrace();
	    }
	    return;
	}

	// start new player
	synchronized(dThreadLock) {
	    stopSound();
	    interrupted = false;
	    paused = false;
	    mtime = "";
	    dThread = new Thread(this);
	    dThread.start();
	}
    }

    public void stopSound() {
	synchronized(dThreadLock) {
	    try {
		interrupted = true;

		// wake up thread if it is paused
		synchronized(pauseLock) {
		    pauseLock.notify();
		}

		if (dThread != null) {
		    dThreadLock.wait();
		}
	    } catch (InterruptedException ie) {
		// nothing
	    }
	}
    }

    void pauseSound() {
	try {
	    if (player != null) {
		// pause player
		player.stop();
		paused = true;
	    }
	} catch (MediaException ex) {
	    ex.printStackTrace();
	}
    }

    public boolean isPlaying() {
	return (player!= null) && (player.getState() >= Player.STARTED);
    }

    private static String guessContentType(String url) throws Exception {
	String ctype;
	// some simple test for the content type
	if (url.endsWith("wav")) {
	    ctype = "audio/x-wav";
	} else if (url.endsWith("jts")) {
	    ctype = "audio/x-tone-seq";
	} else if (url.endsWith("mid")) {
	    ctype = "audio/midi";
	} else {
	    throw new Exception("Cannot guess content type from URL: "+url);
	}
	return ctype;
    }

    void createPlayer() {
	try {
	    if (url.startsWith("http:")) {
		player = Manager.createPlayer(url);
	    } else if (url.startsWith("resource")) {
		int idx = url.indexOf(':');
		String loc = url.substring(idx+1);
		InputStream is = getClass().getResourceAsStream(loc);
		String ctype = guessContentType(url);
		player = Manager.createPlayer(is, ctype);
	    } else if (url.startsWith("rms:")) {
		boolean created = false;
		InputStream stream = null;
		while (stream == null) {
		    try {
			RecordStore rs = RecordStore.openRecordStore(RECORD_STORE_NAME, false);
			byte[] adata = rs.getRecord(1);
			rs.closeRecordStore();
			stream = new ByteArrayInputStream(adata);
			break; // exit while loop
		    } catch (Exception ex) {
		    	// record store not found
		    }
		    if (created) {
			// already tried to create record store!
			throw new Exception("Could not create and open record store!");
		    }
		    created = true;
		    createMyRecordStore(url, RECORD_STORE_NAME);
		}

		String ctype = guessContentType(url);
		player = Manager.createPlayer(stream, ctype);
	    }
	    player.setLoopCount(-1);
	} catch (Exception ex) {
	    if (player != null) {
		player.close();
		player = null;
	    }

	    Alert alert = new Alert("Warning", "Cannot create player", null, null);
	    alert.setTimeout(1000);
	    parentDisplay.setCurrent(alert);
	}
    }

    /**
     * Create a record store for the given url
     */
    private void createMyRecordStore(String url, String name) {
	try {
	    int idx = url.indexOf(':');
	    String loc = url.substring(idx+1);
	    InputStream is = getClass().getResourceAsStream(loc);
	    ByteArrayOutputStream baos = new ByteArrayOutputStream();
	    byte[] tmp = new byte[1024];
	    int nread;

	    while ( (nread = is.read(tmp, 0, 1024)) > 0 ) {
		baos.write(tmp, 0, nread);
	    }
	    byte[] data = baos.toByteArray();

	    is.close();
	    // create a RecordStore
	    RecordStore rs = RecordStore.openRecordStore(name, true);
	    rs.addRecord(data, 0, data.length);
	    rs.closeRecordStore();
	    System.out.println("created record store '"+name+"' with contents of "+loc);
	} catch (Exception ex) {
	    ex.printStackTrace();
	}
    }

    public void paint(Graphics g) {
	int w = getWidth();
	int h = getHeight();

	g.setColor(0);
	g.fillRect(0, 0, w, h);

	g.setColor(0xFF7f00);
	g.drawString("Audio Player", w/2, 8, Graphics.TOP | Graphics.HCENTER);

	if ( logo != null ) {
	    g.drawImage(logo, w/2, 30, Graphics.TOP | Graphics.HCENTER);
	}
	g.setColor(0xFF7f00);
	g.drawString("Audio Player", w/2, 8, Graphics.TOP | Graphics.HCENTER);

	g.drawString(title, w/2, 84, Graphics.TOP | Graphics.HCENTER);

	g.drawString(mtime, 0, 150, Graphics.TOP | Graphics.LEFT);
    }

    public void run() {

	/*
	 * method playSound() runs on GUI thread.
	 * Manager.createPlayer() will potentially invoke a blocking
	 * I/O. This is not the good practice recommonded by MIDP
	 * programming style. So here we will create the
	 * Player in a separate thread.
	 */
	createPlayer();

	if (player == null) {
	    // can't create player
	    synchronized(dThreadLock) {
		dThread = null;
		dThreadLock.notify();
		return;
	    }
	}

	try {
	    player.realize();
	    long dur = player.getDuration();
	    if (dur != -1)
		title = title + " [" + timeFM(dur)+ "]";
	    player.start();
	} catch (Exception ex) {}

	// mtime update loop
	while (!interrupted) {
	    try {
		mtime = timeFM(player.getMediaTime());
		repaint(0,110, 100, 170);
		Thread.sleep(100);

	    } catch (Exception ex) {}

	    // pause the loop if player paused
	    synchronized(pauseLock) {
		if (paused) {
		    try {
			pauseLock.wait();
		    }
		    catch (InterruptedException ie) {
			// nothing
		    }
		}
	    }
	}

	// terminating player and the thread
	player.close();
	player = null;

	synchronized(dThreadLock) {
	    dThread = null;
	    dThreadLock.notify();
	}
    }
    protected void keyPressed(int keycode) {
	switch (keycode) {
	case KEY_STAR:
	    changeVolume(-10);
	    break;
	case KEY_POUND:
	    changeVolume(10);
	    break;
	}
    }

    private void changeVolume(int diff) {
	VolumeControl vc;

	if ( player != null) {
	    vc = (VolumeControl) player.getControl("VolumeControl");
	    if (vc != null) {
		int cv = vc.getLevel();
		cv += diff;
		cv = vc.setLevel(cv);
	    }
	}
    }

    private String timeFM(long val) {
	String ret = "";
	int mval = (int)(val/1000);
	int sec = mval/1000;
	int min = sec/60;
	if (min >= 10)
	    ret = ret + min + ":";
	else if (min > 0)
	    ret = "0" + min + ":";
	else
	    ret = "00:";

	if (sec >= 10)
	    ret = ret + sec + ".";
	else if (sec > 0)
	    ret = ret + "0" + sec + ".";
	else
	    ret = ret + "00.";

	mval = (mval % 1000) / 100;
	ret = ret + mval;

	return (ret);
    }
}
