/*
 * 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.
 */

/*************************************************************************
  MTE_utils contains several functions related to loading frame from 
  storage device. 

   load_frame : load RGB/CCIR data from strage device
   skip_frame : skip frames
 *************************************************************************/   
/*#define HEADER /* skip first 1024 in each frame. used only for test */
#ifndef BLOCK_LENGTH
#define BLOCK_LENGTH 1024 /* Block length of video data in tape device */
#endif

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include "global.h"
#include "utils.h"
#include "errorhandle.h"

/**************************************************************************/
/** LOAD_FRAME: reads a video frame from file descriptor fdin and returns**/
/** a pointer to a two dimensional array of size width x height. CCIR    **/
/** option is used to determine the format of the input file (CCIR/RGB)  **/
/** Frame will be saved byte (unsigned char) matrix by the CCIR format   **/
/**************************************************************************/
FRAME *
  load_frame(fdin,width,height,FORMAT_TYPE,file_type)
int fdin; /* low level file descpriter for input file */
int width;
int height;
int FORMAT_TYPE; /* data format, 0:RGB, 1:CCIR */
int file_type;
{
  int  i, j, size;
  byte *pix;    /* data temporary storage */
  FRAME *cframe; /* CCIR data storage, this will be the return value */
  int red,green,blue;
  int numgot;
  int ifactor; /* 2: CCIR, 3: RGB */
  int w,h;
  char* header;
  int b_length = BLOCK_LENGTH;

/*------------------------- check video data mode -----------------------*/
  switch (FORMAT_TYPE) {
  case 1: /* CCIR */
    w = 720; h = 480;
    ifactor = 2;
    break;
  case 0: /* RGB */
    w = width; h = height;
    ifactor = 3;
    break;
  default:
    break;
  }
  cframe = (FRAME *)make_frame(w,h);

  if (file_type == 1)
    b_length = w*h;

/*------------------------- get header info if exist -----------------------*/
#ifdef HEADER
  header = (char*)malloc(1024*sizeof(char));
  numgot = read(fdin, header, 1024);
  if(numgot == 0)     { errnum = 29; free(header); return NULL;}
  if(numgot < 0)      { errnum =  7; free(header); return NULL;}
  if(numgot != 1024)  { errnum =  6; free(header); return NULL;}
  free(header);
#endif
/*------------------------- LOAD frame ----------------------------------*/
#ifdef DEBUG
  fprintf(stderr,"Loading FRAME: .... ");
  fprintf(stderr,"Width: %d Height: %d Blocksize: %d ... ",w,h,b_length);
#endif
  size = w*h;
  pix = (byte *) malloc(size*ifactor*sizeof(byte));
  for(i=0;i<ifactor;i++)
    for(j=0;j<size/b_length;j++) {
      numgot = 0;
      numgot = read(fdin, pix+i*size+j*b_length,b_length);
#ifdef DEBUG
      if(numgot == 0)     { lseek(fdin, 0L, SEEK_SET); j--;
                              fprintf(stderr,"EOF reached ---> file rewinded\n"); }
#else
      if(numgot == 0)     { errnum = 29;free(pix); return NULL;}
#endif
      else if(numgot < 0)      { errnum =  7; free(pix); return NULL;}
      else if(numgot != b_length) { errnum =  6; free(pix); return NULL;}
    }

/*------------- data convert and put frame array --------------------------*/
  switch (FORMAT_TYPE) {
  case 1:
    for(i=0;i<h;i++)
      for(j=0;j<w/2;j++) {
	cframe->Cr[(w/2)*(i/2) + j] = cframe->Cr[(w/2)*(i/2) + j] + (*pix++)/2;
	cframe->Y[w*i + 2*j] = cframe->Y[w*i + 2*j] + *pix++;
	cframe->Cb[(w/2)*(i/2) + j] = cframe->Cb[(w/2)*(i/2) + j] + (*pix++)/2;
	cframe->Y[w*i + 2*j + 1] = cframe->Y[w*i + 2*j + 1] + *pix++;
      }
    break;
  case 0: /* convert RGB to YUV format */
    red   = 0;    
    green = size;
    blue  = 2*size;
    for(i=0;i<h;i++)
      for(j=0;j<w/2;j++,red++,green++,blue++) {
	cframe->Cr[(w/2)*(i/2) + j] += 
	  ((512*(int)pix[red] - 429*(int)pix[green] 
	    - 83*(int)pix[blue] )/4096 + 32);
	cframe->Y[w*i + 2*j] = 
	  ((306*(int)pix[red] +  601*(int)pix[green] 
	    + 117*(int)pix[blue])/1024);
	cframe->Cb[(w/2)*(i/2) + j] +=
	  ((-173*(int)pix[red++]-339*(int)pix[green++] 
	    + 512*(int)pix[blue++])/4096 + 32);
	cframe->Cr[(w/2)*(i/2) + j] += 
	  ((512*(int)pix[red] -  429*(int)pix[green] 
	    - 83*(int)pix[blue] )/4096 + 32);
	cframe->Y[w*i + 2*j + 1] =
	  ((306*(int)pix[red] + 601*(int)pix[green] 
	    + 117*(int)pix[blue])/1024);
	cframe->Cb[(w/2)*(i/2) + j] +=
	  ((-173*(int)pix[red]-339*(int)pix[green] 
	    + 512*(int)pix[blue])/4096 + 32);
      }
    break;
  default:
    exit (-3);
  }
  free(pix);
#ifdef DEBUG
  fprintf(stderr,"Complete.\n");
#endif
  return (cframe);
}
/************************************************************************
  open video data opens video file to encode and returns file descriptor
  on success, otherwise, retrns -1.
 ************************************************************************/
int
  open_video_data(fname)
char *fname; /* file/devide name of input file */
{
  int fdin;
  if ((fdin = open(fname, O_RDONLY, 0)) == -1) {
    errnum = 24;
    strcpy(errmsg,fname);
    return -1;
  }
#ifdef DEBUG
  fprintf(stderr, "Opened data file (%s).\n",fname);
#endif
  return fdin;
}
/***********************************************************************
  close_video_data close video data source file.
 ***********************************************************************/
close_video_data(fdin)
int fdin;
{
  close(fdin);
}
/***********************************************************************
  Skip frame skip frame
 ***********************************************************************/
skip(fdin,skip_num,width,height,CCIR,file_type)
int fdin,skip_num,width,height,CCIR,file_type;
{
  int i,j,numgot;
  int frame_byte; /* number of byte in each frame */
  int skip_num_per_frame;
  char *junk;
  int b_length = BLOCK_LENGTH;
/*------------------------- skip header ---------------------------*/
#ifdef HEADER
  {
    char* header;
    header = (char*)malloc(1024*sizeof(char));
    numgot = read(fdin, header, 1024);
    if(numgot == 0)    { errnum = 27; free(header); return -1;}
    if(numgot < 0)     { errnum =  7; free(header); return -1;}
    if(numgot != 1024) { errnum =  6; free(header); return -1;}
    free(header);
  }
#endif
  if (file_type == 1)
    b_length = width*height;

/*---------------- computer skip_byte --------------------------------*/
  if(CCIR) { /* CCIR */
    frame_byte = 480 * 720 * 2;
  }
  else {     /* RGB */
    frame_byte = width * height * 3;
  }
/*------------------------- skip loop  ---------------------------*/
  skip_num_per_frame = frame_byte / b_length;
  junk = (char*)malloc(b_length*sizeof(char));
#ifdef DEBUG
  fprintf(stderr,"Skip: %d frames\n",skip_num);
#endif
  for(i=0;i<skip_num;i++) {
    if (Xon)
      if(working_update(1,NULL,0)) { free(junk);return -1;}
#ifdef DEBUG
    fprintf(stderr,"  Skipping %d ... ",i+1);
#endif
    for(j=0;j<skip_num_per_frame;j++) { /*---- skip actual data ----*/
      numgot = read(fdin, junk, b_length);
#ifdef DEBUG
      if(numgot == 0)     { lseek(fdin, 0L, SEEK_SET); j--;
                              fprintf(stderr,"EOF reached ---> file rewinded\n"); }
#else
      if(numgot == 0)     { errnum = 27;free(junk); return -1;}
#endif
      else if(numgot < 0)     { errnum =  7; free(junk); return -1;}
      else if(numgot != b_length) { errnum =  6; free(junk); return -1;}
    }
#ifdef DEBUG
    fprintf(stderr,"Complete.\n");
#endif
  }
  free(junk);
  return 0;
}
/**************************************************************************/
FRAME *
  make_frame(w,h)
int w,h;
{
  FRAME *newframe;

  newframe = (FRAME *)malloc(sizeof(FRAME));
  if (newframe == NULL) perror("make_frame");

  newframe->Y = (byte *)calloc(w*h,sizeof(byte));
  if (newframe->Y == NULL) perror("make_frame (Y)");

  newframe->Cb = (byte *)calloc(w*h/4,sizeof(byte));
  if (newframe->Cb == NULL) perror("make_frame (Cb)");

  newframe->Cr = (byte *)calloc(w*h/4,sizeof(byte));
  if (newframe->Cr == NULL) perror("make_frame (Cr)");

  newframe->width = w; newframe->height = h;

  return newframe;
}
/**************************************************************************/
void 
  free_frame(frame)
FRAME *frame;
{
  int i;

  if (frame == NULL) return;
  if (frame->Y != NULL)
    free((byte *)(frame->Y));
  if (frame->Cb != NULL)
    free((byte *)(frame->Cb));
  if (frame->Cr != NULL)
    free((byte *)(frame->Cr));
}

void 
  copy_frame(frame1,frame2)
FRAME *frame1,*frame2;
{
  memcpy(frame1->Y,frame2->Y,frame1->width*frame1->height);
  memcpy(frame1->Cb,frame2->Cb,frame1->width*frame1->height/4);
  memcpy(frame1->Cr,frame2->Cr,frame1->width*frame1->height/4);
}

short **
  make_mb()

{
  int k;
  short **mb;

  mb = (short **)malloc((NUM_P+1)*sizeof(short *));
  for(k=0;k<NUM_P+1;k++) {
    mb[k] = (short *)calloc(6*DCT_SIZE_2,sizeof(short));
  }

  return mb;
}

void
  free_mb(mb)
short **mb;
{
  int k;

  for(k=0;k<NUM_P+1;k++) 
    free(mb[k]);

  free(mb);
}

void
  copy_mb(mb1,mb2)
short *mb1,*mb2;
{
  memcpy(mb1,mb2,6*DCT_SIZE_2*sizeof(short));
}

void
  add_mb(mb1,mb2)
short *mb1,*mb2;
{
  int i;

  for (i=0;i<6*DCT_SIZE_2;i++)
    mb1[i] += mb2[i];

}

void
  subtract_mb(mb1,mb2)
short *mb1,*mb2;
{
  int i;

  for (i=0;i<6*DCT_SIZE_2;i++)
    mb1[i] -= mb2[i];

}
  
void
  range_mb(mb,min,max)
short *mb;
int min,max;
{
  int i;

  for (i=0;i<6*DCT_SIZE_2;i++)
    mb[i] = RANGE(mb[i],min,max);

}

int
  big(mv)
int mv;
{
  if (mv > 15 || mv < -16)
    return ((mv < 0) ? 32 : -32);
  else
    return (0);
}
