/*
  Decode.java
  Contains functions to: 
     read DCT data from a file, 
     carries out IDCT to obtain YCbCr macroblocks from DCT coefficients, 
     convert YCbCr data to RGB, and
     read YCbCr data from a file
*/

import java.io.*;

class Decode {

 /*
  Get YCbCr data from file pointed by in. Put the four 8x8 Y sample blocks,
  one 8x8 Cb sample block and one 8x8 Cr sample block into a class object
  of YCbCr_MACRO.
  Return: number of bytes read from file. 
 */
  public int get_yccblocks( YCbCr_MACRO ycbcr_macro, DataInputStream in )
  {
    int r, row, col, i, j, k, n, b, c;

    byte abyte;   
 
    n = 0;
    //read data from file and put them in four 8x8 Y sample blocks
    for ( b = 0; b < 4; b++ ) {
      if ( b < 2 )
        k = 8 * b;                //points to beginning of block 
      else
        k = 128 + 8 * ( b - 2 );  //points to beginning of block  
      for ( i = 0; i < 8; i++ ) { //one sample-block
        if ( i > 0 ) k += 16;     //advance by 1 row of macroblock 
        for ( j = 0; j < 8; j++ ){
	  try {
            if ( ( c = in.read() ) == -1 )   //read one byte
              break; 
            ycbcr_macro.Y[k+j] = c;
          } catch (IOException e) {
              e.printStackTrace();
              System.exit(0);
          }
           n++;
        } //for j
      } //for i
    } //for b

    //now do that for 8x8 Cb block
    k = 0;
    for ( i = 0; i < 8; ++i ) {
      for ( j = 0; j < 8; ++j ) {
        try {
          if ( ( c = in.read() ) == -1 )     //read one byte
            break; 
          ycbcr_macro.Cb[k++] = c;
        } catch (IOException e) {
          e.printStackTrace();
          System.exit(0);
        }
        n++;
      }
    }

    //now do that for 8x8 Cr block
    k = 0;
    for ( i = 0; i < 8; ++i ) {
      for ( j = 0; j < 8; ++j ) {
        try {
          if ( ( c = in.read() ) == -1 )     //read one byte
            break; 
          ycbcr_macro.Cr[k++] = c;
        } catch (IOException e) {
          e.printStackTrace();
          System.exit(0);
        }
        n++;
      }
    }
    return n;           //number of bytes read
  }

  /*   Read in YCbCr data from file.
   *   Convert a YCbCr frame to an RGB frame.
   *   Return RGB data via parameter image.  
   */
  public int decode_yccFrame ( RGBImage image, DataInputStream in )
  {
    int r, row, col, i, j, k, block;
    int n = 0;
    //16x16 pixel macroblock; assume 24-bit for each RGB pixel
    RGB_MACRO rgb_macro = new RGB_MACRO();
    YCbCr_MACRO ycbcr_macro = new YCbCr_MACRO();
    RgbYcc rgbycc = new RgbYcc();
    for ( row = 0; row < image.height; row += 16 ) {
      for ( col = 0; col < image.width; col += 16 ) {
        int m = get_yccblocks( ycbcr_macro, in );   
        if ( m <= 0 ) { System.out.printf("\nout of data\n"); return m;}
        n += m;
        rgbycc.ycbcr2macroblock( ycbcr_macro, rgb_macro );
        k = (row * image.width + col); //points to macroblock beginning 
        r = 0;
        for ( i = 0; i < 16; ++i ) {
          for ( j = 0; j < 16; ++j ) {
            image.ibuf[k].R = rgb_macro.rgb[r].R;
            image.ibuf[k].G = rgb_macro.rgb[r].G;
            image.ibuf[k].B = rgb_macro.rgb[r].B;
            k++;  r++;
          }
          k += (image.width - 16);   //points to next row of macroblock
        }
      } //for col
    }  //for row
    return n;  //number of bytes read
  }
  /*----------------------------------------------------------------
    Functions above are from Chapter 5.
    Functions below are added in Chapter 6.
    ----------------------------------------------------------------
  */

  int get_one_dctblock ( int [][]  Y, DataInputStream in  )
  {
    for ( int i = 0; i < 8; ++i )
      for ( int j = 0; j < 8; ++j )
         try {
           Y[i][j] = ( int ) in.readShort();
         } catch (IOException e) {
           e.printStackTrace();
           System.exit(0);
         }
    return 1;
  }

  int get_dct_yccblocks( YCbCr_MACRO ycbcr_macro, DataInputStream in )
  {
    int r, row, col, i, j, k, n, p, block;
    int [][] Y = new int[8][8], X = new int[8][8];
    DctVideo dct_idct = new DctVideo();

    n = 0; 
    //read data from file and put them in four 8x8 Y sample blocks
    for ( block = 0; block < 4; block++ ) {
      if ( get_one_dctblock( Y, in ) < 1 )
        return 0;
      dct_idct.idct ( Y, X );
      k = 0;
      if ( block < 2 )
        p = 8 * block;                //points to beginning of block 
      else
        p = 128 + 8 * ( block - 2 );  //points to beginning of block 
      for ( i = 0; i < 8; i++ ) {     //one sample-block
        if ( i > 0 ) p += 16;         //advance marcoblock length ( 16 )
        for ( j = 0; j < 8; j++ ) {
          ycbcr_macro.Y[p+j] = X[i][j]; 
          n++;
        } //for j
      } //for i
    } //for block

    //now do that for 8x8 Cb block
    k = 0;
    if ( get_one_dctblock( Y, in ) < 1 )
      return 0;
    dct_idct.idct ( Y, X );
    for ( i = 0; i < 8; ++i ) {
      for ( j = 0; j < 8; ++j ) {
         ycbcr_macro.Cb[k] = X[i][j];  
         k++;
         n++;
      }
    }

    //now do that for 8x8 Cr block
    k = 0;
    if ( get_one_dctblock( Y, in ) < 1 )
      return 0;
    dct_idct.idct ( Y, X );
    for ( i = 0; i < 8; ++i ) {
      for ( j = 0; j < 8; ++j ) {
        ycbcr_macro.Cr[k] = X[i][j];  
        k++;
        n++;
      }
    }
    return n;                         //number of coefficients read
  }

  /*
   *   Decode DCT coeffs to a YCbCr frame and then to RGB.  
   */
  int decode_dct ( RGBImage image, DataInputStream in )
  {
    int r, row, col, i, j, k, block;
    int n = 0;
    RGB_MACRO rgb_macro=new RGB_MACRO(); //assume 24-bit for each RGB pixel
    YCbCr_MACRO ycbcr_macro = new YCbCr_MACRO(); //YCbCr macroblock 
    RgbYcc rgbycc = new RgbYcc();
    for ( row = 0; row < image.height; row += 16 ) {
      for ( col = 0; col < image.width; col += 16 ) {
        int m = get_dct_yccblocks( ycbcr_macro, in );
        if ( m <= 0 ) { System.out.printf("\nout of dct data\n"); return m;}
        n += m;
        rgbycc.ycbcr2macroblock( ycbcr_macro, rgb_macro );
        k = row * image.width + col;
        r = 0;
        for ( i = 0; i < 16; ++i ) {
          for ( j = 0; j < 16; ++j ) {
            image.ibuf[k].R = rgb_macro.rgb[r].R;
            image.ibuf[k].G = rgb_macro.rgb[r].G;
            image.ibuf[k].B = rgb_macro.rgb[r].B;
            k++;  r++;
          }
          k += (image.width - 16);   //points to next row of macroblock
        }
      } //for col
    }  //for row
    return n;
  }
}
