/*
 * Copyright 1993 by the University of Pennsylvania
 *
 * Permission to use, copy, and distribute for non-commercial purposes,
 * is hereby granted without fee, providing that the above copyright
 * notice appear in all copies and that both the copyright notice and this
 * permission notice appear in supporting documentation.
 *
 * The software may be modified for your own purposes, but modified versions
 * may not be distributed.
 *
 * This software is provided "as is" without any expressed or implied warranty.
 */

#include <stdio.h>
#include <string.h>
#include <math.h>
#include <sys/types.h>
#include "global.h"
#include "errorhandle.h"
/*---------- general open, close function -----------*/
FILE*
  open_file(fname,status) 
char *fname;
char *status; /* write or read */
{
  FILE *fdstat = NULL;
  if((fdstat = fopen(fname, status))== NULL) {
#ifdef DEBUG
    fprintf(stderr,"File cannot be opened (%s).\n", fname);
#endif
    errnum = 2;
    strcpy(errmsg,fname);      
    return NULL;
  }
#ifdef DEBUG
  else {
    fprintf(stderr,"%s opened.\n", fname);
  }
#endif
  return fdstat;
}

int
  close_file(fdstat) 
FILE *fdstat;
{
  if(fdstat!=NULL) {
    fclose(fdstat);
  }
  return 0;
}
/*---------------- create stat information header ------------------*/
int
  write_stat_header(fdstat,M,N,quant,Beta,
                    start_frame, num_of_frame,height, width)
FILE *fdstat;
int M, N, quant, Beta;
int start_frame, num_of_frame, height, width;
{
  if(fdstat==NULL) return 0;
  fwrite("MPEGTool", sizeof(char), 8, fdstat);/* file identify header */
  fwrite(&N     , sizeof(int) , 1, fdstat);
  fwrite(&M     , sizeof(int) , 1, fdstat);
  fwrite(&quant , sizeof(int) , 1, fdstat);
  fwrite(&Beta  , sizeof(int) , 1, fdstat);
  fwrite(&start_frame  ,sizeof(int), 1, fdstat);
  fwrite(&num_of_frame ,sizeof(int), 1, fdstat);
  fwrite(&width , sizeof(int), 1, fdstat);
  fwrite(&height, sizeof(int), 1, fdstat);
  return 0;  
}

int
  write_stat(fdstat,FS)
FILE *fdstat;
FRAME_STAT *FS;
{
  int mb_num;
  if(fdstat==NULL) return 0;
  mb_num = FS->height/MBSIZE * FS->width/MBSIZE;
  fwrite(FS->block_mode,sizeof(char),mb_num,fdstat);
  fwrite(FS->bit_num,sizeof(short),mb_num,fdstat);
  if(FS->bit_numl!=NULL)
    fwrite(FS->bit_numl,sizeof(short),mb_num,fdstat);
  return 0;
}
/**********************************************************************
  read_stat read statistics data and pre-compute max, avg, std.dev.
 **********************************************************************/
STAT*
  read_stat(fdstat,scope,atm_mode,start_frame_num,
              stat_num, kind_of_stat, priority)
FILE* fdstat;
int  scope;      /* 0: per frame, 1: per slice, 2: per macro block */
int  atm_mode;   /* 0: bit mode,  1: ATM packet mode (44 byte/packet) */ 
int  start_frame_num; /* start FRAME number */
int  stat_num;        /* stat ITEM (e.q. frame, slice, mb) number */
int kind_of_stat;
int priority;
{
  int i,j,k,tmp,dctnum;
  int M,N;
  int v_mb,h_mb,mb_num; /* number of MB's (vertical, horizontal, total)*/
  int frame_num;        /* number of frames */
  int last_frame;
  int counter = 0;
  char idname[8];
  char LP;          /* 0: no LP data (datanum < 64), 1: exist LP data */
  FRAME_STAT FS;
  STAT* SD;
  if(fdstat==NULL) {
    errnum = 3;
    return NULL;
  }
  /*------ read header from file (8 integers) ------*/
  errnum = 4;
  if(fread(idname     ,sizeof(char),8,fdstat) == 0) {
    errnum = 58;
    return NULL;
  }
  if(strncmp(idname,"MPEGTool",8)) { /* file ID check */
#ifdef DEBUG
    rewind(fdstat);
    fprintf(stderr,"Might not MPEGTool Statistics file\n");
#else
    errnum = 55;
    return NULL;
#endif
  }
  if(fread(&N         ,sizeof(int),1,fdstat) == 0) return NULL;
  if(fread(&M         ,sizeof(int),1,fdstat) == 0) return NULL;
  if(fread(&tmp       ,sizeof(int),1,fdstat) == 0) return NULL;
  if(fread(&dctnum    ,sizeof(int),1,fdstat) == 0) return NULL;
  if(fread(&tmp       ,sizeof(int),1,fdstat) == 0) return NULL;
  if(fread(&last_frame,sizeof(int),1,fdstat) == 0) return NULL;
  if(fread(&FS.width  ,sizeof(int),1,fdstat) == 0) return NULL;
  if(fread(&FS.height ,sizeof(int),1,fdstat) == 0) return NULL;
  h_mb = FS.width /MBSIZE;
  v_mb = FS.height/MBSIZE;
  mb_num = v_mb * h_mb;
  if(kind_of_stat == 3) /* stat_num of int.arr is based on frame # */ 
    stat_num *= mb_num; 
  /*------ cancel if LP is requested but not exist ------*/
  LP = (dctnum < 64); /* whether LP exist or not */
  if(!LP&&(priority!=1)) {
    errnum = 59;
    return NULL;
  }
  /*------ frame existence check ------*/
  switch(scope) {
    case 1 : frame_num = (stat_num-1)/v_mb   + 1; break;
    case 2 : frame_num = (stat_num-1)/mb_num + 1; break;
    default: frame_num = stat_num;
  }
  if(last_frame < start_frame_num + frame_num - 1) {
    errnum = 56;
    return NULL;
  }
  /*------ memory allocation for stat data w/ initialize ------*/
  SD = (STAT*)malloc(sizeof(STAT));
  SD->num = stat_num;
  SD->mb_num = mb_num;
  SD->slice_num = v_mb;
  SD->block_mode = NULL;
  SD->block_mode = (char*)calloc(stat_num,sizeof(char));
  SD->bit_num  = (int*)calloc(stat_num,sizeof(int));
  SD->bit_numl = (int*)calloc(stat_num,sizeof(int));
  SD->bit_nums = (int*)calloc(stat_num,sizeof(int));
  /*------ memory allocation for read buffer ------*/
  FS.block_mode = (char*)calloc(mb_num,sizeof(char));
  FS.bit_num    = (short*)calloc(mb_num,sizeof(short));
  FS.bit_numl   = (short*)calloc(mb_num,sizeof(short));
  /*------ skip data ------*/
  for(i=0;i<start_frame_num-1;i++) {
    if(fread(FS.block_mode,sizeof(char) ,mb_num,fdstat)==0) return NULL;
    if(fread(FS.bit_num,sizeof(short),mb_num,fdstat)==0) return NULL;
    if(LP&&(fread(FS.bit_numl,sizeof(short),mb_num,fdstat)==0)) return NULL;
  }
  /*------ read data ------*/
  for(i=0;i<frame_num;i++) { /* frame increment */
    FS.frame = i + 1;
    FS.frame_mode = ( i%N ? ( i%M ? 3 : 1 ) : 0);
    /*------ read file ------*/
    errnum = 57;
    if(fread(FS.block_mode,sizeof(char) ,mb_num,fdstat)==0) return NULL;
    if(fread(FS.bit_num,sizeof(short),mb_num,fdstat)==0) return NULL;
    if(LP&&(fread(FS.bit_numl,sizeof(short),mb_num,fdstat)==0)) return NULL;
    /*------ data conversion ------*/
    FS.SNR = 0;
    for(j=0;j<v_mb;j++)   /*--- slice position ---*/
      for(k=0;k<h_mb;k++){ /*--- mb horizontal position ---*/
        switch(scope) {
          case 0: counter = i; 
                  break;
          case 1: counter = i*v_mb + j; 
                  break;
          case 2: counter = i*mb_num + j*h_mb + k;
        }
        if(counter<stat_num) {
          if(scope==2)
            SD->block_mode[counter] = FS.block_mode[j*h_mb+k];
          SD->bit_num [counter] += FS.bit_num [j*h_mb+k];
          SD->bit_numl[counter] += FS.bit_numl[j*h_mb+k];
          SD->bit_nums[counter] += (FS.bit_num[j*h_mb+k] 
                                   + FS.bit_numl[j*h_mb+k]);
	}
      }
#ifdef _DISPSTAT
  disp_stat(&FS);
#endif
  }
  /*------ atm bit conversion ------*/
  if(atm_mode)
    for(i=0;i<SD->num;i++) {
      SD->bit_num [i] = (SD->bit_num [i] - 1) / ATM_BITS + 1;
      SD->bit_numl[i] = (SD->bit_numl[i] - 1) / ATM_BITS + 1;
      SD->bit_nums[i] = (SD->bit_nums[i] - 1) / ATM_BITS + 1;
    }
  free(FS.block_mode);free(FS.bit_num);free(FS.bit_numl);
  get_stat(SD); /*--- get avg, max, std.dev ---*/
  errnum = 0;
  return SD;
}

int
  get_stat(SD)
STAT* SD;
{
  int i;
  int max = 0, maxl = 0, maxs = 0;
  int min  = 100000000, 
      minl = 100000000,
      mins = 100000000;
  double avg = 0.0, avgl = 0.0, avgs = 0.0;
  double sd  = 0.0, sdl  = 0.0, sds  = 0.0;
  /*------ compute avg, min, max ------*/
  for(i=0;i<SD->num;i++) {
    avg += SD->bit_num[i];
    if(max<SD->bit_num[i])  max = SD->bit_num[i];
    if(min>SD->bit_num[i])  min = SD->bit_num[i];
    avgl += SD->bit_numl[i];
    if(maxl<SD->bit_numl[i])  maxl = SD->bit_numl[i];
    if(minl>SD->bit_numl[i])  minl = SD->bit_numl[i];
    avgs += SD->bit_nums[i];
    if(maxs<SD->bit_nums[i])  maxs = SD->bit_nums[i];
    if(mins>SD->bit_nums[i])  mins = SD->bit_nums[i];
  }
  avg  /= SD->num;
  avgl /= SD->num;
  avgs /= SD->num;
  /*------ compute std. dev. ------*/
  for(i=0;i<SD->num;i++) {
    sd  += (double)(SD->bit_num[i] )*(double)(SD->bit_num[i] );
    sdl += (double)(SD->bit_numl[i])*(double)(SD->bit_numl[i]);
    sds += (double)(SD->bit_nums[i])*(double)(SD->bit_nums[i]);
  }
  sd  /= SD->num; sd  -= avg  * avg;
  if(sd>0) sd = sqrt(sd);
  sdl /= SD->num; sdl -= avgl * avgl;
  if(sdl>0) sdl = sqrt(sdl);
  sds /= SD->num; sds -= avgs * avgs;
  if(sds>0) sds = sqrt(sds);

  SD->max  = max;  SD->maxl = maxl; SD->maxs = maxs;
  SD->min  = min;  SD->minl = minl; SD->mins = mins;
  SD->avg  = avg;  SD->avgl = avgl; SD->avgs = avgs;
  SD->sd   = sd;   SD->sdl  = sdl;  SD->sds  = sds;

#ifdef DEBUG
  fprintf(stderr,"<stat> HP  : %6d %6d %10.2f %10.2f\n\
       LP  : %6d %6d %10.2f %10.2f\n\
       SUM : %6d %6d %10.2f %10.2f\n",
           SD->min,SD->max,SD->avg,SD->sd,
           SD->minl,SD->maxl,SD->avgl,SD->sdl,
           SD->mins,SD->maxs,SD->avgs,SD->sds);
#endif
  return 0;
}  

/*********************************************************************
  DISPLY STATISTICS
 *********************************************************************/
char
  mode_char(i)
char i;
{
  char mode;
  switch(i) {
     case 0 : mode = 'I';break; /* intra */
     case 1 : mode = 'P';break; /* Fwd Pred w/  MC */
     case 11: mode = 'c';break; /* Fwd Pred w/o MC */
     case 2 : mode = 'b';break; /* Bwd Pred */
     case 3 : mode = 'B';break; /* Bi-Direction */
     default: mode = '?';
  }
  return mode;
}

char
  mode_char2(i)
char i;
{
  char mode;
  switch(i) {
     case 0 : mode = '*';break; /* intra */
     case 1 : mode = '>';break; /* Fwd Pred w/  MC */
     case 11: mode = '-';break; /* Fwd Pred w/o MC */
     case 2 : mode = '<';break; /* Bwd Pred */
     case 3 : mode = '%';break; /* Bi-Direction */
     default: mode = '?';
  }
  return mode;
}

int
  disp_stat(FS)
FRAME_STAT* FS;
{
  int i,j;
  int offset1d; /* 1 dimensional offset */
  int n_vert_mb, n_hor_mb; /* # of MB's in picture */
  int sum,total;

  n_vert_mb = FS->height/MBSIZE;
  n_hor_mb = FS->width/MBSIZE;

  printf("\
-------- FRAME # %d STATISTICS REPORT ( %c mode ) ----------\n"
                        ,FS->frame,mode_char(FS->frame_mode));
  total = 0;
  for(i=0;i<n_vert_mb;i++) {
    printf("%3d : ",i+1);
    sum = 0;
    for(j=0;j<n_hor_mb;j++) {
      offset1d = i*n_hor_mb + j;
      printf("%c",mode_char2(FS->block_mode[offset1d]));
      sum += FS->bit_num[offset1d] ;
    }
    printf("   : %5d bits/slice\n",sum);/* + 38); /* 38 for slice header */
    total += sum;
  }
  switch(FS->frame_mode) {            /* add picture header */
    case 0 : total += (62+58) ;break; /* also add group code if I frame */
    case 1 : total += 66 ;break;
    case 3 : total += 70 ;break;
    default: total  =  0 ; /* error */
  }
  printf("Total %9d bit per frame\n",total);
/*printf("(I: intra, P: Fwd Pred w/ MC, c: Fwd Pred w/o MC)\n");*/
  printf("'*': intra, '>': Fwd Pred w/ MC, '-': Fwd Pred w/o MC\n");
  printf("'<': Bwd Pred, '%%': BI-Directional\n");
  printf("\
-------------------------------------------------------------\n");
  return 0;
}
