/*
 *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

//TODO(hlundin): Reformat file to meet style guide.

/* header includes */
#include <float.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef WIN32
#include <winsock2.h>
#include <io.h>
#endif
#ifdef WEBRTC_LINUX
#include <netinet/in.h>
#endif

#include <assert.h>

#include "webrtc/test/gtest.h"
#include "webrtc/typedefs.h"

/*********************/
/* Misc. definitions */
/*********************/

#define FIRSTLINELEN 40
#define CHECK_NOT_NULL(a) if((a)==NULL){ \
    fprintf(stderr,"\n %s \n line: %d \nerror at %s\n",__FILE__,__LINE__,#a ); \
    return(-1);}

struct arr_time {
  float time;
  uint32_t ix;
};

int filelen(FILE *fid)
{
  fpos_t cur_pos;
  int len;

  if (!fid || fgetpos(fid, &cur_pos)) {
    return(-1);
  }

  fseek(fid, 0, SEEK_END);
  len = ftell(fid);

  fsetpos(fid, &cur_pos);

  return (len);
}

int compare_arr_time(const void *x, const void *y);

int main(int argc, char* argv[])
{
  unsigned int  dat_len, rtp_len, Npack, k;
  arr_time    *time_vec;
  char      firstline[FIRSTLINELEN];
  unsigned char* rtp_vec = NULL;
        unsigned char** packet_ptr = NULL;
        unsigned char* temp_packet = NULL;
  const unsigned int kRtpDumpHeaderSize = 4 + 4 + 4 + 2 + 2;
  uint16_t      len;
  uint32_t      *offset;

/* check number of parameters */
  if (argc != 4) {
    /* print help text and exit */
    printf("Apply jitter on RTP stream.\n");
    printf("Reads an RTP stream and packet timing from two files.\n");
    printf("The RTP stream is modified to have the same jitter as described in "
           "the timing files.\n");
    printf("The format of the RTP stream file should be the same as for \n");
    printf("rtpplay, and can be obtained e.g., from Ethereal by using\n");
    printf("Statistics -> RTP -> Show All Streams -> [select a stream] -> "
           "Save As\n\n");
    printf("Usage:\n\n");
    printf("%s RTP_infile dat_file RTP_outfile\n", argv[0]);
    printf("where:\n");

    printf("RTP_infile       : RTP stream input file\n\n");

    printf("dat_file         : file with packet arrival times in ms\n\n");

    printf("RTP_outfile      : RTP stream output file\n\n");

    return(0);
  }

  FILE* in_file=fopen(argv[1],"rb");
  CHECK_NOT_NULL(in_file);
  printf("Input file: %s\n",argv[1]);
  FILE* dat_file=fopen(argv[2],"rb");
  CHECK_NOT_NULL(dat_file);
  printf("Dat-file: %s\n",argv[2]);
  FILE* out_file=fopen(argv[3],"wb");
  CHECK_NOT_NULL(out_file);
  printf("Output file: %s\n\n",argv[3]);

  // add 1000 bytes to avoid (rare) strange error.
  time_vec = (arr_time *) malloc(sizeof(arr_time)
                                 *(filelen(dat_file)/sizeof(float)) + 1000);
  if (time_vec==NULL) {
    fprintf(stderr, "Error: could not allocate memory for reading dat file\n");
    goto closing;
  }

  dat_len=0;
  while(fread(&(time_vec[dat_len].time),sizeof(float),1,dat_file)>0) {
    time_vec[dat_len].ix=dat_len;
    dat_len++;
  }

  if (dat_len == 0) {
    fprintf(stderr, "Error: dat_file is empty, no arrival time is given.\n");
    goto closing;
  }

  qsort(time_vec,dat_len,sizeof(arr_time),compare_arr_time);


  rtp_vec = (unsigned char *) malloc(sizeof(unsigned char)*filelen(in_file));
  if (rtp_vec==NULL) {
    fprintf(stderr,"Error: could not allocate memory for reading rtp file\n");
    goto closing;
  }

  // read file header and write directly to output file
  EXPECT_TRUE(fgets(firstline, FIRSTLINELEN, in_file) != NULL);
  EXPECT_GT(fputs(firstline, out_file), 0);
  EXPECT_EQ(kRtpDumpHeaderSize, fread(firstline, 1, kRtpDumpHeaderSize,
                                      in_file));
  EXPECT_EQ(kRtpDumpHeaderSize, fwrite(firstline, 1, kRtpDumpHeaderSize,
                                       out_file));

  // read all RTP packets into vector
  rtp_len=0;
  Npack=0;

  // read length of first packet.
  len=(uint16_t) fread(&rtp_vec[rtp_len], sizeof(unsigned char), 2, in_file);
  while(len==2) {
    len = ntohs(*((uint16_t *)(rtp_vec + rtp_len)));
    rtp_len += 2;
    if(fread(&rtp_vec[rtp_len], sizeof(unsigned char),
             len-2, in_file)!=(unsigned) (len-2)) {
      fprintf(stderr,"Error: currupt packet length\n");
      goto closing;
    }
    rtp_len += len-2;
    Npack++;

    // read length of next packet.
    len=(uint16_t) fread(&rtp_vec[rtp_len], sizeof(unsigned char), 2, in_file);
  }

  if (Npack == 0) {
    fprintf(stderr, "Error: No RTP packet found.\n");
    goto closing;
  }

  packet_ptr = (unsigned char **) malloc(Npack*sizeof(unsigned char*));

  packet_ptr[0]=rtp_vec;
  k=1;
  while(k<Npack) {
    len = ntohs(*((uint16_t *) packet_ptr[k-1]));
    packet_ptr[k]=packet_ptr[k-1]+len;
    k++;
  }

  for(k=0; k<dat_len && k<Npack; k++) {
    if(time_vec[k].time < FLT_MAX && time_vec[k].ix < Npack){
      temp_packet = packet_ptr[time_vec[k].ix];
      offset = (uint32_t *) (temp_packet+4);
      if ( time_vec[k].time >= 0 ) {
        *offset = htonl((uint32_t) time_vec[k].time);
      }
      else {
        *offset = htonl((uint32_t) 0);
        fprintf(stderr, "Warning: negative receive time in dat file transformed"
                " to 0.\n");
      }

      // write packet to file
      if (fwrite(temp_packet, sizeof(unsigned char),
                 ntohs(*((uint16_t*) temp_packet)),
                 out_file) !=
          ntohs(*((uint16_t*) temp_packet))) {
        return -1;
      }
    }
  }


closing:
  free(time_vec);
  free(rtp_vec);
        if (packet_ptr != NULL) {
    free(packet_ptr);
        }
        fclose(in_file);
  fclose(dat_file);
  fclose(out_file);

  return(0);
}



int compare_arr_time(const void *xp, const void *yp) {

  if(((arr_time *)xp)->time == ((arr_time *)yp)->time)
    return(0);
  else if(((arr_time *)xp)->time > ((arr_time *)yp)->time)
    return(1);

  return(-1);
}
