package awt03_musica;
/********************************************************************
 Ejemplo de lectura de un archivo JAR que contiene archivos de música
 En la parte izquierda del applet tenemos la lista de temas musicales.
 En la parte derecha:
 1. botones "Iniciar" y "Parar".
 2. campos de texto para informar de características de la URL de JAR
    y la extensión de cada archivo de audio.
 Atributos clave:
   Vector temas: vector de la clase tema donde se registran los archivos de audio:
		  class tema {
		       String titulo;
		       int tamanio;
		       AudioClip audio;
		  }
   int num_ultimo_tema: indice del último tema que ha sonado
			Este indice concuerda con el indice dentro de la lista y
			con el indice dentro de Vector temas
 Parámetro de la página HTML que invoca al applet:
   ARCHIVO_JAR: path del archivo jar; es relativo, empieza en el directorio CODEBASE
********************************************************************/
import java.applet.Applet;
import java.applet.AudioClip;
import java.awt.Label;
import java.awt.TextField;
import java.awt.Panel;
import java.awt.List;
import java.awt.GridLayout;
import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Color;
import java.awt.Toolkit;

import java.net.URL;
import java.net.URLConnection;
import java.net.JarURLConnection;
import java.io.IOException;
import java.io.FileNotFoundException;
import java.net.MalformedURLException;
import java.lang.SecurityException;

import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.Vector;
import java.util.Enumeration;

import java.awt.event.*;

public class applet extends Applet {
    Panel panel_central = new Panel();
    Panel panel_derecho = new Panel();
    Label etiq_titulo = new Label( "Escoge un tema musical de la lista y presiona Iniciar");
    List lista = new List();
    Button b_play = new Button( "Iniciar" );
    Button b_stop = new Button( "Parar" );
    Label etiq_host = new Label("Jar.Url.Host:" );
    TextField text_host = new TextField();
    Label etiq_protocol = new Label("Jar.Url.Protocol:" );
    TextField text_protocol = new TextField();
    Label etiq_port = new Label("Jar.URL.Port:" );
    TextField text_port = new TextField();
    Label etiq_length = new Label("Conexion.Length:" );
    TextField text_length = new TextField();

    GridLayout disenio1 = new GridLayout( 1,2,5,5);
    GridLayout disenio2 = new GridLayout( 5,2,3,3);

    private int num_ultimo_tema = -1;                 // Ultimo tema que ha sonado
    private Vector temas = new Vector(5);

    /************** Inicializar el applet *********************/
    public void init() {
	try {
	    jbInit();
	}
	catch (Exception e) {
	    e.printStackTrace();
	}
    }

    /****************** Inicialización de componentes **********/
    private void jbInit() throws Exception {
	this.setLayout( new BorderLayout( 5,5 ) );

	/***** Configuro y añado título del applet ****/
	panel_central.setLayout( disenio1 );       // GridLayout (1,2)
	etiq_titulo.setForeground(Color.red);
	etiq_titulo.setFont(new java.awt.Font("Dialog", 1, 12));
	etiq_titulo.setAlignment( Label.CENTER );
	add( etiq_titulo, BorderLayout.NORTH );

	/****** Configuro y añado lista al panel central ****/
	lista.setBackground(Color.green);
	lista.setForeground(Color.blue);
	lista.addItemListener(new applet_lista_itemAdapter(this));
	panel_central.add( lista );

	/****** Configuro y añado botones al panel derecho *****/
	b_play.addActionListener(new applet_b_play_actionAdapter(this));
	b_stop.addActionListener(new applet_b_stop_actionAdapter(this));
	panel_derecho.add( b_play );
	panel_derecho.add( b_stop );

	text_host.setEditable( false );
	text_port.setEditable( false );
	text_protocol.setEditable( false );
	text_length.setEditable( false );

	/****** Añado etiquetas y campos de texto al panel derecho ****/
	panel_derecho.setLayout( this.disenio2 );
	panel_derecho.add( etiq_host );
	panel_derecho.add( text_host );
	panel_derecho.add( etiq_protocol );
	panel_derecho.add( text_protocol );
	panel_derecho.add( etiq_port );
	panel_derecho.add( text_port );
	panel_derecho.add( etiq_length );
	panel_derecho.add( text_length );

	panel_central.add( panel_derecho );

	cargar_lista();

	add( panel_central, BorderLayout.CENTER);
    }

    /************************************************************************
       cargar_lista
       Lee de archivo JAR las entradas (nombres de archivos musicales). Para
       cada entrada (JarEntry):
       1. Los añade a la lista
       2. Los registra en el Vector de la clase tema, por medio de la función
	  aniadir_a_vector()

       Nota:
	 Para leer las entradas (JarEntry) hay que crear un JarFile. En esta
	 implementación se consigue un JarFile a partir de la URL. La otra
	 posibilidad, es decir, crear directamente un JarFile de la forma
	 siguiente:
	     JarFile jar = new JarFile( this.getCodeBase().toString()+getParameter("ARCHIVO_JAR"));
	 da problemas:
	     Utiliza \, estilo windows, en vez de /, estilo UNIX.
	     Utiliza \ como raiz, estilo windows, en vez de //, estilo UNIX.
    *************************************************************************/
    void cargar_lista() {
	int contador_temas = 0;
	try {
	    /****** Consigo un JarFile a partir de la URL *****/
	    URL url = new URL( "jar:"+this.getCodeBase().toString()+getParameter("ARCHIVO_JAR")+"!/");
	    JarURLConnection conexion = (JarURLConnection) url.openConnection();
	    JarFile jar = conexion.getJarFile();
	    // interesante: System.out.println( jarfile.getName()); // Ubicación en cache local

	    /***** Consigo las entradas del JarFile y las añado a la lista ********/
	    JarEntry entry;
	    for (Enumeration e = jar.entries() ; e.hasMoreElements() ;) {
		entry = (JarEntry) e.nextElement();
		// Excluye estas entradas, el resto las añade a la lista
		if (!"META-INF/MANIFEST.MF".equals(entry.getName()) &&
						!"META-INF/".equals(entry.getName()) ) {
		    lista.add(entry.getName()); // Añadir a la lista
		    AudioClip audio = obtener_audio( entry.getName(), true );
		    aniadir_a_vector( entry.getName(), (int) entry.getSize(), audio );
		    contador_temas++;
		}
	    }

	    /**** Actualizo campos de texto ********/
	    text_host.setText( url.getHost() );
	    text_protocol.setText( url.getProtocol() );
	    text_port.setText( String.valueOf(url.getPort()) );

	    /*** Si no hemos leido archivos, avisamos al usuario ***/
	    if (contador_temas < 1)
		getAppletContext().showStatus( "NO SE HA PODIDO LEER NINGUN ARCHIVO DE MUSICA");
       }
       catch (NullPointerException e) {
	   System.out.print("NullPointerException:");
	   System.out.println(e.getMessage() + "\r\n");
	   e.printStackTrace();
	}
       catch (SecurityException e) {
	    System.out.print("SecurityException:");
	    System.out.println(e.getMessage() + "\r\n");
	    e.printStackTrace();
	}
	catch (MalformedURLException mue) {  // Hija de IOException
	    System.out.print("URL inadecuada:");
	    System.out.println(mue.getMessage() + "\r\n");
	    mue.printStackTrace();
	}
	catch (FileNotFoundException e) {            // Hija de IOException
	    System.out.print("FileNotFoundException:");
	    System.out.println(e.getMessage() + "\r\n");
	    e.printStackTrace();
	}
	catch (IOException ioe) {            // Hija de Exception
	    System.out.print("IOException:");
	    System.out.println(ioe.getMessage() + "\r\n");
	    ioe.printStackTrace();
	}
	catch (Exception e) {
	    System.out.print("Exception:");
	    System.out.println(e.getMessage() + "\r\n");
	    e.printStackTrace();
	}
    }

    /*********************************************************************
     aniadir_a_vector
     Para añadir elementos del vector de la clase tema
    *********************************************************************/
    void aniadir_a_vector( String nombre, int tamanio, AudioClip audio ) {
	tema t = new tema();
	t.titulo = new String( nombre );
	t.tamanio = tamanio;
	t.audio = audio;
	temas.add( t );
    }

    /**********************************************************************
       b_play_actionPerformed
       Respuesta a Evento: presionar botón "Iniciar". Acciones:
       1. Cambia el atributo num_ultimo_tema: si hay una selección, ésta es
	  es el último tema
       2. Play
     **********************************************************************/
    void b_play_actionPerformed( ActionEvent e ) {

	/*** Si no se ha seleccionado ningún elemento, salimos ***/
	if (  lista.getSelectedIndex() < 0 )
	    return;

	AudioClip audio_seleccionado= ((tema)temas.get(lista.getSelectedIndex())).audio;
	AudioClip audio_ultimo;

	/*** Si todavía no hay ningún elemento que haya "sonado", ... ***/
	if ( num_ultimo_tema < 0 )
	    audio_ultimo = null;
	else
	    audio_ultimo= ((tema)temas.get(num_ultimo_tema)).audio;

	/**** Si ha seleccionado un tema diferente al último que ha sonado ... ***/
	if ( num_ultimo_tema != lista.getSelectedIndex() ) {
	    if ( audio_ultimo != null )
		audio_ultimo.stop();                   // Paro el tema anterior
	    num_ultimo_tema = lista.getSelectedIndex();// Este es el último tema que ha "sonado"
	    if (audio_seleccionado == null)
		getAppletContext().showStatus( "NO SE HA LEIDO EL ARCHIVO NUMERO "+num_ultimo_tema);
	}

	if ( audio_seleccionado != null )                                       // Si hay tema, play
	   audio_seleccionado.play();
    }

    /**********************************************************************
       b_stop_actionPerformed
       Respuesta a Evento: presionar botón "Parar".
       Acciones:
	 Parar el AudioClip que está sonando
    **********************************************************************/
    void b_stop_actionPerformed(ActionEvent e) {

	/*** Si no ha sonado ningún tema, entonces no hay nada que parar, salimos */
	if (num_ultimo_tema < 0)
	    return;

	AudioClip audio_ultimo= ((tema)temas.get(num_ultimo_tema)).audio;
	if ( audio_ultimo != null )
	    audio_ultimo.stop();
    }

    /**********************************************************************
       lista_itemStateChanged
       Respuesta a Evento: clic en lista de temas musicales.
       Acciones:
	 Poner en campo de texto el tamaño del archivo de audio.
    **********************************************************************/
    void lista_itemStateChanged(ItemEvent e) {
	int t = lista.getSelectedIndex();
	if ( t >= 0 )  // Si hay una selección, ponemos su tamaño
	   text_length.setText( String.valueOf(((tema)temas.get(t)).tamanio) );
    }

    /*********************************************************************
     obtener_audio
     Parámetros:
	1. titulo: nombre del archivo audio que hay que leer.
	2. devolver_audio: si es true, entonces hay que retornar
	   un AudioClip correspondiente al archivo de audio. Con False
	   devuelve null.
     Busca en fichero jar el archivo de audio
     Devuelve el AudioClip del archivo de audio (si devolver_audio es true)
    **********************************************************************/
    AudioClip obtener_audio( String titulo, boolean devolver_audio ) {
	String titulo_tema_selec = lista.getSelectedItem();
	URL url_musica = descargar( "jar:", getParameter( "ARCHIVO_JAR")+"!/",
					  titulo );
	if ( url_musica != null  && devolver_audio )
	    return getAppletContext().getAudioClip( url_musica );
	return null;
    }

    /************************************************************************
       descargar
       A partir de los parámetros:
	 1- protocolo: si es jar, poner "jar:"; si no, entonces ""
	 2- path relativo de archivo. Es la ruta desde el directorio CODEBASE. Si
	    ya está en CODEBASE, entonces poner "".
	 3- archivo que queremos descargar.
       Devuelve la URL del archivo que deseamos descargar
    *************************************************************************/
    URL descargar( String protocol, String path_relativo, String archivo ) {

	/*** Obtenemos el path absoluto del archivo JAR ***/
	String path_base = protocol + this.getCodeBase().toString()+path_relativo;
	try {
	    /****** Construimos la URL del archivo de sonido a partir de: url de jar y
		    nombre de archivo de música ***************/
	    URL url = new URL( new URL( path_base), archivo );
	    return url;
	}
	catch (NullPointerException e) {    // Hija de RuntimeException
	    System.out.print( "Null Pointer:");
	    System.out.println(e.getMessage() + "\r\n");
	    return null;
	}
	catch (MalformedURLException mue) {  // Hija de IOException
	    System.out.print("URL inadecuada:");
	    System.out.println(mue.getMessage() + "\r\n");
	    return null;
	}
	catch (IOException ioe) {            // Hija de Exception
	    System.out.print("IOException:");
	    System.out.println(ioe.getMessage() + "\r\n");
	    return null;
	}
	catch (SecurityException ace) {
	    System.out.print("Security Exception:");
	    System.out.println(ace.getMessage() + "\r\n");
	    return null;
	}
	catch (Exception e) {
	    System.out.print("Exception:");
	    System.out.println(e.getMessage() + "\r\n");
	    return null;
	}
    }
}

/*************************************************************************
 clase tema
**************************************************************************/
class tema {
    String titulo;
    int tamanio;
    AudioClip audio;
}

/***************************************************************************
 Gestores de eventos
***************************************************************************/
class applet_b_play_actionAdapter implements java.awt.event.ActionListener {
    applet adaptee;

    applet_b_play_actionAdapter(applet adaptee) {
	this.adaptee = adaptee;
    }
    public void actionPerformed(ActionEvent e) {
	adaptee.b_play_actionPerformed(e);
    }
}

class applet_b_stop_actionAdapter implements java.awt.event.ActionListener {
    applet adaptee;

    applet_b_stop_actionAdapter(applet adaptee) {
	this.adaptee = adaptee;
    }
    public void actionPerformed(ActionEvent e) {
	adaptee.b_stop_actionPerformed(e);
    }
}

class applet_lista_itemAdapter implements java.awt.event.ItemListener {
    applet adaptee;

    applet_lista_itemAdapter(applet adaptee) {
	this.adaptee = adaptee;
    }
    public void itemStateChanged(ItemEvent e) {
	adaptee.lista_itemStateChanged(e);
    }
}
