/*
 * RefVector.java
 * Reference Vector used for DPCM Encoding and Decoding.
 */

import java.io.*;
import java.util.*;

class RefVector {
  private CircularQueue buf;  //shared buffer
  private YCbCr_MACRO m; 
  public Vector<YCbCr_MACRO>  ycc_refv; //vector holding YCbCr macroblocks

  //constructor
  public RefVector ( CircularQueue q )
  {
    buf = q;
    //initialize the YCbCr Macro reference vectors
    // calculate the number of macroblocks in one frame
    int n = ( buf.width/16 ) * ( buf.height / 16 );
    System.out.printf("Total macros in one frame = %d\n", n);
    //set reference frame values to 0
    ycc_refv = new Vector<YCbCr_MACRO>();
    for ( int i = 0; i < n; i++ ){
      m = new YCbCr_MACRO();
      for ( int ii = 0; ii < 256; ii++ ){
        m.Y[ii] = 0;
        if ( ii < 64 )
          m.Cb[ii] = m.Cr[ii] = 0;
      }
      ycc_refv.add( m );   //push_back, capacity automatically expanded
    }
  }

  //Calculates residuals of macroblock pointed by mp.
  //Returns results through diff.
  public void calculate_diff (  YCbCr_MACRO mp, YCbCr_MACRO diff, int nm )
  {
    for ( int i = 0; i < 256; ++i ) {
      diff.Y[i] = mp.Y[i] -  ycc_refv.get(nm).Y[i];
    }
    for ( int i = 0; i < 64; ++i ) {
      diff.Cb[i] =  mp.Cb[i] -  ycc_refv.get(nm).Cb[i];
      diff.Cr[i] =  mp.Cr[i] -  ycc_refv.get(nm).Cr[i];
    }
  }

  //reconstructs YCbCr macroblock from quantized DCT coefficients
  //save reconstructed block in ycc_refv[nm]
  public void reconstruct_macro ( short dctcoefs[][][], int nm )
  {
    int [][] X = new int[8][8], Y = new int[8][8];
    YCbCr_MACRO diff_macro = new YCbCr_MACRO();
    short py;
    int i, j, k = 0;
    Quantizer quantizer = new Quantizer();
    DctVideo dct_idct = new DctVideo();

    for ( int b = 0; b < 4; ++b ){        //Y blocks
      quantizer.inverse_quantize_block ( dctcoefs[b] );
      for ( i = 0; i < 8; i++ )
        for ( j = 0; j < 8; j++ )
          Y[i][j] = dctcoefs[b][i][j];
      dct_idct.idct ( Y, X );
      k = 0;
      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++ ){
          diff_macro.Y[k+j] =  X[i][j];
        } 
      } 
    }  //for b

    //Cb
    quantizer.inverse_quantize_block ( dctcoefs[4] );
    for (  i = 0; i < 8; i++ )
        for ( j = 0; j < 8; j++ )
          Y[i][j] =  dctcoefs[4][i][j];
    dct_idct.idct( Y, X );
    k = 0;
    for ( i = 0; i < 8; i++ ) {
      for ( j = 0; j < 8; j++ ) {
        diff_macro.Cb[k] = X[i][j];
        k++;
      }
    }
    //Cr
    quantizer.inverse_quantize_block ( dctcoefs[5] );
    for (  i = 0; i < 8; i++ )
        for ( j = 0; j < 8; j++ )
          Y[i][j] =  dctcoefs[5][i][j];
    dct_idct.idct( Y, X );
    k = 0;
    for ( i = 0; i < 8; i++ ) {
      for ( j = 0; j < 8; j++ ) {
        diff_macro.Cr[k] = X[i][j];
        k++;
      }
    }

    YCbCr_MACRO aMacro;  
    aMacro = ycc_refv.get(nm);
    for ( i = 0; i < 256; i++ ){
      aMacro.Y[i] += diff_macro.Y[i];
      if ( i < 64  ) {
        aMacro.Cb[i] += diff_macro.Cb[i];
        aMacro.Cr[i] += diff_macro.Cr[i];
      }
    }
    ycc_refv.set( nm, aMacro );
  }


  //return the requested YCbCr macroblock
  public YCbCr_MACRO get ( int nm )
  {
    return ycc_refv.get(nm);
  }
}
