/*
 * Copyright 2016 Intel Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// This file contains an implementation of a Mpeg2 raw stream parser,
// as defined in ISO/IEC 13818-2
//
// parser is highly influenced on the way other parsers are written in
// this project in particular vp8_parser.
//
// The parser is built under the logic that the client will separate the
// input stream in chunks of information separated by start codes.
// Client can decide if partial start_code streams are permitted and owns
// the decision to parse partial information

#ifndef mpeg2_parser_h
#define mpeg2_parser_h

// local headers
#include "common/NonCopyable.h"
#include "bitReader.h"
#include "VideoCommonDefs.h"

// system headers
#include <stdio.h>
#include <string.h>

namespace YamiParser {
namespace MPEG2 {

    // See spec for definitions of values/fields.

    enum PictureCodingType {
        kIFrame = 1,
        kPFrame,
        kBFrame,
    };

    enum PictureStructure {
        kTopField = 1,
        kBottomField,
        kFramePicture,
    };

    enum StartCodeSize {
        kStartCodePrefixSize = 0,
        kStartCodeSize,
    };

    enum ExtensionIdentifierType {
        kSequence = 1,
        kSequenceDisplay,
        kQuantizationMatrix,
        kSequenceScalable = 5,
        kPictureDisplay = 7,
        kPictureCoding,
        kPictureSpatialScalable,
        kPictureTempralScalable,
    };

    enum StartCodeType {
        MPEG2_INVALID_START_CODE = -1,
        MPEG2_PICTURE_START_CODE = 0x00,
        MPEG2_SLICE_START_CODE_MIN,
        MPEG2_SLICE_START_CODE_MAX = 0xaf,
        MPEG2_RESERVED_CODE_0,
        MPEG2_RESERVED_CODE_1,
        MPEG2_USER_DATA_START_CODE,
        MPEG2_SEQUENCE_HEADER_CODE,
        MPEG2_SEQUENCE_ERROR_CODE,
        MPEG2_EXTENSION_START_CODE,
        MPEG2_RESERVED_CODE_2,
        MPEG2_SEQUENCE_END_CODE,
        MPEG2_GROUP_START_CODE,
    };

    enum SystemStartCodeType {
        MPEG2_PROGRAM_END_CODE = 0xb9,
        MPEG2_PACK_HEADER_CODE,
        MPEG2_SYSTEM_HEADER_CODE,
        MPEG2_PROGRAM_STREAM_MAP_CODE,
        MPEG2_PRIVATE_STREAM_CODE_1,
        MPEG2_PADDING_STREAM_CODE,
        MPEG2_PRIVATE_STREAM_CODE_2,
        MPEG2_AUDIO_PES_CODE_MIN = 0xc0,
        MPEG2_AUDIO_PES_CODE_MAX = 0xdf,
        MPEG2_VIDEO_PES_CODE_MIN = 0xe0,
        MPEG2_VIDEO_PES_CODE_MAX = 0xef,
        MPEG2_ECM_STREAM_CODE = 0xf0,
        MPEG2_EMM_STREAM_CODE,
        MPEG2_DSMCC_STREAM_CODE,
        MPEG2_13522_STREAM_CODE,
        MPEG2_H_222_TYPE_A_CODE,
        MPEG2_H_222_TYPE_B_CODE,
        MPEG2_H_222_TYPE_C_CODE,
        MPEG2_H_222_TYPE_D_CODE,
        MPEG2_H_222_TYPE_E_CODE,

    };

    enum ParserResult {
        MPEG2_PARSER_OK = 0,
        MPEG2_PARSER_BROKEN_DATA,
        MPEG2_PARSER_ERROR,
    };

    enum ProfileType {
        MPEG2_PROFILE_HIGH = 1,
        MPEG2_PROFILE_SPATIALLY_SCALABLE,
        MPEG2_PROFILE_SNR_SCALABLE,
        MPEG2_PROFILE_MAIN,
        MPEG2_PROFILE_SIMPLE,
    };

    enum LevelType {
        MPEG2_LEVEL_HIGH = 4,
        MPEG2_LEVEL_HIGH_1440 = 6,
        MPEG2_LEVEL_MAIN = 8,
        MPEG2_LEVEL_LOW = 10,
    };


    struct QuantMatrices {
        QuantMatrices();

        bool load_intra_quantiser_matrix;
        uint8_t intra_quantiser_matrix[64];
        bool load_non_intra_quantiser_matrix;
        uint8_t non_intra_quantiser_matrix[64];
        bool load_chroma_intra_quantiser_matrix;
        uint8_t chroma_intra_quantiser_matrix[64];
        bool load_chroma_non_intra_quantiser_matrix;
        uint8_t chroma_non_intra_quantiser_matrix[64];

    };

    // ISO/IEC spec section 6.2.2.1
    struct SeqHeader {
        SeqHeader();

        uint32_t horizontal_size_value;
        uint32_t vertical_size_value;
        uint32_t aspect_ratio_info;
        uint32_t frame_rate_code;
        uint32_t bit_rate_value;
        bool marker_bit;
        uint32_t vbv_buffer_size_value;
        bool constrained_params_flag;
        QuantMatrices quantizationMatrices;
    };

    // ISO/IEC spec section 6.2.2.3
    struct SeqExtension {
        SeqExtension();

        uint32_t profile_and_level_indication;
        bool progressive_sequence;
        uint32_t chroma_format;
        uint32_t horizontal_size_extension;
        uint32_t vertical_size_extension;
        uint32_t bit_rate_extension;
        bool marker_bit;
        uint32_t vbv_buffer_size_extension;
        bool low_delay;
        uint32_t frame_rate_extension_n;
        uint32_t frame_rate_extension_d;
        //calclated value;
        uint32_t m_width;
        uint32_t m_height;
    };

    // ISO/IEC spec section 6.2.2.6 and 6.3.9
    struct GOPHeader {
        GOPHeader();

        bool drop_frame_flag;
        uint32_t time_code_hours;
        uint32_t time_code_minutes;
        bool marker_bit;
        uint32_t time_code_seconds;
        uint32_t time_code_pictures;
        bool closed_gop;
        bool broken_link;
    };

    // ISO/IEC spec section 6.2.3
    struct PictureHeader {
        PictureHeader();

        uint32_t temporal_reference;
        uint32_t picture_coding_type;
        uint32_t vbv_delay;
        bool full_pel_forward_vector;
        uint32_t forward_f_code;
        bool full_pel_backward_vector;
        uint32_t backward_f_code;
        bool extra_bit_picture;
    };

    // ISO/IEC spec section 6.2.3.1
    struct PictureCodingExtension {
        PictureCodingExtension();
        uint32_t getFCode();

        uint32_t f_code[2][2]; // forward_horizontal
        // forward_vertical
        // backward_horizontal
        // backward_vertical
        uint32_t intra_dc_precision;
        uint32_t picture_structure;
        bool top_field_first;
        bool frame_pred_frame_dct;
        bool concealment_motion_vectors;
        bool q_scale_type;
        bool intra_vlc_format;
        bool alternate_scan;
        bool repeat_first_field;
        bool chrome_420_type;
        bool progressive_frame;
        bool composite_display_flag;
        bool v_axis;
        uint32_t field_sequence;
        bool sub_carrier;
        uint32_t burst_amplitude;
        uint32_t sub_carrier_phase;
    };

    struct Slice {
        Slice();

        // helper variables
        const uint8_t* sliceData;
        uint64_t sliceDataSize;
        uint64_t sliceHeaderSize; // in bits
        uint32_t verticalPosition;
        uint32_t macroblockRow;
        uint32_t macroblockColumn;
        // spec variables
        uint32_t slice_vertical_position_extension;
        uint32_t priority_breakpoint;
        uint32_t quantiser_scale_code;
        bool intra_slice_flag;
        bool intra_slice;
        uint32_t reserved_bits;
        bool extra_bit_slice;
        uint32_t extra_information_slice;

        bool isFirstSlice()
        {
            return !macroblockRow && !macroblockColumn;
        }
    };

    class DecodeUnit {
    public:
        bool parse(const uint8_t* data, int32_t size);
        bool isSlice();

        const uint8_t* m_data;
        int32_t m_size;
        StartCodeType m_type;
    };

    // A parser for raw Mpeg2 streams as specified in ISO/IEC 13818-2.
    class Parser {
    public:
        Parser();
        ~Parser();

        // Try to parse exactly the information of interest in a Mpeg2 raw
        // stream
        // Client can seek for the next start code and then parse it as required

        // parseSequenceHeader will parse a SequenceHeader according to the spec
        // storing the information in the m_sequenceHdr structure and updating
        // the position to the last bit parsed. information will be returned
        bool parseSequenceHeader(const DecodeUnit&, SharedPtr<QuantMatrices>&);

        // parseSequenceExtension will parse a SequenceExtension according to
        // the
        // spec
        // storing the information in the sequence_extension structure and
        // updating
        // the position to the last bit parsed.
        bool parseSequenceExtension(BitReader&);

        // parseGOPHeader will parse a GOPHeader according to the spec storing
        // the information in the gop_header_ structure and updating the
        // position
        // to the last bit parsed.
        bool parseGOPHeader(const DecodeUnit&);

        // parsePictureHeader will parse a PictureHeader according to the spec
        // storing
        // the information in the m_pictureHeader_ structure and updating the
        // position
        // to the last bit parsed.
        bool parsePictureHeader(const DecodeUnit&);

        // parsePictureCodingExtension will parse a PictureHeader according to
        // the
        // spec storing the information in the picture_coding_extension_
        // structure
        // and updating the position to the last bit parsed.
        bool parsePictureCodingExtension(BitReader&);

        // parseQuantMatrixExtension will parse a Quant Matrix Extension ID
	// within a Extension Start Code and keep it on the quantization
	// Extension structure for later use
        bool parseQuantMatrixExtension(BitReader&, SharedPtr<QuantMatrices>&);

        // parseSlice will parse a Slice according to the spec storing the
        // information
        // in the Slice structure and updating the position to the last
        // bit parsed.
        bool parseSlice(Slice&, const DecodeUnit&);

        uint32_t getWidth();
        uint32_t getHeight();
        ProfileType getProfile();
        LevelType getLevel();

        SeqHeader m_sequenceHdr;
        SeqExtension m_sequenceExtension;
        GOPHeader m_GOPHeader;
        PictureHeader m_pictureHeader;
        PictureCodingExtension m_pictureCodingExtension;

    private:
        friend class MPEG2ParserTest;
        SharedPtr<QuantMatrices> m_quantMatrices;

        bool readQuantMatrix(bool& loadMatrix, uint8_t matrix[], BitReader&);
        bool calculateMBColumn(Slice&, BitReader&);

        DISALLOW_COPY_AND_ASSIGN(Parser);
    };

} // namespace MPEG2
} // namespace YamiParser

#endif // mpeg2_parser_h
