//##############################################################################
// File Name : mo_compen.c
// Author : Aviral Mittal
// Author's Email : avimit@yahoo.com
// University of EDinburgh
//
//This program implements Motion Compensation on a frame
//using a set of randomly generated vectors.
//
//This program extracts named frame from a qcif file and modifies 
//it so that a 'known' set of motion vectors may be implemented
//Motion vectors for this purpose are generated randomly.
//Usage: unix> mo_compen <in file> <n1>
//Where n1 is frame number to be modified
//Example: unix> mo_compen container.qcif 45
//Three output files are generated
//1. 000000....n1.pgm //the original requested frame
//2. mod.pgm //modified version of above frame
//3. mod.qcif//n1.pgm and mod.pgm stiched together to from a qcif file
////////////////////////////////////////////////////////////////////////////////
//MORE ABOUT MODIFICATION OR MOTION COMPENSATION
// It moves each block of desired frame by vector[row][col][0] x direction
// and by vector[row][col][1] in y direction
// vectors are generated by rand();
// LIMITATIONS
//
////////////////////////////////////////////////////////////////////////////////
// Version History
// Version 1.0
// Date of release 04 June 2006
// Changes w.r.t first release
// #0 Initial release
////////////////////////////////////////////////////////////////////////////////
//  NOTES
//  #1. This file is the extension work from the directory modify_frame
//  The first version of the file mo_compen.c was made taking modify_frame.c 2.0 as
//  a reference
//  #2. This prog may be used in conjunction with mest_es.c, which should give
//      same motion vectors which were generated during the run time of this 
//      program
//      Only diff would be the vectors in first row,col of blocks which will be 0 
//      The qcif file generated by this prog, can be read by mest_ms directly
////////////////////////////////////////////////////////////////////////////////
//WARNING!: The command line arguments are not checked. Its the 
//responsibility of the user to put correct command line options
//or funny things might happen.
//##############################################################################
#define NULL (0)
#include <stdio.h>
#include <string.h>
#define roww 144
#define coll 176
#define cb_coll 72
#define cb_roww 88
#define cr_coll 72
#define cr_roww 88
#define ppsize 8 //block size can be 2,4,8,16
#define ssize 4 //max vector disp for each block

//Function Prototype Declaration
void print_header(FILE * fout,int row, int col);
void print_luma(FILE * fout,unsigned char luma[][coll],int row, int col);
void int2str(int xx, char * mstr);
void populate_luma(unsigned char luma[][coll],int row, int col);
//The following function get_frame gets a frame form input file
//in a vector called luma, and it also writes that as a pgm file.
//fi is the input file pointer
//fo is the output file pointer
//stfr is the frame number
//mstr is the name of output file name
void get_frame(FILE * fi,
  FILE * fr,
  unsigned char luma[][coll],
  unsigned char cb[][cb_coll],
  unsigned char cr[][cr_coll],
  int row,
  int col,
  int stfr,
  char* mstr);

//The following function applies randomly generated motion vectors
//to the frame and writes that to file mod.pgm
void modify_frame(unsigned char luma[][coll], 
  int vector[][coll/ppsize][2],
  int row, 
  int col);

void myfopen(FILE **fileptr,int mode, char * fname);

void print_help_message();
void handle_args(int argc, char * argv[], char * inpfile,int * fr_nos);

void populate_vectors(int vector[][coll/ppsize][2],int row, int col);


//Function Prototype Declaration Ends

int main(int argc, char * argv[])
{
int kk =0;
int row = roww;
int col = coll;

int cb_row = cb_roww;
int cb_col = cb_coll;

int cr_row = cr_roww;
int cr_col = cr_coll;

int prn = 1;//change it to 0 to skip printing of informative messages.

FILE *fi = NULL;//fi = fopen(argv[1], "r");
FILE *fr;//fo = fopen(000000000.pgm, "w");
FILE *fout;//fo = fopen(mod.qcif, "w");
unsigned char def_name[] = "default8A.pgm";

unsigned char def_in_file[] = "default.qcif";
char inpfile[250];
//char * inpfile = NULL;
char outfile[250];
//char * outfile = NULL;
int stfr=0;
int finfr=0;

int fr_nos[2] = {0,0}; //For frame numbers from command line

//char * mstr = NULL;
char mstr[15];

unsigned char luma[row][col];
unsigned char cb[cb_roww][cb_coll];
unsigned char cr[cr_roww][cr_coll];

int vector[roww/ppsize][coll/ppsize][2];


  strcpy(inpfile,def_in_file);
  strcpy(outfile,"mod.qcif");
  handle_args(argc,argv,inpfile,fr_nos);
  stfr = fr_nos[0];
  if(prn) printf("INFO!: Frame=%d\n",stfr);
  strcpy(mstr,def_name);
  myfopen(&fi,0,inpfile); //0 means open as rb
  myfopen(&fout,2,outfile); //2 means open as w
  get_frame(fi,fr,luma,cb,cr,row,col,stfr,mstr); //get luma,cb,cr vectors
  fwrite(luma,1,row*col,fout);//write luma in qcif file as it is
  fwrite(cb,1,cb_row*cb_col,fout);//wirte cb for sake of qcif format
  fwrite(cr,1,cr_row*cr_col,fout);//wirte cr for sake of qcif format
  populate_vectors(vector,row,col); //generate random vectors
  modify_frame(luma,vector,row,col);//modify luma vector,put as mod.pgm 
  fwrite(luma,1,row*col,fout);//write modified luma in qcif file
  fclose(fout);
}// int main(int argc, char * argv[])
void print_header(FILE * fout,int row,int col)
{
  fprintf(fout,"P2\n");
  fprintf(fout,"%d %d\n",col,row);
  fprintf(fout,"255\n");
}// void print_header(char * fname)

void int2str(int xx, char * mstr)
{
  int remainder = 1; 
  int ii =0;
  while(*mstr) mstr++; //point to the end of string;
  mstr=mstr-5; //point to the last character, to overwrite it.
  for(ii=0;ii<9;ii++)
  {
    remainder = xx%10;
    xx = xx/10;
    *mstr = '0'+remainder;//Convert int to char
    mstr--;
  }// for(ii=0;ii<9;ii++)
}// void int2str(int xx, char * mstr)

void print_luma(FILE * fout,unsigned char luma[][coll],int row, int col)
{
  int ii = 0;
  int jj = 0;
  int * temp;
  for(ii=0;ii<row;ii++)
    for(jj=0;jj<col;jj++)
      fprintf(fout,"%d\n",luma[ii][jj]);
}// void print_luma(FILE * fout,char luma[][coll],int row, int col);

void get_frame(
  FILE * fi,
  FILE * fr,
  unsigned char luma[][coll],
  unsigned char cb[][cb_coll],
  unsigned char cr[][cr_coll],
  int row,
  int col,
  int stfr,
  char* mstr)
{
  int kk=0;
    for(kk=0;kk<=stfr;kk++)
    {
      fread(luma,1,row*col,fi); //read whole luma frame
      if(kk>=stfr) //Only print frame in a file if required
      {
        int2str(kk,mstr);
        printf("Output file: %s\n",mstr);
        printf("Output file: mod.pgm\n");
        printf("Output file: mod.qcif\n");
        myfopen(&fr,2,mstr);
        print_header(fr,row,col);
        print_luma(fr,luma,row,col);
        fclose(fr);
      }// if(kk>=stfr)
      //Now read the Cb Cr values for the same frame
      fread(cb,1,88*72,fi);
      fread(cb,1,88*72,fi);
    }// for(kk=0;kk<stfr;kk++)
}// void get_frame(char luma[][coll], int stfr)
void modify_frame(unsigned char luma[][coll], 
  int vector[][coll/ppsize][2],
  int row, 
  int col)
{
  int rr=0;
  int cc=0;

  unsigned char luma_copy[roww][coll];
  
  FILE * fout;

  if(!(fout = fopen("mod.pgm", "w")))
  {
    printf("Cannot open file :mod.pgm Exiting . . . .\n");
    exit(-1);
  }// if(!(fi = fopen("mod.pgm, "w")))
  //populate_luma(luma_copy,row,col);
  for(rr=ppsize;rr<row-ppsize;rr=rr+ppsize)
  {
    for(cc=ppsize;cc<col-ppsize;cc=cc+ppsize)
    {
      int dispx=0;
      int dispy=0;
      int ii=0;
      int jj=0;
      dispx=vector[rr/ppsize][cc/ppsize][0];
      dispy=vector[rr/ppsize][cc/ppsize][1];
      for(ii=0;ii<ppsize;ii++)
      {
        for(jj=0;jj<ppsize;jj++)
        {
          luma_copy[rr+ii+dispx][cc+jj+dispy]=luma[rr+ii][cc+jj]; 
        } 
      }// for(ii=0;ii<ppsize;ii++)
    }// for(cc=0;cc<col;cc=cc+ppsize)
  }// for(rr=0;rr<row;rr++)
  for(rr=0;rr<ppsize;rr++)
    for(cc=0;cc<col;cc++)
      luma_copy[rr][cc]=luma[rr][cc]; 
  for(rr=row-ppsize;rr<row;rr++)
    for(cc=0;cc<col;cc++)
      luma_copy[rr][cc]=luma[rr][cc]; 
  for(rr=ppsize;rr<row-ppsize;rr++)
    for(cc=0;cc<ppsize;cc++)
      luma_copy[rr][cc]=luma[rr][cc]; 
  for(rr=ppsize;rr<row-ppsize;rr++)
    for(cc=col-ppsize;cc<col;cc++)
      luma_copy[rr][cc]=luma[rr][cc]; 

  for(rr=0;rr<row;rr++)
    for(cc=0;cc<col;cc++)
      luma[rr][cc]=luma_copy[rr][cc]; 
  print_header(fout,row,col);
  print_luma(fout,luma,row,col);
  fclose(fout);
}// void modify_frame(unsigned char luma[][coll], int row, int col)

void populate_luma(unsigned char luma[][coll],int row, int col)
{
  int ii=0;
  int jj=0;
  unsigned int lumaint[roww][coll];
  for(ii=0;ii<row;ii++)
  {
    for(jj=0;jj<col;jj++)
    {
      luma[ii][jj] = ii*col+jj;
      lumaint[ii][jj] = ii*col+jj;
      //printf("luma[%d][%d]=%d,index=%d\n",ii,jj,lumaint[ii][jj],ii*col+jj);
    }
  }
}// void populate_luma(luma[][coll])

void myfopen(FILE **fileptr,int mode, char * fname)
{
  unsigned char luma[144][176];
  if(mode == 0)
  {
    if(!(*fileptr = fopen(fname, "rb")))
    {
      printf("1. Cannot open file :%s Exiting . . . .\n",fname);
      exit(-1);
    }// if(!(fileptr = fopen(fname, "r")))
  }// if(mode == 0)
  else if(mode == 1)
  {
    if(!(*fileptr = fopen(fname, "r")))
    {
      printf("2. Cannot open file :%s Exiting . . . .\n",fname);
      exit(-1);
    }// if(!(fileptr = fopen(fname, "r")))
  }// else if(mode == 1)
  else if(mode == 2)
  {
    if(!(*fileptr = fopen(fname, "w")))
    {
      printf("3. Cannot open file :%s Exiting . . . .\n",fname);
    exit(-1);
    }// if(!(fileptr = fopen(fname, "r")))
  }// else if(mode == 2)
}// void myfopen(FILE **fileptr,int mode, char * fname);
void print_help_message()
{
        printf("###################################################\n");
        printf("#This Program extracts frame from qcif file and\n");
        printf("#motion copensates it using randomly generated motion vectors\n");
        printf("###################################################\n");
        printf("Usage: unix> mo_compen <in file> <n1>\n"); 
        printf("Where n1 is the frame number\n"); 
        printf("Example: unix> mo_compen bridge.qcif 45\n"); 
        printf("This will produce 000000045.pgm i.e requested frame\n"); 
        printf("This will produce mod.pgm i.e moved version of above frame\n"); 
        printf("This will produce mod.qcif both put as a video file\n"); 
        printf("###################################################\n");
}// void print_help_message()

void handle_args(int argc, char * argv[], char * inpfile, int * fr_nos)
{
int stfr = 0;//local copy of it into this function.
switch(argc)
  {
    case 1 :
      { 
        print_help_message();
        exit(-1);
      }// case 1 :
    case 2 :
      { 
        //inpfile=argv[1];
        strcpy(inpfile,argv[1]);
        printf("INFO! #1. Input file is %s\n",inpfile);
        break;
      }// case 2 :
    case 3 :
      { 
        //inpfile=argv[1];
        strcpy(inpfile,argv[1]);
        printf("INFO! #2.Input file is %s\n",inpfile);
        //grab frame number in variable.
        //The input argv is a string. so a string of type
        //23 which contains the frame number to be printed
        //must be processed to get integer stfr, the frame number required
        while(*argv[2])
        {
          stfr = *argv[2]-48+stfr; //Convert argv[2] to an integer stfr
          argv[2]++;
          stfr=stfr*10;
        }
        stfr=stfr/10; //Re-adjust the result
        fr_nos[0] = stfr;
        break;
      }// case 3 :
    default: 
      {
        printf("ERROR! Too many args at command line. Exiting....\n");
        exit(-1);
      }
  }// switch(argc)
}// void handle_args(int argc, char * argv[], char * inpfile, int *fr_nos)

void populate_vectors(int vector[][coll/ppsize][2],int row, int col)
{
  int rr=0;
  int cc=0;
  for(rr=0;rr<row;rr=rr+ppsize)
  {
    for(cc=0;cc<col;cc=cc+ppsize)
    {
      vector[rr/ppsize][cc/ppsize][0] = rand()%ssize;
      printf("vector[%d][%d][0]=%d\n",rr/ppsize,cc/ppsize,vector[rr/ppsize][cc/ppsize][0]); 
      vector[rr/ppsize][cc/ppsize][1] = rand()%ssize;
      printf("vector[%d][%d][1]=%d\n",rr/ppsize,cc/ppsize,vector[rr/ppsize][cc/ppsize][1]); 
    }// for(cc=0;cc<col;cc=cc+ppsize)
  }// for(rr=0;rr<row;rr=rr+ppsize)
}// void populate_vectors(int vector[][coll/ppsize][2],int row, int col)
