/*
 *  Copyright (C) 2006  Takashi Kasuya <kasuya@sfc.keio.ac.jp>
 *
 * This library is free software; you can redistribute it and/or
 *　modify it under the terms of the GNU Lesser General Public
 *　License as published by the Free Software Foundation; either
 *　version 2.1 of the License, or (at your option) any later version.
 *　This library is distributed in the hope that it will be useful,
 *　but WITHOUT ANY WARRANTY; without even the implied warranty of
 *　MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *　Lesser General Public License for more details.
 *
 *　You should have received a copy of the GNU Lesser General Public
 *　License along with this library; if not, write to the Free Software
 *　Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */


package jp.ac.naka.ec.media;

import java.awt.Component;
import java.io.IOException;
import java.util.Vector;

import javax.media.CaptureDeviceInfo;
import javax.media.CaptureDeviceManager;
import javax.media.ControllerEvent;
import javax.media.ControllerListener;
import javax.media.DataSink;
import javax.media.Manager;
import javax.media.MediaException;
import javax.media.MediaLocator;
import javax.media.NoProcessorException;
import javax.media.NotRealizedError;
import javax.media.Processor;
import javax.media.control.FormatControl;
import javax.media.control.TrackControl;
import javax.media.format.AudioFormat;
import javax.media.protocol.ContentDescriptor;
import javax.media.protocol.DataSource;
import javax.swing.JFrame;


/**
 * RTPによる音声送信を行う。
 * @author Takashi Kasuya
 *
 */
public class AudioTransmitter {

	private String url;
	private String media = null;
	private int port;
	private DataSink d;
	private AudioFormat format;
	public static int rtp_bit = 8;

	private Processor p = null;

	private long waiting_time = 10000;

	/**
	 * コンストラクタ
	 * 
	 * @param dist_url
	 * @param port
	 * @throws MediaException
	 */
	public AudioTransmitter(String dist_url, int port) throws MediaException {
		this(null, dist_url, port, new AudioFormat(AudioFormat.GSM_RTP, 8000,
				8, 1));
	}

	/**
	 * 
	 * @param dist_url
	 * @param port
	 * @param format
	 * @throws MediaException
	 */
	public AudioTransmitter(String dist_url, int port, AudioFormat format)
			throws MediaException {
		this(null, dist_url, port, format);
	}

	/**
	 * 
	 * @param media
	 * @param dist_url
	 * @param port
	 * @throws MediaException
	 */
	public AudioTransmitter(String media, String dist_url, int port)
			throws MediaException {
		this(media, dist_url, port, new AudioFormat(AudioFormat.GSM_RTP, 8000,
				8, 1));
	}

	/**
	 * 
	 * @param media
	 * @param dist_url
	 * @param port
	 * @param format
	 * @throws MediaException
	 */
	public AudioTransmitter(String media, String dist_url, int port,
			AudioFormat format) throws MediaException {
		this.url = dist_url;
		this.port = port;
		if (media != null || media != "")
			this.media = media;
		this.format = format;
		init();
	}

	private void init() throws MediaException {
		MediaLocator locator = null;
		if (media == null) {
			// ここは決めうち　（Macだとだめっぽい）
			AudioFormat format = new AudioFormat(AudioFormat.LINEAR, 8000, 8, 1);
			Vector devices = CaptureDeviceManager.getDeviceList(format);
			CaptureDeviceInfo di = null;

			if (!devices.isEmpty())
				di = (CaptureDeviceInfo) devices.elementAt(0);
			else
				throw new MediaException("There is no available CaptureDevice.");
			locator = di.getLocator();
		} else {
			locator = new MediaLocator(media);
		}
		try {
			p = Manager.createProcessor(locator);
		} catch (IOException e) {
			System.exit(-1);
		} catch (NoProcessorException e) {
			System.exit(-1);
		}
		WaitingListener listener = new WaitingListener();
		p.addControllerListener(listener);
		p.configure();
		synchronized (listener) {
			listener.waitForEvent(Processor.Configured);
		}

		p.setContentDescriptor(new ContentDescriptor(ContentDescriptor.RAW));
		TrackControl track[] = p.getTrackControls();
		boolean encodingOk = false;

		for (int i = 0; i < track.length; i++) {
			if (!encodingOk && track[i] instanceof FormatControl) {
				FormatControl control = (FormatControl) track[i];
				if (control.setFormat(format) == null)
					track[i].setEnabled(false);
				else
					encodingOk = true;
			} else {
				track[i].setEnabled(false);
			}
		}

		if (encodingOk) {
			p.realize();
			synchronized (listener) {
				listener.waitForEvent(Processor.Realized);
			}
			DataSource ds = null;
			try {
				ds = p.getDataOutput();
			} catch (NotRealizedError e) {
				e.printStackTrace();
				System.exit(-1);
			}

			try {
				MediaLocator ml = new MediaLocator("rtp://" + url + ":" + port
						+ "/audio/1");
				d = Manager.createDataSink(ds, ml);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

	}

	/**
	 * RTP送信の開始、自動再生
	 * 
	 * @throws SecurityException
	 * @throws IOException
	 */
	public void play() throws SecurityException, IOException {
		d.open();
		d.start();
		p.start();
	}

	/**
	 * 音声ストリーミングを終了
	 *
	 */
	public void close () {
		if (d == null || p == null) {
			throw new NullPointerException("まだ初期化されていません");
		}
		p.close();
		d.close();
	}
	
	/**
	 * ストリーミング中かどうか
	 * @return
	 */
	public boolean isPlaying() {
		if (p==null || d== null) {
			return false;
		}
		return (p.getState() == Processor.Started) ? true : false; 
	}
	
	/**
	 * 送信コントロールのためのControllerCOmponentの取得
	 * 
	 * @return
	 */
	public Component getControlPanelComponent() {
		if (p != null) {
			return p.getControlPanelComponent();
		} else {
			throw new NullPointerException("Playerが作られていません");
		}
	}

	class WaitingListener implements ControllerListener {

		Thread th;

		int state;

		int previous;

		public void waitForEvent(final int state) {
			this.state = state;
			th = new Thread() {
				public void run() {
					try {
						synchronized (this) {
							if (previous < state)
								wait();
						}
					} catch (InterruptedException e) {
						;
					}
				}
			};
			th.start();
			try {
				th.join();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}

		public void controllerUpdate(ControllerEvent evt) {
			// System.out.println(evt.toString());
			Processor p = (Processor) evt.getSourceController();
			//System.out.println(p.getState());
			previous = p.getState();
			if (th == null)
				return;
			if (p.getState() >= state) {
				synchronized (th) {
					th.notifyAll();
				}
			}

		}

	}

	public static void main(String[] args) throws Exception {
		try {
			AudioTransmitter at = new AudioTransmitter("192.168.0.11", 22224,
					new AudioFormat(AudioFormat.ULAW_RTP, 8000, 8, 1));

			at.play();
			JFrame jf = new JFrame();
			jf.add(at.getControlPanelComponent());
			jf.setVisible(true);
			jf.pack();
			jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
