package local.ua;


import local.media.AudioInput;
import local.media.AudioOutput;
import local.media.RtpStreamSender;
import local.media.RtpStreamReceiver;
import org.zoolu.sip.provider.SipStack;
import org.zoolu.tools.Log;
import org.zoolu.tools.LogLevel;

import java.net.DatagramSocket;
import javax.sound.sampled.AudioFormat;

// Test..
import local.media.ToneInputStream;
import java.io.InputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.AudioInputStream;


/** Audio launcher based on javax.sound  */
public class JAudioLauncher implements MediaLauncher
{  
   /** Event logger. */
   Log log=null;

   /** Payload type */
   int payload_type=0;
   /** Sample rate [bytes] */
   int sample_rate=8000;
   /** Sample size [bytes] */
   int sample_size=1;
   /** Frame size [bytes] */
   int frame_size=500;
   /** Frame rate [frames per second] */
   int frame_rate=16; //=sample_rate/(frame_size/sample_size);
   AudioFormat.Encoding codec=AudioFormat.Encoding.ULAW;
   boolean signed=false; 
   boolean big_endian=false;

   //String filename="audio.wav"; 

   /** Test tone */
   public static final String TONE="TONE";

   /** Test tone frequency [Hz] */
   public static int tone_freq=100;
   /** Test tone ampliture (from 0.0 to 1.0) */
   public static double tone_amp=1.0;

   /** Runtime media process */
   Process media_process=null;
   
   int dir; // duplex= 0, recv-only= -1, send-only= +1; 

   DatagramSocket socket=null;
   RtpStreamSender sender=null;
   RtpStreamReceiver receiver=null;
   AudioInput audio_input=null;
   AudioOutput audio_output=null;
   
   ToneInputStream tone=null;
   
   /** Costructs the audio launcher */
   public JAudioLauncher(RtpStreamSender rtp_sender, RtpStreamReceiver rtp_receiver, Log logger)
   {  log=logger;
      sender=rtp_sender;
      receiver=rtp_receiver;
   }


   /** Costructs the audio launcher */
   public JAudioLauncher(int local_port, String remote_addr, int remote_port, int direction, Log logger)
   {  log=logger;
      try
      {  socket=new DatagramSocket(local_port);
         dir=direction;
         // sender
         if (dir>=0)
         {  printLog("new audio sender to "+remote_addr+":"+remote_port,LogLevel.MEDIUM);
            //audio_input=new AudioInput();
            AudioFormat format=new AudioFormat(codec,sample_rate,8*sample_size,1,sample_size,sample_rate,big_endian);
            audio_input=new AudioInput(format);
            //sender=new RtpStreamSender(audio_input.getInputStream(),false,payload_type,frame_rate,frame_size,socket,remote_addr,remote_port);
            sender=new RtpStreamSender(audio_input.getInputStream(),true,payload_type,frame_rate,frame_size,socket,remote_addr,remote_port);
            sender.setSyncAdj(2);
         }
         // receiver
         if (dir<=0)
         {  printLog("new audio receiver on "+local_port,LogLevel.MEDIUM);
            //audio_output=new AudioOutput();
            AudioFormat format=new AudioFormat(codec,sample_rate,8*sample_size,1,sample_size,sample_rate,big_endian);
            audio_output=new AudioOutput(format);
            receiver=new RtpStreamReceiver(audio_output.getOuputStream(),socket);
         }
      }
      catch (Exception e) {  printException(e,LogLevel.HIGH);  }
   }


   /** Costructs the audio launcher */
   public JAudioLauncher(int local_port, String remote_addr, int remote_port, int direction, String audiofile_in, String audiofile_out, int sample_rate, int sample_size, int frame_size, Log logger)
   {  log=logger;
      frame_rate=sample_rate/frame_size;
      try
      {  socket=new DatagramSocket(local_port);
         dir=direction;
         // sender
         if (dir>=0 && audiofile_in!=null && audiofile_in.equals(JAudioLauncher.TONE))
         {  printLog("new audio sender to "+remote_addr+":"+remote_port,LogLevel.MEDIUM);
            printLog("Tone generator: "+tone_freq+" Hz");
            tone=new ToneInputStream(tone_freq,tone_amp,sample_rate,sample_size,ToneInputStream.PCM_LINEAR_UNSIGNED,big_endian);
            sender=new RtpStreamSender(tone,true,payload_type,frame_rate,frame_size,socket,remote_addr,remote_port);
         }
         else
         if (dir>=0 && audiofile_in!=null)
         {  printLog("new audio sender to "+remote_addr+":"+remote_port,LogLevel.MEDIUM);
            File file=new File(audiofile_in);
            if (audiofile_in.indexOf(".wav")>0)
            {  AudioFileFormat format=AudioSystem.getAudioFileFormat(file);
               printLog("File audio format: "+format);
               AudioInputStream input_stream=AudioSystem.getAudioInputStream(file);
               sender=new RtpStreamSender(input_stream,true,payload_type,frame_rate,frame_size,socket,remote_addr,remote_port);
            }
            else
            {  FileInputStream input_stream=new FileInputStream(file);
               sender=new RtpStreamSender(input_stream,true,payload_type,frame_rate,frame_size,socket,remote_addr,remote_port);
            }
         }
         else
         if (dir>=0)
         {  printLog("new audio sender to "+remote_addr+":"+remote_port,LogLevel.MEDIUM);
            //audio_input=new AudioInput();
            AudioFormat format=new AudioFormat(codec,sample_rate,8*sample_size,1,sample_size,sample_rate,big_endian);
            audio_input=new AudioInput(format);
            //sender=new RtpStreamSender(audio_input.getInputStream(),false,payload_type,frame_rate,frame_size,socket,remote_addr,remote_port);
            sender=new RtpStreamSender(audio_input.getInputStream(),true,payload_type,frame_rate,frame_size,socket,remote_addr,remote_port);
            sender.setSyncAdj(2);
         }
         
         // receiver
         if (dir<=0 && audiofile_out!=null)
         {  printLog("new audio receiver on "+local_port,LogLevel.MEDIUM);
            File file=new File(audiofile_out);
            FileOutputStream output_stream=new FileOutputStream(file);
            receiver=new RtpStreamReceiver(output_stream,socket);
         }
         else
         if (dir<=0)
         {  printLog("new audio receiver on "+local_port,LogLevel.MEDIUM);
            //audio_output=new AudioOutput();
            AudioFormat format=new AudioFormat(codec,sample_rate,8*sample_size,1,sample_size,sample_rate,big_endian);
            audio_output=new AudioOutput(format);
            receiver=new RtpStreamReceiver(audio_output.getOuputStream(),socket);
         }
      }
      catch (Exception e) {  printException(e,LogLevel.HIGH);  }
   }


   /** Starts media application */
   public boolean startMedia()
   {  printLog("starting java audio..",LogLevel.HIGH);

      if (sender!=null)
      {  printLog("start sending",LogLevel.LOW);
         sender.start();
         if (audio_input!=null) audio_input.play();
      }
      if (receiver!=null)
      {  printLog("start receiving",LogLevel.LOW);
         receiver.start();
         if (audio_output!=null) audio_output.play();
      }
      
      return true;      
   }


   /** Stops media application */
   public boolean stopMedia()
   {  printLog("halting java audio..",LogLevel.HIGH);    
      if (sender!=null)
      {  sender.halt(); sender=null;
         printLog("sender halted",LogLevel.LOW);
      }      
      if (audio_input!=null)
      {  audio_input.stop(); audio_output=null;
      }      
      if (receiver!=null)
      {  receiver.halt(); receiver=null;
         printLog("receiver halted",LogLevel.LOW);
      }      
      if (audio_output!=null)
      {  audio_output.stop(); audio_output=null;
      }
      // take into account the resilience of RtpStreamSender
      // (NOTE: it does not take into acconunt the resilience of RtpStreamReceiver; this can cause SocketException)
      try { Thread.sleep(RtpStreamReceiver.SO_TIMEOUT); } catch (Exception e) {}
      socket.close();
      return true;
   }



   // ****************************** Logs *****************************

   /** Adds a new string to the default Log */
   private void printLog(String str)
   {  printLog(str,LogLevel.HIGH);
   }


   /** Adds a new string to the default Log */
   private void printLog(String str, int level)
   {  if (log!=null) log.println("AudioLauncher: "+str,level+SipStack.LOG_LEVEL_UA);  
      if (level<=LogLevel.HIGH) System.out.println("AudioLauncher: "+str);
   }

   /** Adds the Exception message to the default Log */
   void printException(Exception e,int level)
   {  if (log!=null) log.printException(e,level+SipStack.LOG_LEVEL_UA);
      if (level<=LogLevel.HIGH) e.printStackTrace();
   }

}