/*
 * Copyright (c) 1997-2004 Alexandros Eleftheriadis, Danny Hong and
 * Yuntai Kyong.
 * 
 * This file is part of Flavor, developed at Columbia University
 * (www.ee.columbia.edu/flavor).
 *
 * Flavor is free software; you can redistribute it and/or modify
 * it under the terms of the Flavor Artistic License as described in
 * the file COPYING.txt. 
 *
 */

/* 
 * Authors:
 * Danny Hong <danny@ee.columbia.edu>
 * Chun Hao (Tony) Wang <tony.wang@ee.cooper.edu>
 *
 */

/*
 * MPEG-1 Video
 *
 * This example reads MPEG-1 Video streams.
 *
 */


%pragma notrace

%import "mpeg1vsmap.fl"

const int PICTURE_START_CODE    = 0x00000100;
const int USER_DATA_START_CODE  = 0x000001B2;
const int SEQUENCE_HEADER_CODE  = 0x000001B3;
const int EXTENSION_START_CODE  = 0x000001B5;
const int SEQUENCE_END_CODE     = 0x000001B7;
const int GROUP_START_CODE      = 0x000001B8;

class Block(int i, int pattern_code, int intra, int picture_coding_type) {
    if (pattern_code == 1) {
        if (intra == 1) {
            if (i < 4) {
                int(TableB5a) dct_dc_size_luminance;  
                if (dct_dc_size_luminance != 0)
                    unsigned int(dct_dc_size_luminance) dct_dc_differential;
            }
            else {
                int(TableB5b) dct_dc_size_chrominance;
                if (dct_dc_size_chrominance != 0)
                    unsigned int(dct_dc_size_chrominance) dct_dc_differential;
            }
        }
        else {
            DCTCoeff(FirstCoeffTable) dct_coeff_first;
            if (dct_coeff_first.level == 0) {
                unsigned int(8) level_lsb;
                dct_coeff_first.level = level_lsb;
            }
            else if (dct_coeff_first.level == -128) {
                unsigned int(8) level_lsb;
                dct_coeff_first.level = level_lsb - 256;
            }
        }                
        if (picture_coding_type != 4) {
            while (nextbits(2) != 0b10) {
                DCTCoeff(NextCoeffTable) dct_coeff_next;
                if (dct_coeff_next.level == 0) {
                    unsigned int(8) level_lsb;
                    dct_coeff_next.level = level_lsb;
                }
                else if (dct_coeff_next.level == -128) {
                    unsigned int(8) level_lsb;
                    dct_coeff_next.level = level_lsb - 256;
                }
            }
            bit(2) end_of_block = 0b10;
        }
    }
}

class Macroblock(int forward_f_code, int backward_f_code, int picture_coding_type) {
    while (nextbits(11) == 0b0000.0001.111)
        skipbits(11);   // Skip stuffing bits
    while (nextbits(11) == 0b0000.0001.000) 
        skipbits(11);   // Skip escape bits

    int(TableB1) macroblock_address_increment;
	
    switch (picture_coding_type) {
    case 0b001: // Intra-coded (I)
        MacroblockTypes(TableB2a) macroblock_type;
        break;
    case 0b010: // Predictive-coded (P)
        MacroblockTypes(TableB2b) macroblock_type;
        break;
    case 0b011: // Bidirectionally-predictive-coded (B)
        MacroblockTypes(TableB2c) macroblock_type;
        break;
    case 0b100: // DC intra_coded (D)
        MacroblockTypes(TableB2d) macroblock_type;
        break;
    }
	
    if (macroblock_type.quant != 0)
        bit(5) quantizer_scale;

    if (macroblock_type.motion_forward != 0) {
        int(TableB4) motion_horizontal_forward_code;
        if ((1<<(forward_f_code-1)) != 1 && motion_horizontal_forward_code != 0)
            bit(forward_f_code-1) motion_horizontal_forward_r;

        int(TableB4) motion_vertical_forward_code;
        if ((1<<(forward_f_code-1)) != 1 && motion_vertical_forward_code != 0)
            bit(forward_f_code-1) motion_vertical_forwrad_r;
    }
    if (macroblock_type.motion_backward != 0) {
        int(TableB4) motion_horizontal_backward_code;
        if ((1<<(backward_f_code-1)) != 1 && motion_horizontal_backward_code != 0)
            bit(backward_f_code-1) motion_horizontal_forward_r;

        int(TableB4) motion_vertical_backward_code;
        if ((1<<(backward_f_code-1)) != 1 && motion_vertical_backward_code != 0)
            bit(backward_f_code-1) motion_vertical_backward_r;
    }

    int i;
    int pattern_code[6] = (macroblock_type.intra != 0) ? 1 : 0;
    if (macroblock_type.pattern != 0) {
        int(TableB3) coded_block_pattern;
        for (i=0; i<6; i++) 
            if ((coded_block_pattern & (1<<(5-i))) != 0) pattern_code[i] = 1;
    }

    for (i=0; i<6; i++) 
        Block b(i, pattern_code[i], macroblock_type.intra, picture_coding_type);
        	
    if (picture_coding_type == 4)
       bit(1) end_of_macroblock = 0b1;
}

class Slice(int forward_f_code, int backward_f_code, int picture_coding_type) {
    bit(32) slice_start_code = 0x00000101 .. 0x000001AF;
    bit(5) quantizer_scale;

    while (nextbits(1) == 0b1) {
        bit(1) extra_bit_slice = 0b1;
        bit(8) extra_information_slice;
    }
    bit(1) extra_bit_slice = 0b0;
    do {
        Macroblock m(forward_f_code, backward_f_code, picture_coding_type);
    } while(nextbits(23) != 0);
    nextcode(aligned(8), 0x000001);
}

%pragma trace

class Picture {
    bit(32) picture_start_code = PICTURE_START_CODE;
    bit(10) temporal_reference;
    bit(3) picture_coding_type;
    bit(16) vbv_delay;
	
    if (picture_coding_type == 2 || picture_coding_type == 3) {
        bit(1) full_pel_forward_vector;
        bit(3) forward_f_code;
    }
    if (picture_coding_type == 3) {
        bit(1) full_pel_backward_vector;
        bit(3) backward_f_code;
    }
    while (nextbits(1) == 0b1) {
        bit(1) extra_bit_picture = 0b1;
        bit(8) extra_information_picture;
    }
    bit(1) extra_bit_picture = 0b0;

    nextcode(aligned(8), 0x000001);	
    if (nextbits(32) == EXTENSION_START_CODE) {
        bit(32) extension_start_code = EXTENSION_START_CODE;
        // We are skipping picture_extension_data
        nextcode(aligned(8), 0x000001);
    }
    if (nextbits(32) == USER_DATA_START_CODE) {
        bit(32) extension_start_code = USER_DATA_START_CODE;
        // We are skipping user_data
        nextcode(aligned(8), 0x000001);
    }
    do {
        Slice s(forward_f_code, backward_f_code, picture_coding_type);
    } while(nextbits(32) >= 0x00000101 && nextbits(32) <= 0x000001AF);
}

class GroupOfPictures {
    bit(32) group_start_code = GROUP_START_CODE;
    bit(25) time_code;
    bit(1) closed_gop;
    bit(1) broken_link;

    nextcode(aligned(8), 0x000001);
    if (nextbits(32) == EXTENSION_START_CODE) {
        bit(32) extension_start_code = EXTENSION_START_CODE;
        // We are skipping group_extension_data
        nextcode(aligned(8), 0x000001);
    }
    if (nextbits(32) == USER_DATA_START_CODE) {
        bit(32) extension_start_code = USER_DATA_START_CODE;
        // We are skipping user_data
        nextcode(aligned(8), 0x000001);
    }
    while(nextbits(32) == PICTURE_START_CODE)
        Picture p;
}

class SequenceHeader {
    int i;

    bit(32) sequence_header_code = SEQUENCE_HEADER_CODE;
    bit(12) horizontal_size;
    bit(12) vertical_size;
    bit(4) pel_aspect_ratio;
    bit(4) picture_rate;
    bit(18) bit_rate;
    bit(1) marker_bit = 0b1;
    bit(10) vbv_buffer_size;
    bit(1) constrained_parameters_flag;
	
    bit(1) load_intra_quantizer_matrix;
    if (load_intra_quantizer_matrix != 0)
        bit(8) intra_quantizer_matrix[64];

    bit(1) load_non_intra_quantizer_matrix;
    if (load_non_intra_quantizer_matrix != 0)
        bit(8) non_intra_quantizer_matrix[64];

    nextcode(aligned(8), 0x000001);
    if (nextbits(32) == EXTENSION_START_CODE) {
        bit(32) extension_start_code = EXTENSION_START_CODE;
        // We are skipping sequence_extension_data
        nextcode(aligned(8), 0x000001);
    }	
    if (nextbits(32) == USER_DATA_START_CODE) {
        bit(32) user_data_start_code = USER_DATA_START_CODE;
        // We are skipping user_data
        nextcode(aligned(8), 0x000001);
    }
}

////////////////////
// The main class //
////////////////////
class MPEG1Video {
    nextcode(aligned(8), 0x000001);

    do {
        SequenceHeader sh;
        do {
            GroupOfPictures gp;
        } while (nextbits(32) == GROUP_START_CODE);
    } while (nextbits(32) == SEQUENCE_HEADER_CODE);

    bit(32) sequence_end_code = SEQUENCE_END_CODE;
}