package client;

import java.io.*;
import java.awt.*;
import java.net.*;
import java.awt.event.*;
import java.util.Vector;

import javax.media.*;
import javax.media.rtp.*;
import javax.media.rtp.event.*;
import javax.media.rtp.rtcp.*;
import javax.media.protocol.*;
import javax.media.protocol.DataSource;
import javax.media.format.AudioFormat;
import javax.media.format.VideoFormat;
import javax.media.Format;
import javax.media.format.FormatChangeEvent;
import javax.media.control.BufferControl;
import javax.swing.*;

/**
 * AVReceive3 to receive RTP transmission using the RTPConnector.
 */

public class AVReceive extends Thread 
	implements ReceiveStreamListener, SessionListener, ControllerListener
{
    String sessions[] = null;
    RTPManager mgrs[] = null;
    Vector playerWindows = null;

    boolean dataReceived = false;
    Object dataSync = new Object();
    
    AVReceive media = null;
//    boolean isVideo = false;
    boolean isAV = false;	//只需要在video那邊設
    int type = 0;	//0:video, 1:audio + wav, 3:movie

    public AVReceive( String sessions[], int type )
    {
    	this.type = type;
	    this.sessions = sessions;
    }

    protected boolean initialize()
    {
        try{
        	mgrs = new RTPManager[ sessions.length ];
    	    playerWindows = new Vector();
    	    SessionLabel session;

		    // Open the RTP sessions.
		    for( int i = 0; i < sessions.length; i++ )
		    {
		    	// Parse the session addresses.
		    	try{
			    session = new SessionLabel( sessions[i] );
    			} catch( IllegalArgumentException e ){
    				System.err.println( "Failed to parse the session address given: " + sessions[i] );
    				return false;
    			}

    			System.err.println( "  - Open RTP session for: addr: " + session.addr + " port: " + session.port + " ttl: " + session.ttl );

    			mgrs[i] = ( RTPManager )RTPManager.newInstance();
    			mgrs[i].addSessionListener( this );
    			mgrs[i].addReceiveStreamListener( this );

    			// Initialize the RTPManager with the RTPSocketAdapter
    			mgrs[i].initialize( new RTPSocketAdapter(
					InetAddress.getByName( session.addr ), 
					session.port, session.ttl ) );

    			// You can try out some other buffer size to see
    			// if you can get better smoothness.
    			BufferControl bc = ( BufferControl )mgrs[i].getControl( "javax.media.control.BufferControl" );
    			if ( bc != null )
    				bc.setBufferLength( 350 );
		    }
        } catch( Exception e ){
            System.err.println( "Cannot create the RTP Session: " + e.getMessage() );
            return false;
        }

        // Wait for data to arrive before moving on.
        long then = System.currentTimeMillis();
        long waitingPeriod = 30000;    // Wait for a maximum of 30 secs.

        try{
        	synchronized ( dataSync )
			{
        		while ( !dataReceived && System.currentTimeMillis() - then < waitingPeriod )
        		{
        			if( !dataReceived )
        				System.err.println( "  - Waiting for RTP data to arrive..." );
        			dataSync.wait( 1000 );
        		}
			}
        } catch( Exception e ){}

        if( !dataReceived )
        {
        	System.err.println( "No RTP data was received." );
        	close();
        	return false;
        }
        return true;
    }

    public boolean isDone()
    {
    	return playerWindows.size() == 0;
    }

    /**
     * Close the players and the session managers.
     */
    protected void close()
    {
    	for( int i = 0; i < playerWindows.size(); i++ )
    	{
    		try{
    			( ( PlayerWindow )playerWindows.elementAt(i) ).close();
    		} catch( Exception e ){}
    	}
    	playerWindows.removeAllElements();

    	// close the RTP session.
    	for( int i = 0; i < mgrs.length; i++ )
    	{
    		if( mgrs[i] != null )
    		{
                mgrs[i].removeTargets( "Closing session from AVReceive3" );
                mgrs[i].dispose();
                mgrs[i] = null;
    		}
    	}
    }

    PlayerWindow find( Player p )
    {
    	for( int i = 0; i < playerWindows.size(); i++ )
    	{
    		PlayerWindow pw = ( PlayerWindow )playerWindows.elementAt(i);
    		if( pw.player == p )
    			return pw;
    	}
    	return null;
    }

    PlayerWindow find( ReceiveStream strm )
    {
    	for( int i = 0; i < playerWindows.size(); i++ )
    	{
    		PlayerWindow pw = ( PlayerWindow )playerWindows.elementAt(i);
    		if( pw.stream == strm )
    			return pw;
    	}
    	return null;
    }

    /**
     * SessionListener.
     */
    public synchronized void update( SessionEvent evt )
    {
    	if( evt instanceof NewParticipantEvent )
    	{
    		Participant p = ( ( NewParticipantEvent )evt ).getParticipant();
    		System.err.println( "  - A new participant had just joined: " + p.getCNAME() );
    	}
    }

    /**
     * ReceiveStreamListener
     */
    public synchronized void update( ReceiveStreamEvent evt )
    {
    	RTPManager mgr = ( RTPManager )evt.getSource();
    	Participant participant = evt.getParticipant();	// could be null.
    	ReceiveStream stream = evt.getReceiveStream();  // could be null.

    	if( evt instanceof RemotePayloadChangeEvent )
    	{
    		System.err.println( "  - Received an RTP PayloadChangeEvent." );
    		System.err.println( "Sorry, cannot handle payload change." );
//    		System.exit(0);
			JOptionPane.showMessageDialog( null,
				"Received an RTP PayloadChangeEvent.\nSorry, cannot handle payload change." );
			return;
    	}
    	else if( evt instanceof NewReceiveStreamEvent )
    	{
    		try{
    			stream = ( ( NewReceiveStreamEvent )evt ).getReceiveStream();
    			DataSource ds = Manager.createCloneableDataSource(
    					stream.getDataSource() );

    			// Find out the formats.
    			RTPControl ctl = ( RTPControl )ds.getControl( "javax.media.rtp.RTPControl" );
    			if( ctl != null )
    			{
    				System.err.println( "  - Recevied new RTP stream: " + ctl.getFormat() );
    			}
    			else
    				System.err.println( "  - Recevied new RTP stream" );
    			
    			if( participant == null )
    				System.err.println( "      The sender of this stream had yet to be identified." );
    			else
    			{
    				System.err.println(	"      The stream comes from: " + participant.getCNAME() ); 
    			}
    			
    			// create a player by passing datasource to the Media Manager
    			Player p = javax.media.Manager.createPlayer( ds );
    			if( p == null )
    				return;
    			p.addControllerListener( this );
    			p.realize();
    			PlayerWindow pw = new PlayerWindow( p, stream, ds );
    			playerWindows.addElement( pw );

    			// Notify intialize() that a new stream had arrived.
    			synchronized ( dataSync )
				{
    				dataReceived = true;
    				dataSync.notifyAll();
				}
    		} catch( Exception e )
			{
    			System.err.println( "NewReceiveStreamEvent exception " + e.getMessage() );
    			return;
			}
    	}
    	else if( evt instanceof StreamMappedEvent )
    	{
    		if( stream != null && stream.getDataSource() != null )
    		{
    			DataSource ds = stream.getDataSource();
    			// Find out the formats.
    			RTPControl ctl = ( RTPControl )ds.getControl( "javax.media.rtp.RTPControl" );
    			System.err.println( "  - The previously unidentified stream " );
    			if( ctl != null )
    				System.err.println(	"      " + ctl.getFormat() );
    			System.err.println( "      had now been identified as sent by: " + participant.getCNAME() );
    		}
    	}
    	else if( evt instanceof ByeEvent )
    	{
    		System.err.println( "  - Got \"bye\" from: " + participant.getCNAME() );
    		PlayerWindow pw = find( stream );
    		if( pw != null )
    		{
    			pw.close();
    			playerWindows.removeElement( pw );
    		}
    	}
    }

    /**
     * ControllerListener for the Players.
     */
    public synchronized void controllerUpdate( ControllerEvent ce )
    {
    	Player p = ( Player )ce.getSourceController();
    	if( p == null )
    		return;

    	// Get this when the internal players are realized.
    	if( ce instanceof RealizeCompleteEvent )
    	{
    		PlayerWindow pw = find( p );
    		if( pw == null )
    		{
    			// Some strange happened.
    			System.err.println( "Internal error!" );
//    			System.exit( -1 );
    			JOptionPane.showMessageDialog( null, "Internal error!" );
    			return;
    		}
    		pw.initialize();
    		pw.setVisible( true );
    		p.start();
    	}

    	if( ce instanceof ControllerErrorEvent )
    	{
    		p.removeControllerListener( this );
    		PlayerWindow pw = find( p );
    		if( pw != null )
    		{
    			pw.close();	
    			playerWindows.removeElement( pw );
    		}
    		System.err.println( "AVReceive3 internal error: " + ce );
    	}
    }

    /**
     * A utility class to parse the session addresses.
     */
    class SessionLabel
	{
    	public String addr = null;
    	public int port;
    	public int ttl = 1;

    	SessionLabel( String session )throws IllegalArgumentException
		{
    		int off;
    		String portStr = null, ttlStr = null;

    		if( session != null && session.length() > 0 )
    		{
    			while( session.length() > 1 && session.charAt(0) == '/' )
    				session = session.substring(1);

    			// Now see if there's a addr specified.
    			off = session.indexOf( '/' );
    			if( off == -1 )
    			{
    				if( !session.equals("") )
    					addr = session;
    			}
    			else
    			{
    				addr = session.substring( 0, off );
    				session = session.substring( off + 1 );
    				// Now see if there's a port specified
    				off = session.indexOf( '/' );
    				if( off == -1 )
    				{
    					if( !session.equals("") )
    						portStr = session;
    				}
    				else
    				{
    					portStr = session.substring( 0, off );
    					session = session.substring( off + 1 );
    					// Now see if there's a ttl specified
    					off = session.indexOf( '/' );
    					if( off == -1 )
    					{
    						if( !session.equals("") )
    							ttlStr = session;
    					}
    					else
    					{
    						ttlStr = session.substring( 0, off );
    					}
    				}
    			}
    		}

    		if( addr == null )
    			throw new IllegalArgumentException();

    		if( portStr != null )
    		{
    			try{
    				Integer integer = Integer.valueOf( portStr );
    				if( integer != null )
    					port = integer.intValue();
    			} catch( Throwable t ){
    				throw new IllegalArgumentException();
    			}
    		}
    		else
    			throw new IllegalArgumentException();

    		if( ttlStr != null )
    		{
    			try{
    				Integer integer = Integer.valueOf( ttlStr );
    				if( integer != null )
    					ttl = integer.intValue();
    			} catch( Throwable t ){
    				throw new IllegalArgumentException();
    			}
    		}
		}
    }

    /**
     * GUI classes for the Player.
     */
    class PlayerWindow extends JFrame implements ActionListener
	{
    	Player player;
		Processor p;
    	ReceiveStream stream;
    	DataSource recSource;
    	DataSink filewriter;
    	FileTypeDescriptor ftype;
		MediaLocator dest;
    	
    	Container c;
    	JPanel recPanel;
    	JButton recV, recA, recM, stopRec;
    	boolean isVideo = false;

    	PlayerWindow( Player p, ReceiveStream strm, DataSource d )
		{
    		player = p;
    		stream = strm;
    		recSource = ( ( SourceCloneable )d ).createClone();
    		addWindowListener(
				new WindowAdapter(){
						public void windowClosing( WindowEvent e ){
							close();
			}});
		}

    	public void initialize()
    	{
    		c = getContentPane();
    		c.setLayout( new BorderLayout() );
    		c.add( new PlayerPanel( player ), BorderLayout.CENTER );
    		
    		recPanel = new JPanel();
    		recPanel.setLayout( new GridLayout( 1, 3 ));
    		recV = new JButton( "錄影" );
    		recA = new JButton( "錄音" );
//    		recM = new JButton( "錄影音" );
    		stopRec = new JButton( "停止" );
    		recV.addActionListener( this );
    		recA.addActionListener( this );
//    		recM.addActionListener( this );
    		stopRec.addActionListener( this );
    		recPanel.add( recV );
    		recPanel.add( recA );
//    		recPanel.add( recM );
    		recPanel.add( stopRec );
    		c.add( recPanel, BorderLayout.SOUTH );
    		if( type == 2 )
    		{
    			recV.setEnabled( false );
        		recA.setEnabled( false );
        		stopRec.setEnabled( false );
//        		recM.setEnabled( false );
        		setVisible( true );
        		return;
    		}
    		if( player.getVisualComponent() != null )
    			isVideo = true;
    		else
    			isVideo = false;
    			
    		recV.setEnabled( isVideo );
    		recA.setEnabled( !isVideo );
    		stopRec.setEnabled( false );
//    		recM.setEnabled( false );
    		setVisible( true );
    	}

    	public void close()
    	{
    		stopRecord();
    		player.close();
    		setVisible( false );
    		dispose();
    	}

    	public void addNotify()
    	{
    		super.addNotify();
    		pack();
    	}

		/* (non-Javadoc)
		 * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
		 */
		public void actionPerformed( ActionEvent e )
		{
			if( e.getSource() == recV )
			{
				recV.setEnabled( false );
				recA.setEnabled( false );
//				recM.setEnabled( false );
				stopRec.setEnabled( true );
				ftype = new FileTypeDescriptor(
						FileTypeDescriptor.MSVIDEO );
				dest = new MediaLocator( "file:///receive_media\\foo1.avi" );
				record();
			}
			else if( e.getSource() == recA )
			{
				recV.setEnabled( false );
				recA.setEnabled( false );
//				recM.setEnabled( false );
				stopRec.setEnabled( true );
				ftype = new FileTypeDescriptor(
						FileTypeDescriptor.WAVE );
				dest = new MediaLocator( "file:///receive_media\\foo1.wav" );
				record();
			}
//			else if( e.getSource() == recM )
//			{
//				recV.setEnabled( false );
//				recA.setEnabled( false );
////				recM.setEnabled( false );
//				stopRec.setEnabled( true );
//			}
			else if( e.getSource() == stopRec )
			{
				stopRec.setEnabled( false );
				recV.setEnabled( isVideo );
				recA.setEnabled( !isVideo );
				stopRecord();
			}
		}
		
		public void stopRecord()
		{
			if( p != null )
			{
				p.stop();
				p.close();
			}
			if( filewriter != null )
			{
				try {
					filewriter.stop();
				} catch (IOException e1) {
					e1.printStackTrace();
				}
				filewriter.close();
			}
		}
		
		public void record()
		{
			try {
				p = Manager.createProcessor( recSource );
				
//				System.out.println("before");
				p.configure();
				
				while( p.getState() != Processor.Configured );
//				if( p.getState() == Processor.Configured )
//					System.out.println("after");
				p.setContentDescriptor( ftype );
				p.realize();
				
				while( p.getState() != Processor.Realized );

				p.start();
				filewriter = Manager.createDataSink( p.getDataOutput(), dest );
				filewriter.open();
				filewriter.start();
				
			} catch (NoProcessorException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			} catch (NoDataSinkException e) {
				e.printStackTrace();
			}
		}
	}

    /**
     * GUI classes for the Player.
     */
    class PlayerPanel extends Panel
	{
    	Component vc, cc;
    	PlayerPanel( Player p )
		{
    		setLayout( new BorderLayout() );
    		if( ( vc = p.getVisualComponent() ) != null )
    			add( "Center", vc );
    		if( ( cc = p.getControlPanelComponent() ) != null )
    			add( "South", cc );
		}

    	public Dimension getPreferredSize()
    	{
    		int w = 0, h = 0;
    		if( vc != null )
    		{
    			Dimension size = vc.getPreferredSize();
    			w = size.width;
    			h = size.height;
    		}
    		if( cc != null )
    		{
    			Dimension size = cc.getPreferredSize();
    			if( w == 0 )
    				w = size.width;
    			h += size.height;
    		}
    		if( w < 160 )
    			w = 160;
    		return new Dimension( w, h );
    	}
    }

    public void run()
    {
    	if( !initialize() )
    	{
    	    System.err.println( "Failed to initialize the sessions." );
//    	    System.exit(-1);
    	    return;
    	}

    	// Check to see if AVReceive3 is done.
    	try{
    	    while( !isDone() )
    		Thread.sleep( 1000 );
    	} catch( Exception e ){}

    	System.err.println( "Exiting AVReceive3" );
    }
}
//    public static void main(String argv[]) {
//	if (argv.length == 0)
//	    prUsage();
//
//   	AVReceive3 avReceive = new AVReceive3(argv);
//	if (!avReceive.initialize()) {
//	    System.err.println("Failed to initialize the sessions.");
//	    System.exit(-1);
//	}
//
//	// Check to see if AVReceive3 is done.
//	try {
//	    while (!avReceive.isDone())
//		Thread.sleep(1000);
//	} catch (Exception e) {}
//
//	System.err.println("Exiting AVReceive3");
//    }
//
//
//    static void prUsage() {
//	System.err.println("Usage: AVReceive3 <session> <session> ...");
//	System.err.println("     <session>: <address>/<port>/<ttl>");
//	System.exit(0);
//    }

// end of AVReceive3 
