import java.util.Vector;
import java.awt.Dimension;
import java.awt.GridBagLayout;
import java.awt.GridBagConstraints;
import java.awt.Insets;
import java.awt.event.ActionListener;
import java.awt.event.KeyListener;
import java.awt.event.MouseListener;
import java.awt.event.WindowListener;
import java.awt.event.ActionEvent;
import java.awt.event.WindowEvent;
import java.awt.event.MouseEvent;
import java.awt.event.KeyEvent;
import java.net.InetAddress;
import java.net.UnknownHostException;
import javax.swing.JFrame;
import javax.swing.JTextField;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ScrollPaneConstants;
import javax.swing.border.TitledBorder;
import javax.swing.border.EtchedBorder;
import javax.media.CaptureDeviceInfo;
import javax.media.CaptureDeviceManager;
import javax.media.Format;
import javax.media.format.AudioFormat;
import javax.media.format.VideoFormat;

//clase principal del proyecto
public class Comunicador extends JFrame implements ActionListener, KeyListener,
                                          MouseListener, WindowListener
{
	// Constantes de la clase
	private static final String TITULO = "Videoconferencia";
	private static final int ANCHOTEXTO = 15;

	// Variables miembro de la clase
	private JTextField ipLocal;
	private JTextField puertoLocal;
	private JTextField puertoVideoLocal;
	private JTextField ipRemota;
	private JTextField puertoRemoto;
	private JTextField puertoVideoRemoto;
	private JButton IP;
	private JButton Iniciar;
	private JButton Recibir;
	private JButton IniciarVideo;
	private JButton RecibirVideo;
	private JLabel Estado;
	private Config config;
	private boolean iniciado;
	private boolean recibiendo;
	private boolean iniciadoVideo;
	private boolean recibiendoVideo;
	private Vector deviceList;
	private Vector deviceVideoList;
	private CaptureDeviceInfo captureAudioDevice;
	private CaptureDeviceInfo captureVideoDevice;
	private JList listaAudioDevice;
	private JList listaVideoDevice;
	private TransmisorAudio tx;
	private ReceptorAudio rx;
	private TransmisorVideo tx2;
	private ReceptorVideo rx2;

	// Método main que inicia el comunicador
	public static void main(String[] args) { new Comunicador(); }

	// Constructor de la clase
	public Comunicador()
	{
		System.out.println(Misc.ind() + "Inicializando variables...");
		captureAudioDevice = null;
		captureVideoDevice = null;

		//ponemos título a la ventana
		System.out.println(Misc.ind() + "Poniendo titulo...");
		setTitle(TITULO);

		//creamos una configuración con las IPs local y remota y los puertos
		System.out.println(Misc.ind() + "Creando configuracion...");
		Misc.aumentarInd();
		config = new Config();
		Misc.reducirInd();

		//creamos una lista de dispositivos de captura de audio (formato de audio lineal)
		System.out.println(Misc.ind() + "Creando lista de dispositivos de captura de audio...");
		Misc.aumentarInd();
		deviceList = CaptureDeviceManager.getDeviceList(new AudioFormat(AudioFormat.LINEAR, 44100, 8, 1));
		for (int i = 0; i < deviceList.size(); i++)
			System.out.println(Misc.ind() + ((CaptureDeviceInfo)deviceList.elementAt(i)).getName());

		//comprobamos que haya dispositivos de captura de audio
		//en caso de haberlo, seleccionamos el primero por defecto
		if (deviceList.size() > 0)
			captureAudioDevice = (CaptureDeviceInfo)deviceList.firstElement();
		else
		{
			captureAudioDevice = null;
			System.out.println(Misc.ind() + "No se han encontrado dispositivos de captura de audio...");
		}
		Misc.reducirInd();
		System.out.println(Misc.ind() + "Lista de dispositivos de captura de audio creada...");

		//creamos una lista de dispositivos de captura de video (formato de video RGB)
		System.out.println(Misc.ind() + "Creando lista de dispositivos de captura de video...");
		Misc.aumentarInd();
		deviceVideoList = CaptureDeviceManager.getDeviceList(new VideoFormat(VideoFormat.RGB, new Dimension(352, 288), Format.NOT_SPECIFIED, null, Format.NOT_SPECIFIED));
		for (int i = 0; i < deviceVideoList.size(); i++)
			System.out.println(Misc.ind() + ((CaptureDeviceInfo)deviceVideoList.elementAt(i)).getName());

		//comprobamos que haya dispositivos de captura de video
		//en caso de haberlo, seleccionamos el primero por defecto
		if (deviceVideoList.size() > 0)
			captureVideoDevice = (CaptureDeviceInfo)deviceVideoList.firstElement();
		else
		{
			captureVideoDevice = null;
			System.out.println(Misc.ind() + "No se han encontrado dispositivos de captura de video...");
		}
		Misc.reducirInd();
		System.out.println(Misc.ind() + "Lista de dispositivos de captura de audio creada...");

		//creamos el panel principal de la aplicación con su layout
		GridBagLayout gridBagLayout = new GridBagLayout();
		GridBagConstraints gbc;
		System.out.println(Misc.ind() + "Creando panel principal...");
		Misc.aumentarInd();
		JPanel p = new JPanel();
		p.setLayout(gridBagLayout);

		//creamos el subpanel de configuración local
		System.out.println(Misc.ind() + "Creando panel de configuracion local...");
		Misc.aumentarInd();
		JPanel panelConfiguracionLocal = crearPanelConfiguracionLocal();
		Misc.reducirInd();

		//aplicamos su configuración y lo insertamos en el panel principal
		System.out.println(Misc.ind() + "Insertando panel de configuracion local...");
		gbc = new GridBagConstraints();
		gbc.gridx = 0;
		gbc.gridy = 0;
		gbc.gridwidth = 2;
		gbc.anchor = GridBagConstraints.CENTER;
		gbc.fill = GridBagConstraints.BOTH;
		gbc.insets = new Insets(10, 5, 0, 0);
		((GridBagLayout)p.getLayout()).setConstraints(panelConfiguracionLocal, gbc);
		p.add(panelConfiguracionLocal);

		//creamos el subpanel de configuración remota
		System.out.println(Misc.ind() + "Creando panel de configuracion remota...");
		Misc.aumentarInd();
		JPanel panelConfiguracionRemota = crearPanelConfiguracionRemota();
		Misc.reducirInd();

		//aplicamos su configuración y lo insertamos en el panel principal
		System.out.println(Misc.ind() + "Insertando panel de configuracion remota...");
		gbc = new GridBagConstraints();
		gbc.gridx = 1;
		gbc.gridy = 1;
		gbc.weightx = 1.0;
		gbc.weighty = 1.0;
		gbc.anchor = GridBagConstraints.CENTER;
		gbc.fill = GridBagConstraints.BOTH;
		gbc.insets = new Insets(10, 5, 0, 0);
		((GridBagLayout)p.getLayout()).setConstraints(panelConfiguracionRemota, gbc);
		p.add(panelConfiguracionRemota);

		//creamos el subpanel de botones
		System.out.println(Misc.ind() + "Creando panel de botones...");
		Misc.aumentarInd();
		JPanel panelBotones = crearPanelBotones();
		Misc.reducirInd();

		//aplicamos su configuración y lo insertamos en el panel principal
		System.out.println(Misc.ind() + "Insertando panel de botones...");
		gbc = new GridBagConstraints();
		gbc.gridx = 1;
		gbc.gridy = 2;
		gbc.weightx = 1.0;
		gbc.weighty = 1.0;
		gbc.anchor = GridBagConstraints.CENTER;
		gbc.fill = GridBagConstraints.BOTH;
		gbc.insets = new Insets(10, 5, 0, 0);
		((GridBagLayout)p.getLayout()).setConstraints(panelBotones, gbc);
		p.add(panelBotones);

		//creamos el subpanel de etiquetas
		System.out.println(Misc.ind() + "Creando panel de etiquetas...");
		Misc.aumentarInd();
		JPanel panelEstado = crearPanelEstado();
		Misc.reducirInd();

		//aplicamos su configuración y lo insertamos en el panel principal
		System.out.println(Misc.ind() + "Insertando panel de etiquetas...");
		gbc = new GridBagConstraints();
		gbc.gridx = 1;
		gbc.gridy = 3;
		gbc.weightx = 1.0;
		gbc.weighty = 1.0;
		gbc.anchor = GridBagConstraints.CENTER;
		gbc.fill = GridBagConstraints.BOTH;
		gbc.insets = new Insets(10, 5, 0, 0);
		((GridBagLayout)p.getLayout()).setConstraints(panelEstado, gbc);
		p.add(panelEstado);

		Misc.reducirInd();

		//aplicamos la configuración al panel principal		System.out.println(Misc.ind() + "Insertando panel principal...");
		getContentPane().add(p);
		listaAudioDevice.addMouseListener(this);
		listaVideoDevice.addMouseListener(this);
		addWindowListener(this);
        pack();

        //hacemos visible el panel principal
        setVisible(true);
		System.out.println(Misc.ind() + "Tareas de inicializacion finalizadas...");
	}

	//método que crea una etiqueta y la configura
	private JLabel crearEtiqueta(JPanel p, int x, int y, String etiqueta)
	{
		//creamos la etiqueta
		JLabel label = new JLabel(etiqueta);

		//configuramos la etiqueta
		System.out.println(Misc.ind() + "Insertando etiqueta " + etiqueta);
		GridBagConstraints gbc = new GridBagConstraints();
		gbc.gridx = x;
		gbc.gridy = y;
		gbc.weightx = 0.0;
		gbc.weighty = 0.0;
		gbc.anchor = GridBagConstraints.EAST;
		gbc.fill = GridBagConstraints.NONE;
		gbc.insets = new Insets(5, 5, 0, 5);
		((GridBagLayout)p.getLayout()).setConstraints(label, gbc);

		//insertamos la etiqueta
		p.add(label);

		//devolvemos la etiqueta
		return label;
	}

	//método que crea un cuadro de texto y lo configura
	private JTextField crearCuadroTexto(JPanel p, int x, int y, int a, String texto)
	{
		//creamos el cuadro de texto
		JTextField t = new JTextField(a);
		t.addActionListener(this);

		//configuramos el cuadro de texto
		System.out.println(Misc.ind() + "Insertando cuadro de texto " + texto);
		GridBagConstraints gbc = new GridBagConstraints();
		gbc.gridx = x;
		gbc.gridy = y;
		gbc.weightx = 0.0;
		gbc.weighty = 0.0;
		gbc.anchor = GridBagConstraints.WEST;
		gbc.fill = GridBagConstraints.NONE;
		gbc.insets = new Insets( 5,5,0,5);
		((GridBagLayout)p.getLayout()).setConstraints(t, gbc);

		//insertamos el cuadro de texto
		p.add(t);

		//colocamos texto por defecto al cuadro de texto
		t.setText(texto);

		//devolvemos el cuadro de texto
		return t;
	}

	//método que crea un boton y lo configura
	private JButton crearBoton(JPanel p, int x, int y, String texto)
	{
		//creamos el boton
		JButton t = new JButton(texto);
		t.addActionListener(this);

		//configuramos el boton
		System.out.println(Misc.ind() + "Insertando boton " + texto);
		GridBagConstraints gbc = new GridBagConstraints();
		gbc.gridx = x;
		gbc.gridy = y;
		gbc.weightx = 0.0;
		gbc.weighty = 0.0;
		gbc.anchor = GridBagConstraints.WEST;
		gbc.fill = GridBagConstraints.NONE;
		gbc.insets = new Insets( 5,5,0,5);
		((GridBagLayout)p.getLayout()).setConstraints(t, gbc);

		//insertamos el boton
		p.add(t);

		//devolvemos el boton
		return t;
	}

	//método que crea un panel con scroll y lo configura
	private void crearPanelScroll(JPanel p, int x, int y, JList lista)
	{
		//creamos el panel scroll
		JScrollPane panelScrollAudioDevices = new JScrollPane(lista,
							ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
							ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);

		//configuramos el panel scroll
		System.out.println(Misc.ind() + "Insertando panel con scroll...");
		GridBagConstraints gbc = new GridBagConstraints();
		gbc.gridx = x;
		gbc.gridy = y;
		gbc.weightx = 0.0;
		gbc.weighty = 0.0;
		gbc.anchor = GridBagConstraints.CENTER;
		gbc.fill = GridBagConstraints.BOTH;
		gbc.insets = new Insets(10, 5, 0, 0);
		((GridBagLayout)p.getLayout()).setConstraints(panelScrollAudioDevices, gbc);
		p.add(panelScrollAudioDevices);
	}

	//método que crea una lista de dispositivos
	private JList crearLista(Vector deviceList, String prototipo)
	{
		//generamos un vector de nombres de dispositivo
		Vector listaNombresAudioDevice = new Vector();
		for (int i = 0; i < deviceList.size(); i++)
			listaNombresAudioDevice.add(((CaptureDeviceInfo)deviceList.elementAt(i)).getName());

		//creamos la lista de dispositivos
		JList listaAudioDevice = new JList(listaNombresAudioDevice);
		listaAudioDevice.addKeyListener(this);

		//asignamos una máscara para el ancho de la lista
		listaAudioDevice.setPrototypeCellValue(prototipo);

		//devolvemos la lista
		return listaAudioDevice;
	}

	//método que crea el panel de configuración local
	private JPanel crearPanelConfiguracionLocal()
	{
		JPanel p = new JPanel();
		GridBagLayout gridBagLayout = new GridBagLayout();
		p.setLayout(gridBagLayout);

		//creamos una etiqueta para la IP local
		System.out.println(Misc.ind() + "Creando etiqueta de IP...");
		crearEtiqueta(p, 0, 0, "Dirección IP:");

		//creamos un cuadro de texto para la IP local
		System.out.println(Misc.ind() + "Creando cuadro de texto local...");
		ipLocal = crearCuadroTexto(p, 1, 0, ANCHOTEXTO, config.destino().ipLocal());

		//creamos un boton de búsqueda automática de la IP local
		System.out.println(Misc.ind() + "Creando boton de busqueda de IP...");
		IP = crearBoton(p, 2, 0, "Generar");

		//creamos una etiqueta para el puerto de transmisión de audio
		System.out.println(Misc.ind() + "Creando etiqueta de puerto...");
		crearEtiqueta(p, 0, 1, "Puerto de emisión de audio:");

		//creamos un cuadro de texto para el puerto de transmisión de audio
		System.out.println(Misc.ind() + "Creando cuadro de texto puerto...");
		puertoLocal = crearCuadroTexto(p, 1, 1, ANCHOTEXTO, config.destino().puertoAudioLocal());

		//creamos una etiqueta para el puerto de transmisión de video
		System.out.println(Misc.ind() + "Creando etiqueta de puerto de video...");
		crearEtiqueta(p, 0, 3, "Puerto de emisión de video:");

		//creamos un cuadro de texto para el puerto de transmisión de video
		System.out.println(Misc.ind() + "Creando cuadro de texto puerto...");
		puertoVideoLocal = crearCuadroTexto(p, 1, 3, ANCHOTEXTO, config.destino().puertoVideoLocal());

		//creamos una etiqueta para los dispositivos de captura de audio
		System.out.println(Misc.ind() + "Creando etiqueta de dispositivos de captura de audio...");
		crearEtiqueta(p, 0, 4, "Dispositivos de captura de audio:");

		//creamos una lista de nombres de dispositivos de captura de audio
		System.out.println(Misc.ind() + "Creando lista de nombres de dispositivos de captura de audio...");
		listaAudioDevice = crearLista(deviceList, "xxxxxxxxxxxxxxxxxxxxxxxxx");

		//creamos el panel con scroll para dispositivos de audio
		System.out.println(Misc.ind() + "Creando panel con scroll de dispositivos de audio...");
		crearPanelScroll(p, 0, 5, listaAudioDevice);

		//creamos una etiqueta para los dispositivos de captura de video
		System.out.println(Misc.ind() + "Creando etiqueta de dispositivos de captura de video...");
		crearEtiqueta(p, 1, 4, "Dispositivos de captura de video:");

		//creamos una lista de nombres de dispositivos de captura de video
		System.out.println(Misc.ind() + "Creando lista de nombres de dispositivos de captura de video...");
		listaVideoDevice = crearLista(deviceVideoList, "xxxxxxxxxxxxxxxxxxxxxxxxx");

		//creamos el panel con scroll para dispositivos de video
		System.out.println(Misc.ind() + "Creando panel con scroll de dispositivos de video...");
		crearPanelScroll(p, 1, 5, listaVideoDevice);

		//terminamos de configurar el panel
		System.out.println(Misc.ind() + "Poniendo titulo al panel...");
		TitledBorder titledBorder = new TitledBorder(new EtchedBorder(), "Configuración local");
		p.setBorder(titledBorder);
		return p;
	}

	//método que crea el panel de configuración remota
	private JPanel crearPanelConfiguracionRemota()
	{
		JPanel p = new JPanel();
		GridBagLayout gridBagLayout = new GridBagLayout();
		p.setLayout(gridBagLayout);

		//creamos una etiqueta para para la dirección IP de destino
		System.out.println(Misc.ind() + "Creando etiqueta de IP remota...");
		crearEtiqueta(p, 0, 0, "Dirección IP:");

		//creamos un cuadro de texto para la IP del destino
		System.out.println(Misc.ind() + "Creando cuadro de texto de IP remota...");
		ipRemota = crearCuadroTexto(p, 1, 0, ANCHOTEXTO, config.destino().ipRemota());

		//creamos una etiqueta para para el puerto de audio de destino
		System.out.println(Misc.ind() + "Creando etiqueta de puerto de audio remoto...");
		crearEtiqueta(p, 0, 1, "Puerto de recepcion de audio:");

		//creamos un cuadro de texto para el puerto de audio remoto
		System.out.println(Misc.ind() + "Creando cuadro de texto de puerto de audio remoto...");
		puertoRemoto = crearCuadroTexto(p, 1, 1, ANCHOTEXTO, config.destino().puertoAudioRemoto());

		//creamos una etiqueta para para el puerto de video de destino
		System.out.println(Misc.ind() + "Creando etiqueta de puerto de video remoto...");
		crearEtiqueta(p, 0, 2, "Puerto de recepcion de video:");

		//creamos un cuadro de texto para el puerto de video remoto
		System.out.println(Misc.ind() + "Creando cuadro de texto puerto...");
		puertoVideoRemoto = crearCuadroTexto(p, 1, 2, ANCHOTEXTO, config.destino().puertoVideoRemoto());

		//terminamos de configurar el panel
		System.out.println(Misc.ind() + "Poniendo titulo al panel...");
		TitledBorder titledBorder = new TitledBorder(new EtchedBorder(), "Configuración remota");
		p.setBorder( titledBorder);
		return p;
	}

	//método que crea un panel de botones
	private JPanel crearPanelBotones()
	{
		JPanel p = new JPanel();
		GridBagLayout gridBagLayout = new GridBagLayout();
		p.setLayout(gridBagLayout);

		//creamos un boton para iniciar la transmisión de audio
		System.out.println(Misc.ind() + "Creando boton de inicio transmision de audio...");
		Iniciar = crearBoton(p, 0, 0, "Iniciar transmisión audio");
		iniciado = false;

		//creamos un boton para iniciar la recepcion de audio
		System.out.println(Misc.ind() + "Creando boton de inicio recepcion de audio...");
		Recibir = crearBoton(p, 1, 0, "Iniciar recepción audio");
		recibiendo = false;

		//creamos un boton para iniciar la transmision de video
		System.out.println(Misc.ind() + "Creando boton de inicio transmision de video...");
		IniciarVideo = crearBoton(p, 0, 1, "Iniciar transmisión video");
		iniciadoVideo = false;

		//creamos un boton para iniciar la recepcion de video
		System.out.println(Misc.ind() + "Creando boton de inicio recepcion de video...");
		RecibirVideo = crearBoton(p, 1, 1, "Iniciar recepción video");
		recibiendoVideo = false;

		//terminamos de configurar el panel
		TitledBorder titledBorder = new TitledBorder(new EtchedBorder(), "Panel de botones");
		p.setBorder(titledBorder);
		return p;
	}

	//método que crea un panel de informacion
	private JPanel crearPanelEstado()
	{
		JPanel p = new JPanel();
		GridBagLayout gridBagLayout = new GridBagLayout();
		p.setLayout(gridBagLayout);

		System.out.println(Misc.ind() + "Creando etiqueta de estado...");
		Estado = crearEtiqueta(p, 0, 0, "José Antonio Jiménez Pérez / Jorge Barbero Pérez");

		//terminamos de configurar el panel
		TitledBorder titledBorder = new TitledBorder(new EtchedBorder(), "Acerca de...");
		p.setBorder(titledBorder);
		return p;
	}

	//método que gestiona los eventos
	public void actionPerformed(ActionEvent event)
	{
		//Obtenemos la fuente del evento
		Object source = event.getSource();

		//Buscamos de donde proviene el evento
		//si proviene del boton Iniciar
		if (source == Iniciar)
		{
			//comprobamos si ya está iniciado
			if (!iniciado)
			{
				//comprobamos si hay dispositivos de captura de audio
				if (captureAudioDevice != null)
				{
					//actualizamos la etiqueta del boton
					System.out.println(Misc.ind() + "Se ha pulsado el boton de iniciar transmision de audio...");
					Iniciar.setText("Detener transmisión audio");

					//iniciamos la transmisión
					iniciarTransmisionAudio();
					iniciado = true;
				}
				else
					System.out.println(Misc.ind() + "No hay ningun dispositivo de captura de audio...");
			}
			else
			{
				//actualizamos la etiqueta del boton
				System.out.println(Misc.ind() + "Se ha pulsado el boton de detener transmision de audio...");
				Iniciar.setText("Iniciar transmisión audio");
				//detenemos la transmisión
				detenerTransmisionAudio();
				iniciado = false;
			}
		}
		//si proviene del boton IniciarVideo
		else if (source == IniciarVideo)
		{
			//comprobamos si ya está iniciado
			if (!iniciadoVideo)
			{
				//comprobamos si hay dispositivos de captura de video
				if (captureVideoDevice != null)
				{
					//actualizamos la etiqueta del boton
					System.out.println(Misc.ind() + "Se ha pulsado el boton de iniciar transmision de video...");
					IniciarVideo.setText("Detener transmisión video");
					//iniciamos la transmisión
					iniciarTransmisionVideo();
					iniciadoVideo = true;
				}
				else
					System.out.println(Misc.ind() + "No hay ningun dispositivo de captura de video...");
			}
			else
			{
				//actualizamos la etiqueta del boton
				System.out.println(Misc.ind() + "Se ha pulsado el boton de detener transmision de video...");
				IniciarVideo.setText("Iniciar transmisión video");
				//detenemos la transmisión
				detenerTransmisionVideo();
				iniciadoVideo = false;
			}
		}
		//si proviene del boton Recibir
		else if (source == Recibir)
		{
			//comprobamos si ya está iniciado
			if (!recibiendo)
			{
				//actualizamos la etiqueta del boton
				System.out.println(Misc.ind() + "Se ha pulsado el boton de iniciar recepcion de audio...");
				Recibir.setText("Detener recepción audio");
				//iniciamos la recepcion
				iniciarRecepcionAudio();
			}
			else
			{
				//actualizamos la etiqueta del boton
				System.out.println(Misc.ind() + "Se ha pulsado el boton de detener recepcion de audio...");
				Recibir.setText("Iniciar recepción audio");
				//detenemos la recepcion
				detenerRecepcionAudio();
			}
			recibiendo = !recibiendo;
		}
		//si proviene del boton RecibirVideo
		else if (source == RecibirVideo)
		{
			//comprobamos si ya está iniciado
			if (!recibiendoVideo)
			{
				//actualizamos la etiqueta del boton
				System.out.println(Misc.ind() + "Se ha pulsado el boton de iniciar recepcion de video...");
				RecibirVideo.setText("Detener recepción video");
				//iniciamos la recepcion
				iniciarRecepcionVideo();
			}
			else
			{
				//actualizamos la etiqueta del boton
				System.out.println(Misc.ind() + "Se ha pulsado el boton de detener recepcion de video...");
				RecibirVideo.setText("Iniciar recepción video");
				//detenemos la recepcion
				detenerRecepcionVideo();
			}
			recibiendoVideo = !recibiendoVideo;
		}
		//si proviene del cuadro de texto ipLocal
		else if (source == ipLocal)
		{
			//actualizamos la configuracion
			config.destino().ipLocal(ipLocal.getText().trim());
			System.out.println(Misc.ind() + "IP local cambiada a " + config.destino().ipLocal());
		}
		//si proviene del cuadro de texto puertoLocal
		else if (source == puertoLocal)
		{
			//actualizamos la configuracion
			config.destino().puertoAudioLocal(puertoLocal.getText().trim());
			System.out.println(Misc.ind() + "Puerto local de audio cambiado a " + config.destino().puertoAudioLocal());
		}
		//si proviene del cuadro de texto puertoVideoLocal
		else if (source == puertoVideoLocal)
		{
			//actualizamos la configuracion
			config.destino().puertoVideoLocal(puertoVideoLocal.getText().trim());
			System.out.println(Misc.ind() + "Puerto local de video cambiado a " + config.destino().puertoVideoLocal());
		}
		//si proviene del cuadro de texto ipRemota
		else if (source == ipRemota)
		{
			//actualizamos la configuracion
			config.destino().ipRemota(ipRemota.getText().trim());
			System.out.println(Misc.ind() + "IP remota cambiada a " + config.destino().ipRemota());
		}
		//si proviene del cuadro de texto puertoRemoto
		else if (source == puertoRemoto)
		{
			//actualizamos la configuracion
			config.destino().puertoAudioRemoto(puertoRemoto.getText().trim());
			System.out.println(Misc.ind() + "Puerto remoto de audio cambiado a " + config.destino().puertoAudioRemoto());
		}
		//si proviene del cuadro de texto puertoVideoRemoto
		else if (source == puertoVideoRemoto)
		{
			//actualizamos la configuracion
			config.destino().puertoVideoRemoto(puertoVideoRemoto.getText().trim());
			System.out.println(Misc.ind() + "Puerto remoto de video cambiado a " + config.destino().puertoVideoRemoto());
		}
		//si proviene del boton de generar la IP
		else if (source == IP)
		{
			System.out.println(Misc.ind() + "Obteniendo IP local...");
			try
			{
				//Intentamos generar la dirección IP
				String host = InetAddress.getLocalHost().getHostAddress();
				//actualizamos el cuadro de texto implicado
				ipLocal.setText(host);
				//actualizamos la configuracion
				config.destino().ipLocal(host);
				System.out.println(Misc.ind() + "IP local cambiada a " + host);
			}
			//capturamos la excepcion
			catch(UnknownHostException e)
			{
				System.out.println(Misc.ind() + "Fallo al obtener la IP local...");
				ipLocal.setText("");
				config.destino().ipLocal("");
			}
		}
	}

	//metodo que crea el transmisor de audio
	private void iniciarTransmisionAudio()
	{
		//creamos el transmisor de audio
		System.out.println(Misc.ind() + "Creando transmisor de audio");
		Misc.aumentarInd();
		tx = new TransmisorAudio(config.destino());
		Misc.reducirInd();
		//iniciamos el transmisor de audio
		System.out.println(Misc.ind() + "Iniciando transmisor de audio");
		Misc.aumentarInd();
		tx.start(captureAudioDevice);
		Misc.reducirInd();
		System.out.println(Misc.ind() + "Transmisor de audio iniciado");
	}

	//metodo que destruye el transmisor de audio
	private void detenerTransmisionAudio()
	{
		//detenemos el transmisor de audio
		System.out.println(Misc.ind() + "Destruyendo transmisor de audio");
		Misc.aumentarInd();
		tx.stop();
		Misc.reducirInd();
		tx = null;
		System.out.println(Misc.ind() + "Transmisor de audio destruido");
	}

	//metodo que crea el receptor de audio
	private void iniciarRecepcionAudio()
	{
		//creamos el receptor de audio
		System.out.println(Misc.ind() + "Creando receptor de audio");
		Misc.aumentarInd();
		rx = new ReceptorAudio(config.destino());
		Misc.reducirInd();
		System.out.println(Misc.ind() + "Receptor de audio iniciado");
	}

	//metodo que destruye el receptor de audio
	private void detenerRecepcionAudio()
	{
		//detenemos el receptor de audio
		System.out.println(Misc.ind() + "Destruyendo receptor de audio");
		Misc.aumentarInd();
		rx.close();
		rx = null;
		Misc.reducirInd();
		System.out.println(Misc.ind() + "Receptor de audio destruido");
	}

	//metodo que crea el transmisor de video
	private void iniciarTransmisionVideo()
	{
		//creamos el transmisor de video
		System.out.println(Misc.ind() + "Creando transmisor de video");
		Misc.aumentarInd();
		tx2 = new TransmisorVideo(config.destino());
		Misc.reducirInd();
		//iniciamos el transmisor de video
		System.out.println(Misc.ind() + "Iniciando transmisor de video");
		Misc.aumentarInd();
		tx2.start(captureVideoDevice);
		Misc.reducirInd();
		System.out.println(Misc.ind() + "Transmisor de video iniciado");
	}

	//metodo que destruye el transmisor de video
	private void detenerTransmisionVideo()
	{
		//detenemos el transmisor de video
		System.out.println(Misc.ind() + "Destruyendo transmisor de video");
		Misc.aumentarInd();
		tx2.stop();
		Misc.reducirInd();
		tx2= null;
		System.out.println(Misc.ind() + "Transmisor de video destruido");
	}

	//metodo que crea el receptor de video
	private void iniciarRecepcionVideo()
	{
		//creamos el receptor de video
		System.out.println(Misc.ind() + "Creando receptor de video");
		Misc.aumentarInd();
		rx2 = new ReceptorVideo(config.destino());
		Misc.reducirInd();
		System.out.println(Misc.ind() + "Receptor de video iniciado");
	}

	//metodo que destruye el receptor de video
	private void detenerRecepcionVideo()
	{
		//detenemos el receptor de video
		System.out.println(Misc.ind() + "Destruyendo receptor de video");
		Misc.aumentarInd();
		rx2.close();
		rx2 = null;
		Misc.reducirInd();
		System.out.println(Misc.ind() + "Receptor de video destruido");
	}

	//metodo ejecutado al cerrar la ventana del programa
	public void windowClosing(WindowEvent event)
	{
		//comprobamos si se está transmitiendo o recibiendo algo
		//en caso afirmativo lo detenemos
		if (iniciado)
			detenerTransmisionAudio();
		if (recibiendo)
			detenerRecepcionAudio();
		if (iniciadoVideo)
			detenerTransmisionVideo();
		if (recibiendoVideo)
			detenerRecepcionVideo();
		//actualizamos la configuración a partir de los cuadros de texto
		config.destino().ipLocal(ipLocal.getText().trim());
		config.destino().puertoAudioLocal(puertoLocal.getText().trim());
		config.destino().puertoVideoLocal(puertoVideoLocal.getText().trim());
		config.destino().ipRemota(ipRemota.getText().trim());
		config.destino().puertoAudioRemoto(puertoRemoto.getText().trim());
		config.destino().puertoVideoRemoto(puertoVideoRemoto.getText().trim());
		//guardamos la configuración en disco
		System.out.println(Misc.ind() + "Guardando configuracion...");
		Misc.aumentarInd();
		config.write();
		Misc.reducirInd();
		System.out.println(Misc.ind() + "Configuracion guardada...");
		System.exit( 0);
	}

	//métodos no utilizados
	public void windowClosed(WindowEvent event) {}
	public void windowDeiconified(WindowEvent event) {}
	public void windowIconified(WindowEvent event) {}
	public void windowActivated(WindowEvent event) {}
	public void windowDeactivated(WindowEvent event) {}
	public void windowOpened(WindowEvent event) {}
	public void keyPressed(KeyEvent event) {}
	public void keyTyped(KeyEvent event) {}
	public void mousePressed(MouseEvent e) {}
	public void mouseReleased(MouseEvent e) {}
	public void mouseEntered(MouseEvent e) {}
	public void mouseExited(MouseEvent e) {}

	//método que detecta la pulsación de una tecla
	public void keyReleased(KeyEvent event)
	{
		//llamamos al método que gestiona esto
		seleccion(event.getSource());
	}

	//método que detecta un click del raton
	public void mouseClicked(MouseEvent e)
	{
		//llamamos al método que gestiona esto
		seleccion(e.getSource());
	}

	//método que gestiona la pulsación de una tecla y el click del raton
	private void seleccion(Object source)
	{
		//si el evento proviene de la lista de dispositivos de audio
		if(source == listaAudioDevice)
		{
			//capturamos el índice
			int index = listaAudioDevice.getSelectedIndex();

			//si hay elementos, actualizamos el dispositivo de captura de audio
			if(index != -1)
			{
				System.out.println(Misc.ind() + "Actualizando el dispositivo de captura de audio...");
				captureAudioDevice = (CaptureDeviceInfo)deviceList.elementAt(index);
				Misc.aumentarInd();
				System.out.println(Misc.ind() + "Dispositivo de captura de audio cambiado a " + captureAudioDevice.getName());
				Misc.reducirInd();
			}
		}
		//si el evento proviene de la lista de dispositivos de video
		else if(source == listaVideoDevice)
		{
			//capturamos el índice
			int index = listaVideoDevice.getSelectedIndex();

			//si hay elementos, actualizamos el dispositivo de captura de video
			if(index != -1)
			{
				System.out.println(Misc.ind() + "Actualizando el dispositivo de captura de video...");
				captureVideoDevice = (CaptureDeviceInfo)deviceVideoList.elementAt(index);
				Misc.aumentarInd();
				System.out.println(Misc.ind() + "Dispositivo de captura de video cambiado a " + captureVideoDevice.getName());
				Misc.reducirInd();
			}
		}
	}
}
