/*****************************************************************************
 *                                                                          
 * This software module was originally developed by 
 *
 * J. Ignacio Ronda (UPM-GTI / ACTS-MoMuSys)
 *                                                      
 * and edited by                                        
 *                                                      
 * Martina Eckert (UPM-GTI / ACTS-MoMuSys)
 *
 * in the course of development of the MPEG-4 Video (ISO/IEC 14496-2) standard.
 * This software module is an implementation of a part of one or more MPEG-4 
 * Video (ISO/IEC 14496-2) tools as specified by the MPEG-4 Video (ISO/IEC 
 * 14496-2) standard. 
 *
 * ISO/IEC gives users of the MPEG-4 Video (ISO/IEC 14496-2) standard free 
 * license to this software module or modifications thereof for use in hardware
 * or software products claiming conformance to the MPEG-4 Video (ISO/IEC 
 * 14496-2) standard. 
 *
 * Those intending to use this software module in hardware or software products
 * are advised that its use may infringe existing patents. The original 
 * developer of this software module and his/her company, the subsequent 
 * editors and their companies, and ISO/IEC have no liability for use of this 
 * software module or modifications thereof in an implementation. Copyright is 
 * not released for non MPEG-4 Video (ISO/IEC 14496-2) Standard conforming 
 * products. 
 *
 * ACTS-MoMuSys partners retain full right to use the code for his/her own 
 * purpose, assign or donate the code to a third party and to inhibit third 
 * parties from using the code for non MPEG-4 Video (ISO/IEC 14496-2) Standard
 * conforming products. This copyright notice must be included in all copies or
 * derivative works. 
 *
 * Copyright (c) 1997
 *
 *****************************************************************************/

/***********************************************************HeaderBegin*******
 *                                                                         
 * File:        rc_hist.c
 *
 * Author:      J. I. Ronda, M. Eckert, UPM-GTI
 *
 * Created:     04-07-97
 *                                                                         
 * Description: History access functions
 *
 * Notes:       
 *
 * Flags:       -D_RC_DEBUG_  -  RC debugging   
 *
 * Modified:
 *      12.11.97  Martina Eckert: Headers, comments, cleaning
 *      18.11.97  M.Wollborn: include unistd only for non-PC
 *      27.11.97  M. Eckert: Changes for independent frame type control
 *                                 
 ***********************************************************HeaderEnd*********/

/************************    INCLUDE FILES    ********************************/

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#if !defined(WIN32)
#  include <unistd.h>
#endif

#include <ctype.h>

#include "momusys.h"
#include "mom_vop.h"
#include "vm_config.h"
#include "mom_vol.h"
#include "mom_structs.h"
#include "vm_enc_defs.h"

#include "rc.h"

/***********************************************************CommentBegin******
 *
 * -- Funtions of RCH data type --
 *
 * Author : 
 *      J. Ignacio Ronda, UPM-GTI     
 *
 * Created :            
 *      18-06-97
 *
 ***********************************************************CommentEnd********/

/***********************************************************CommentBegin******
 *
 * -- rch_print --
 *
 * Purpose :
 *      Type printing function. 
 * 
 * Arguments in :       
 *      RC_HIST  *rch    -  History data 
 *      FILE     *file   -  Output filename
 *      char     *prefix -  for output string
 *
 * Description :   
 *     Prints information about history data     
 *
 * Modified : 
 *
 *
 ***********************************************************CommentEnd********/

void rch_print(
               RC_HIST *rch, 
               FILE    *file, 
               char    *prefix
               )
{
  Int i, first, count;

  fprintf(file, "%s size= %d\n", prefix, rch->size);

  fprintf(file, "%s n=    %d\n", prefix, rch->n);
  fprintf(file, "%s prev= %d\n", prefix, rch->prev);
  fprintf(file, "%s ptr=  %d\n", prefix, rch->ptr);

  fprintf(file, "%s total_dist=      %6.3f\n", prefix, rch->total_dist);
  fprintf(file, "%s total_bits_text= %d\n", prefix, rch->total_bits_text);
  fprintf(file, "%s total_bits=      %d\n", prefix, rch->total_bits);
  fprintf(file, "%s total_frames=    %d\n", prefix, rch->total_frames);

  /* Stored data printing (in input order) */
  if (rch->n < rch->size) /* Table incomplete */
     first = 0;
  else                    /* Table full */
     first = rch->ptr;
  for (i=first,count=0; count<rch->n; i=rch_inc_mod(rch,i),count++)
     {
     fprintf(file, "%s i= %3d: bits_text= %d\n", prefix, i, rch->bits_text[i]);
     fprintf(file, "%s         pixels=    %d\n", prefix, rch->pixels[i]);
     fprintf(file, "%s         mad_text=  %6.3f\n", prefix, rch->mad_text[i]);
     fprintf(file, "%s         dist=      %6.3f\n", prefix, rch->dist[i]);
     fprintf(file, "%s         bits_vop=  %d\n", prefix, rch->bits_vop[i]);
     fprintf(file, "%s         qp=        %d\n", prefix, rch->qp[i]);
     }
}

/***********************************************************CommentBegin******
 *
 * -- rch_init --
 *
 * Purpose :
 *      Initialization of history data
 * 
 * Arguments in :       
 *      RC_HIST  *rch    -  History data 
 *      Int      size    -  Size of history
 *
 * Modified : 
 *      27.11.97  M. Eckert: Changes for independent frame type control
 *
 *
 ***********************************************************CommentEnd********/

void rch_init(
              RC_HIST *rch, 
              Int size
              )
{
   Int i;
   for(i=0; i<3; i++){
      rch[i].size=size;
      rch[i].n=0;
      rch[i].prev=0;
      rch[i].ptr=0;
      rch[i].proc_stat=0;
      
      if ((rch[i].bits_text=(Int *)calloc(sizeof(Int),size))==0)
	 error_exit("RC: ERROR: calloc\n");
      if ((rch[i].pixels=(Int *)calloc(sizeof(Int),size))==0)
	 error_exit("RC: ERROR: calloc\n");
      if ((rch[i].mad_text=(Double *)calloc(sizeof(Double),size))==0)
	 error_exit("RC: ERROR: calloc\n");
      if ((rch[i].dist=(Double *)calloc(sizeof(Double),size))==0)
	 error_exit("RC: ERROR: calloc\n");
      if ((rch[i].bits_vop=(Int *)calloc(sizeof(Int),size))==0)
	 error_exit("RC: ERROR: calloc\n");
      if ((rch[i].qp=(Int *)calloc(sizeof(Int),size))==0)
	 error_exit("RC: ERROR: calloc\n");
      
      rch[i].total_dist=0;
      rch[i].total_bits_text=0;
      rch[i].total_bits=0;
      rch[i].total_frames=0;
      rch[i].last_pred_bits_text=0;
      
#ifdef _RC_DEBUG_
   rch_print(&rch[i], stdout, "RC: RCH_INIT");
#endif
   }   
}


/***********************************************************CommentBegin******
 *
 * -- rch_free --
 *
 * Purpose :
 *      Free reserved memory for history
 * 
 * Arguments in :       
 *      RC_HIST  *rch    -  History data 
 *
 * Created : 
 *      03.02.98   
 *
 *
 ***********************************************************CommentEnd********/

Void rch_free(
              RC_HIST *rch
)

{
   Int i;

   for(i=0; i<3; i++){
      free(rch[i].bits_text);
      free(rch[i].pixels);
      free(rch[i].mad_text);
      free(rch[i].dist);
      free(rch[i].bits_vop);
      free(rch[i].qp);
   }

}

/***********************************************************CommentBegin******
 *
 * -- rch_store_before --
 *
 * Purpose :
 *      Storing of data of a frame available BEFORE its encoding
 *      but after obtaining its prediction error.
 * 
 * Arguments in :       
 *      RC_HIST  *rch    -  History data 
 *      Int      pixels  -  current number of pixels to store 
 *      Double   mad     -  current mad to store 
 *      Int      qp      -  current quantization parameter to store 
 *
 * Modified : 
 *
 *
 ***********************************************************CommentEnd********/

void rch_store_before(
                      RC_HIST *rch,
                      Int      pixels, /* Num. pixels of VOP */
                      Double   mad,    /* MAD of prediction error */
                      Int      qp
                      )
{


if (rch->prev)
{
     error_exit("RC: rch_store_before: error\n");
}
rch->pixels[rch->ptr]  = pixels;
rch->mad_text[rch->ptr]= mad;
rch->qp[rch->ptr]      = qp;


rch->prev=1;

}

/***********************************************************CommentBegin******
 *
 * -- rch_store_after --
 *
 * Purpose :
 *      Storing of data of a frame available AFTER its encoding
 * 
 * Arguments in :       
 *      RC_HIST  *rch       -  History data 
 *      RC_UPM_DATA  *rcd   -  UPM data 
 *      Int      bits_text  -  number of pixels for texture
 *      Int      bits_vop   -  number of pixels for vop
 *      Double   dist       -  obtained distortion value
 *
 * Modified : 
 *      6.2.98   M.Eckert: Introduction of rcd and rc_algorithm for saving of
 *                         total data amount for global RC
 *
 *
 ***********************************************************CommentEnd********/

void rch_store_after(
                     RC_HIST      *rch,
                     Int          bits_text,
                     Int          bits_vop,
                     Double       dist
                     )
{

  /* Save only for actual frame type: */

  if (!rch->prev)
    error_exit("RC: rch_store_after: error\n");

  rch->bits_text[rch->ptr] = bits_text;
  rch->bits_vop[rch->ptr]  = bits_vop;
  rch->dist[rch->ptr]      = dist;
  
  rch->total_dist      += dist;
  rch->total_bits_text += bits_text;
  rch->total_bits      += bits_vop;
  rch->total_frames++;
  
  rch->ptr++;

  if (rch->ptr == rch->size)
    rch->ptr = 0;
  if (rch->n < rch->size)
    rch->n++;
  
  rch->prev=0;


}


/***********************************************************CommentBegin******
 *
 * -- Auxiliary functions (no modification of data structure) --
 *
 * Author : 
 *      J. Ignacio Ronda, UPM-GTI     
 *
 * Created :            
 *      18-06-97
 *
 ***********************************************************CommentEnd********/


/***********************************************************CommentBegin******
 *
 * -- rch_get_last_qp ---
 *
 * Purpose :
 *      Obtainment of last QP
 *
 * Arguments in :       
 *      RC_HIST  *rch       -  History data 
 *
 ***********************************************************CommentEnd********/

Int rch_get_last_qp(
                    RC_HIST *rch
                    )
{
  if (rch->n == 0)
     error_exit("RC: rch_get_last_qp: error 1\n");

  if (rch->ptr == 0)
     return rch->qp[rch->n-1];
  else
     return rch->qp[rch->ptr-1];
}

/***********************************************************CommentBegin******
 *
 * -- rch_get_last_dist ---
 *
 * Purpose :
 *      Obtainment of last PSNR value. 
 *
 * Arguments in :       
 *      RC_HIST  *rch       -  History data 
 *
 ***********************************************************CommentEnd********/


Double rch_get_last_dist(
                         RC_HIST *rch
                         )
{
  if (rch->n == 0)
     error_exit("RC: rch_get_last_dist: error 1\n");

  if (rch->ptr == 0)
     return rch->dist[rch->n-1];
  else
     return rch->dist[rch->ptr-1];
}

/***********************************************************CommentBegin******
 *
 * -- rch_get_bits_vop ---
 * 
 * Purpose :
 *      Obtainmemt of total bits of last VOP
 *
 * Arguments in :       
 *      RC_HIST  *rch       -  History data 
 *
 ***********************************************************CommentEnd********/

Int rch_get_last_bits_vop(
                          RC_HIST *rch
                          )
{
  if (rch->n == 0)
     error_exit("RC: rch_get_bits_vop: error 1\n");

  if (rch->ptr == 0)
     return rch->bits_vop[rch->n-1];
  else
     return rch->bits_vop[rch->ptr-1];
}

/***********************************************************CommentBegin******
 *
 * -- rch_get_last_bits_nottext ---
 *
 * Purpose :
 *      Obtainmemt of bits for header, shape & motion of last VOP
 *
 * Arguments in :       
 *      RC_HIST  *rch       -  History data 
 *
 ***********************************************************CommentEnd********/

Int rch_get_last_bits_nottex(
                             RC_HIST *rch
                             )
{
  if (rch->n == 0)
      return 0; /* The case that a VO exists, but still does not contain
		   anything. So it has not been coded before and has no history. */
      /*     error_exit("RC: rch_get_last_bits_nottex: error 1\n");*/

  if (rch->ptr == 0)
     return (rch->bits_vop[rch->n-1] - rch->bits_text[rch->n-1]);
  else
     return (rch->bits_vop[rch->ptr-1] - rch->bits_text[rch->ptr-1]);
}


/***********************************************************CommentBegin******
 *
 * -- rch_get_last_mad_text ---
 *
 * Purpose :
 *      Obtainment of last MAD of texture
 *
 * Arguments in :       
 *      RC_HIST  *rch       -  History data 
 *
 ***********************************************************CommentEnd********/

Double rch_get_last_mad_text(
                             RC_HIST *rch
                             )
{
  if (rch->n == 0)
    {
      return 0; /* The case that a VO exists, but still does not contain
		   anything. So it has not been coded before and has no history. */

      /*  error_exit("RC: rch_get_last_mad_text: error 1\n");*/
    }

  if (rch->ptr == 0)
     return rch->mad_text[rch->n-1];
  else
     return rch->mad_text[rch->ptr-1];
}


/***********************************************************CommentBegin******
 *
 * -- rch_get_plast_mad_text ---
 *
 * Purpose :
 *      Obtainment of previous-to-last MAD of texture
 *
 * Arguments in :       
 *      RC_HIST  *rch       -  History data 
 *
 ***********************************************************CommentEnd********/

Double rch_get_plast_mad_text(
                              RC_HIST *rch
                              )
{
  if (rch->n < 2)
     error_exit("RC: rch_get_plast_mad_text: error 1\n");

  if (rch->ptr == 0)
     return rch->mad_text[rch->n-2];
  else if (rch->ptr == 1)
     return rch->mad_text[rch->n-1];
  else
     return rch->mad_text[rch->ptr-2];
}

/***********************************************************CommentBegin******
 *
 * -- rch_inc_mod ---
 *
 * Purpose :
 *      Module-n number increase
 *
 * Arguments in :       
 *      RC_HIST  *rch       -  History data 
 *      Int      i          -  module number
 *
 ***********************************************************CommentEnd********/
                    

Int rch_inc_mod(
                RC_HIST *rch, 
                Int i
                )
{
  if (i<0 || i>=rch->size)
     error_exit("rch_inc_mod: range");
  i++;
  if (i==rch->size)
    return 0;
  else
    return i;
}


/***********************************************************CommentBegin******
 *
 * -- rch_dec_mod ---
 *
 * Purpose :
 *      Module-n number decrease
 *
 * Arguments in :       
 *      RC_HIST  *rch       -  History data 
 *      Int      i          -  module number
 *
 ***********************************************************CommentEnd********/

Int rch_dec_mod(
                RC_HIST *rch, 
                Int i
                )
{
  if (i<0 || i>=rch->size)
     error_exit("rch_dec: range");
  i--;
  if (i==-1)
    return rch->size-1;
  else
    return i;
}

/***********************************************************CommentBegin******
 *
 * -- rch_get_last_pixels ---
 *
 * Purpose :
 *      Obtainment of last number of pixels
 *
 * Arguments in :       
 *      RC_HIST  *rch       -  History data 
 *
 ***********************************************************CommentEnd********/

Int rch_get_last_pixels(
                        RC_HIST *rch
                        )
{
  if (rch->n == 0)
    {
      return 0; /* The case that a VO exists, but still does not contain
		   anything. So it has not been coded before and has no history. */
      
      /*
	error_exit("RC: rch_get_last_pixel: error 1\n");
      */ 
    }

  if (rch->ptr == 0)
     return rch->pixels[rch->n-1];
  else
     return rch->pixels[rch->ptr-1];
}

/***********************************************************CommentBegin******
 *
 * -- rch_get_curr_pixels ---
 *
 * Purpose :
 *      Obtainment of current number of pixels
 *
 * Arguments in :       
 *      RC_HIST  *rch       -  History data 
 *
 ***********************************************************CommentEnd********/

Int rch_get_curr_pixels(
                        RC_HIST *rch
                        )
{
  if (rch->n == 0)
    {
      error_exit("RC: rch_get_curr_pixel: error 1\n");
    }

  return rch->pixels[rch->ptr];
}

/* The following ones come from rc_upm_model.c */


/***********************************************************CommentBegin******
 *
 * -- first_frame --
 *
 * Purpose :
 *       Returns 1, if the currently coded VOL belongs to first frame
 * 
 * Arguments in :       
 *      RC_HIST  *rch       -  History data 
 *
 * Modified : 
 *      05.12.97  M. Eckert: Change site and input parameters
 *
 *DIE KANN JA DANN WOHL RAUS!
 ***********************************************************CommentEnd********/

Int first_frame(
		RC_HIST *rch
                )
{

   if (rch_first(rch))
      return 1;
   else
      return 0;
}

/***********************************************************CommentBegin******
 *
 * -- rch_set_process_flag --
 *
 * Author : 
 *      Martina Eckert, UPM-GTI     
 *
 * Purpose :
 *      Sets flag to mark process status
 *      The flag "proc_stat" indicates the process status of one VO during 
 *      process of all VOs of a frame:
 *      0: vo_id < current vo_id ||
 *         vo_id = current vo_id && vol_id < current vol_id  -> already processed
 *      1: vo_id = current vo_id && vol_id = current vol_id  -> currently in process
 *      2: vo_id > current vo_id
 *         vo_id = current vo_id && vol_id > current vol_id  -> not yet processed
 * 
 * Arguments in :       
 *      RC_HIST  *rch       -  History data 
 *      Int      set          -  actual process status
 *
 * Modified : 
 *      05.12.97  M. Eckert: Rename, change site and input parameters
 *
 ***********************************************************CommentEnd********/

void rch_set_process_flag(
			  RC_HIST *rch, 
			  Int set
			  )
{


   if (set == 0)              /* Already processed */
      rch_proc_stat(rch) = 0;
   else if (set == 1)
      rch_proc_stat(rch) = 1; /* Currently in process */
   else
      rch_proc_stat(rch) = 2; /* Not yet processed */

}

/***********************************************************CommentBegin******
 *
 * -- rch_store --
 *
 * Author : 
 *      Martina Eckert, UPM-GTI     
 *
 * Purpose :
 *       Store number of pixel and mad before calculating qp
 * 
 * Arguments in :       
 *      RC_HIST  *rch       -  History data 
 *      Int      pixels       -  Number of coded pixels
 *      Double   mad          -  mean absolute difference
 *
 * Modified : 
 *      05.12.97  M. Eckert: Change site and input parameters
 *
 *
 ***********************************************************CommentEnd********/

void rch_store(
               RC_HIST *rch, 
               Int pixels, 
               Double mad
               )
{ 
  
   if (rch->prev)
   {
        error_exit("RC: rch_store: error\n");
   }

   rch->pixels[rch->ptr]   = pixels;
   rch->mad_text[rch->ptr] = mad;
  
}

/***********************************************************CommentBegin******
 *
 * -- rch_store_qp_before --
 *
 * Author : 
 *      Martina Eckert, UPM-GTI     
 *
 * Purpose :
 *       Store qp before coding
 * 
 * Arguments in :       
 *      RC_HIST  *rch       -  History data 
 *      Int      qp           -  quantization parameter
 *
 * Modified : 
 *      05.12.97  M. Eckert: Change site and input parameters
 *
 *
 ***********************************************************CommentEnd********/

void rch_store_qp_before(
			 RC_HIST *rch, 
                         Int qp
                         )
{ 

   if (rch->prev)
   {
      error_exit("RC: rch_store_qp_before: error\n");
   }

   rch->qp[rch->ptr] = qp;

   rch->prev=1;

}

/***********************************************************CommentBegin******
 *
 * -- rch_qp_range_control --
 *
 * Author : 
 *      Martina Eckert, UPM-GTI     
 *
 * Purpose :
 *       Avoid a too big alternation of qp
 * 
 * Arguments in :       
 *      RC_HIST  *rch       -  History data 
 *      Int      qp           -  quantization parameter
 *
 * Modified : 
 *      05.12.97  M. Eckert: Rename, change site and input parameters
 *
 ***********************************************************CommentEnd********/

Int rch_qp_range_control(
			 RC_HIST *rch, 
			 Int qp
			 )
{ 
   Int      last_qp;

   if (rch_first(rch))
      return qp;

   last_qp = rch_get_last_qp(rch);

   qp = (Int)MIN3(ceil(last_qp+5), qp, MAX_QUANT);
   qp = (Int)MAX3(ceil(last_qp-5), qp, MIN_QUANT);

   return qp;
 
}

/***********************************************************CommentBegin******
 *
 * -- rch_min_qp --
 *
 * Author : 
 *      Martina Eckert, UPM-GTI     
 *
 * Purpose :
 *       Returns the next minimal possible qp
 * 
 * Arguments in :       
 *      RC_HIST  *rch       -  History data 
 *
 * Return value :
 *      minimal possible qp
 *
 * Modified : 
 *      05.12.97  M. Eckert: Rename, change site and input parameters
 * ONLY IN UPM1 USED!
 *
 ***********************************************************CommentEnd********/

Int rch_min_qp(
               RC_HIST *rch
	       )
{
   Int      last_qp;

   if (rch_first(rch))
      return MIN_QUANT;
   else
   {
      last_qp = rch_get_last_qp(rch);
      return MAX((last_qp - QP_INC), MIN_QUANT);
      /* return MAX((last_qp*0.75), MIN_QUANT);*/
   }

}

/***********************************************************CommentBegin******
 *
 * -- rch_max_qp --
 *
 * Author : 
 *      Martina Eckert, UPM-GTI     
 *
 * Purpose :
 *       Returns the next maximal possible qp
 * 
 * Arguments in :       
 *      RC_HIST  *rch       -  History data 
 *
 * Return value :
 *      maximal possible qp
 *
 * Modified : 
 *      05.12.97  M. Eckert: Rename, change site and input parameters
 * ONLY IN UPM1 USED!
 *
 *
 ***********************************************************CommentEnd********/

Int rch_max_qp(
               RC_HIST *rch
	       )
{
   Int      last_qp;

   if (rch_first(rch))
      return MAX_QUANT;
   else
   {
      last_qp = rch_get_last_qp(rch);
      return MIN((last_qp + QP_INC), MAX_QUANT);
      /* return ((last_qp*1.25), MAX_QUANT);*/

   }

}

/***********************************************************CommentBegin******
 *
 * -- rch_exclude_qp --
 *
 * Author : 
 *      Martina Eckert, UPM-GTI     
 *
 * Created :            
 *      11-12-97
 *
 * Purpose :
 *      Returns the value of the last qp if it was repeated 5 times.
 *      In other cases return of 0.
 * 
 * Arguments in :       
 *      RC_HIST  *rch       -  History data 
 *
 * Return value :
 *      qp to exclude or 0 
 *
 ***********************************************************CommentEnd********/

Int rch_exclude_qp(
		   RC_HIST *rch
		   )
{
   Int i, n, count, last_qp;

   n = rch_get_total_frames(rch)-1; /* Number of coded frames */
   /*  last_qp = rch->qp[n];*/
   last_qp = rch_get_last_qp(rch);

   if (n >= QP_INC)
   {
      for (i=rch_dec_mod(rch,rch_get_ptr(rch)), count=0; count<QP_INC; i=rch_dec_mod(rch,i), count++)
	 if(rch->qp[i] != last_qp)
	    return 0;
      /*
	 else
	     printf("%d.: %d == last! \n",count, rch->qp[i]);
	     */
      return last_qp;
   }
   else
      return 0;

}

/*********************************************************** End of file ***/
