/*
 * Copyright (C) 2005 Luca Veltri - University of Parma - Italy
 * 
 * This source code is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This source code 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 General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this source code; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * 
 * Author(s):
 * Luca Veltri (luca.veltri@unipr.it)
 */

package local.media;


import local.net.RtpPacket;
import local.net.RtpSocket;

import java.io.*;
import java.net.InetAddress;
import java.net.DatagramSocket;


/** RtpStreamReceiver is a generic stream receiver.
  * It receives packets from RTP and writes them into an OutputStream.
  */
public class RtpStreamTranslator extends Thread
{

   /** Whether debug mode */
   private static final boolean DEBUG=true;

   /** Size of the read buffer */
   private static final int BUFFER_SIZE=32768;

   /** Maximum blocking time, spent waiting for reading new bytes [milliseconds] */
   private static final int SO_TIMEOUT=200;

   /** The OutputStream */
   OutputStream output_stream=null;

   /** The input RtpSocket */
   RtpSocket rtp_socket_in=null;

   /** The input RtpSocket */
   RtpSocket rtp_socket_out=null;

   /** Whether the receive socket has been created here */
   boolean socket_in_is_local=false;

   /** Whether the send socket has been created here */
   boolean socket_out_is_local=false;

   /** Whether it is running */
   boolean running=false;


   /** Constructs a RtpStreamTranslator.
     * @param recv_port the local receiver port
     * @param dest_addr the destination address
     * @param dest_port the thestination port */
   public RtpStreamTranslator(int recv_port, String dest_addr, int dest_port)
   {  try
      {  DatagramSocket recv_socket=new DatagramSocket(recv_port);
         socket_in_is_local=true;
         init(recv_socket,null,dest_addr,dest_port);
      }
      catch (Exception e) {  e.printStackTrace();  }
   }

   /** Constructs a RtpStreamTranslator.
     * @param socket_in the local receiver socket
     * @param socket_out the socket used to send the RTP packet
     * @param dest_addr the destination address
     * @param dest_port the thestination port */
   public RtpStreamTranslator(DatagramSocket socket_in, DatagramSocket socket_out, String dest_addr, int dest_port)
   {  init(socket_in,socket_out,dest_addr,dest_port);
   }

   /** Inits the RtpStreamTranslator */
   private void init(DatagramSocket socket_in, DatagramSocket socket_out, String dest_addr, int dest_port)
   {  
      try
      {  if (socket_out==null)
         {  socket_out=new DatagramSocket();
            socket_out_is_local=true;
         }
         rtp_socket_in=new RtpSocket(socket_in);
         rtp_socket_out=new RtpSocket(socket_out,InetAddress.getByName(dest_addr),dest_port);
      }
      catch (Exception e) {  e.printStackTrace();  }    
   }


   /** Whether is running */
   public boolean isRunning()
   {  return running;
   }

   /** Stops running */
   public void halt()
   {  running=false;
   }

   /** Runs it in a new Thread. */
   public void run()
   {
      if (rtp_socket_in==null || rtp_socket_out==null)
      {  if (DEBUG) println("ERROR: RTP socket_in or socket_out is null");
         return;
      }
      //else

      byte[] buffer_in=new byte[BUFFER_SIZE];
      RtpPacket rtp_packet_in=new RtpPacket(buffer_in,0);
 
      byte[] buffer_out=new byte[BUFFER_SIZE];
      RtpPacket rtp_packet_out=new RtpPacket(buffer_out,0);
      //rtp_packet_out.setPayloadType(p_type);      

      if (DEBUG) println("Reading blocks of max "+BUFFER_SIZE+" bytes");

      //File file=new File("audio.wav");
      //javax.sound.sampled.AudioInputStream audio_input_stream=null;
      //try {  audio_input_stream=javax.sound.sampled.AudioSystem.getAudioInputStream(file);  } catch (Exception e) {  e.printStackTrace();  }

      running=true;
      try
      {  rtp_socket_in.getDatagramSocket().setSoTimeout(SO_TIMEOUT);
         while (running)
         {  try
            {  // read a block of data from the rtp_socket_in
               rtp_socket_in.receive(rtp_packet_in);
               //if (DEBUG) System.out.print(".");
               
               // send the block to the rtp_socket_out (if still running..)
               if (running)
               {  byte[] pkt1=rtp_packet_in.getPacket();
                  int offset1=rtp_packet_in.getHeaderLength();
                  int len1=rtp_packet_in.getPayloadLength();

                  byte[] pkt2=rtp_packet_out.getPacket();
                  int offset2=rtp_packet_out.getHeaderLength();
                  int pos2=offset2;
                  
                  for (int i=0; i<len1; i++)
                  {  int linear=G711.ulaw2linear(pkt1[offset1+i]);
                     //aux[pos++]=(byte)(linear&0xFF);
                     //aux[pos++]=(byte)((linear&0xFF00)>>8);
                     //int linear2=G711.ulaw2linear(audio_input_stream.read());
                     //linear+=linear2;
                     pkt2[pos2++]=(byte)G711.linear2ulaw(linear);
                  }             
                  rtp_packet_out.setPayloadType(rtp_packet_in.getPayloadType());
                  rtp_packet_out.setSequenceNumber(rtp_packet_in.getSequenceNumber());
                  rtp_packet_out.setTimestamp(rtp_packet_in.getTimestamp());
                  rtp_packet_out.setPayloadLength(pos2-offset2);
                  rtp_socket_out.send(rtp_packet_out);
               }

            }
            catch (java.io.InterruptedIOException e) { }
         }
      }
      catch (Exception e) {  running=false; e.printStackTrace();  }

      // close RtpSocket and local DatagramSocket
      DatagramSocket socket_in=rtp_socket_in.getDatagramSocket();
      rtp_socket_in.close();
      if (socket_in_is_local && socket_in!=null) socket_in.close();
      DatagramSocket socket_out=rtp_socket_out.getDatagramSocket();
      rtp_socket_out.close();
      if (socket_out_is_local && socket_out!=null) socket_out.close();
      
      // free all
      rtp_socket_in=null;
      rtp_socket_out=null;
      
      if (DEBUG) println("rtp translator terminated");
   }


   /** Debug output */
   private static void println(String str)
   {  System.out.println("RtpStreamTranslator: "+str);
   }
   

   public static int byte2int(byte b)
   {  //return (b>=0)? b : -((b^0xFF)+1);
      //return (b>=0)? b : b+0x100; 
      return (b+0x100)%0x100;
   }


   public static int byte2int(byte b1, byte b2)
   {  return (((b1+0x100)%0x100)<<8)+(b2+0x100)%0x100; 
   }
   
   
   // ******************************* MAIN *******************************

   /** The main method. */
   public static void main(String[] args)
   {
      String daddr=null;
      int dport=0;
      int rport=0;

      boolean help=true;

      for (int i=0; i<args.length; i++)
      {
         if (args[i].equals("-h"))
         {  break;
         }
         if (i==0 && args.length>1)
         {  rport=Integer.parseInt(args[i++]);
            daddr=args[i++];
            dport=Integer.parseInt(args[i++]);
            help=false;
            continue;
         }

         // else, do:
         System.out.println("unrecognized param '"+args[i]+"'\n");
         help=true;
      }
              
      if (help)
      {  System.out.println("usage:\n  java RtpStreamTranslator <recv_port> <dest_addr> <dest_port> [options]");
         System.out.println("   options:");
         System.out.println("   -h               this help");
         System.exit(0);
      }         
            
      RtpStreamTranslator translator=new RtpStreamTranslator(rport,daddr,dport);
      translator.start();
   }

}


