//##############################################################################
// File Name : modify_frame.c
// Author : Aviral Mittal
// Author's Email : avimit@yahoo.com
// University of Edinburgh
//
//This program extracts named frame from a qcif file and modifies 
//it so that a 'known' set of motion vectors may be implemented
//Usage: unix> modify_frame <in file> <n1>
//Where n1 is frame number to be modified
//Example: unix> modify_frame 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
// It moves each pixel of desired frame by 'ssrr' in x direction
// and by srcc in y direction
// ssrr and sscc needs to be modified before compilation.
// LIMITATIONS
//
// 1.) The pixels cannot be moved beyond the (row,col) or (0,0) boundary.
// So only those pixels are moved which after moving lie in the boundary
// (0.0) and (row,col)
// For example if ssrr=5, sscc=5, then pixels (row-4,col-4) will have
// destination co-ordinates of (row+1, col+1) which is beyond the boundary
// hence these pixels will not be moved.
//
// 2.) ssrr sscc LEGAL VALUES
// (-row,-col) to (row,col)
// WARNING! Illegal values will not be reported by this program
// Expect funny results corresponding to Illegal values.
////////////////////////////////////////////////////////////////////////////////
//Version History
//Version 2.0
//date of release 04 June 2006
//Changes w.r.t first release
//#1. All butter crosssient.i.e :) all function implementation.
//#2. inpfile,outfile,mstr made char arrays instead of pointers.
// after learning a lesson that unless these names are arrays, these cannot
// be updated in a function successfully.
// Recall in the function strcmp, I had to copy each char from soruce to dest
// just not made their address equal.
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//Version History
//Version 1.0
//date of release 
////////////////////////////////////////////////////////////////////////////////
//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

//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 modifies the frame accroding to
//ssrr and sscc and writes that to file mod.pgm
void modify_frame(unsigned char luma[][coll], 
  int row, 
  int col, 
  int ssrr, 
  int sscc);

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

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


//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 ssrr=3; //defines the x-direction shift of image
int sscc=0; //defines the y-direction shift of image


  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
  modify_frame(luma,row,col,ssrr,sscc);//modify luma vector,put it 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 row, 
  int col,
  int ssrr,
  int sscc)
{
  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=0;rr<row;rr++)
  {
    for(cc=0;cc<col;cc++)
    {
      if(rr+ssrr>=0 && cc+sscc>=0 && rr+ssrr<=row && cc+sscc<=col)
      {
        luma_copy[rr][cc]=luma[rr+ssrr][cc+sscc]; 
      }// if(rr+ssrr>=0 && cc+sscc>=0 && rr+ssrr<=row && cc+sscc<=col)
      else
        luma_copy[rr][cc]=luma[rr][cc]; 
    }// for(cc=0;cc<col;cc++)
  }// for(rr=0;rr<row;rr++)
  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,ssrr,sscc)

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("#modify it as required\n");
        printf("###################################################\n");
        printf("Usage: unix> modify_frame <in file> <n1>\n"); 
        printf("Where n1 is the frame number\n"); 
        printf("Example: unix> modify_frame 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)
