"use strict";



function H265SPSParser() {
    var vBitCount = 0;
    var spsMap = null;
    var pSPSBytes = null;

    function Constructor() {
        vBitCount = 0;
        spsMap = new Map();
    }

    function get_bit(base, offset) {
        var vCurBytes = (vBitCount + offset) >> 3;
        offset = (vBitCount + offset) & 0x00000007;
        return (((base[(vCurBytes)])) >> (0x7 - (offset & 0x7))) & 0x1;
    }

    function read_bits(pBuf, vReadBits) {
        var vCurBytes = vBitCount / 8;
        var vCurBits = vBitCount % 8;
        var vOffset = 0;
        var vTmp = 0,
            vTmp2 = 0;

        if (vReadBits == 1) {
            vTmp = get_bit(pBuf, vOffset);
        } else {
            for (var i = 0; i < vReadBits; i++) {
                vTmp2 = get_bit(pBuf, i);
                vTmp = (vTmp << 1) + vTmp2;
            }
        }

        vBitCount += vReadBits;
        return vTmp;
    }

    function ue(base, offset) {
        var zeros = 0,
            vTmp = 0,
            vReturn = 0;
        var vIdx = offset;
        do {
            vTmp = get_bit(base, vIdx++);
            if (vTmp == 0)
                zeros++;
        } while (0 == vTmp);

        if (zeros == 0) {
            vBitCount += 1;
            return 0;
        }

        // insert first 1 bit
        vReturn = 1 << zeros;

        for (var i = zeros - 1; i >= 0; i-- , vIdx++) {
            vTmp = get_bit(base, vIdx);
            vReturn |= vTmp << i;
        }

        vBitCount += zeros * 2 + 1;

        return (vReturn - 1);
    }

    function se(base, offset) {
        var vReturn = ue(base, offset);

        if (vReturn & 0x1) {
            return (vReturn + 1) / 2;
        } else {
            return -vReturn / 2;
        }
    }

    function byte_aligned() {
        if ((vBitCount & 0x00000007) == 0)
            return 1;
        else
            return 0;
    }

    function profile_tier_level(profilePresentFlag, maxNumSubLayersMinus1) {
        if (profilePresentFlag) {
            spsMap.set("general_profile_space", read_bits(pSPSBytes, 2));
            spsMap.set("general_tier_flag", read_bits(pSPSBytes, 1));
            spsMap.set("general_profile_idc", read_bits(pSPSBytes, 5));
            var generalProfileCompatibilityFlag = new Array(32);

            for (var j = 0; j < 32; j++) {
                generalProfileCompatibilityFlag[j] = read_bits(pSPSBytes, 1);
            }

            spsMap.set("general_progressive_source_flag", read_bits(pSPSBytes, 1));
            spsMap.set("general_interlaced_source_flag", read_bits(pSPSBytes, 1));
            spsMap.set("general_non_packed_constraint_flag", read_bits(pSPSBytes, 1));
            spsMap.set("general_frame_only_constraint_flag", read_bits(pSPSBytes, 1));

            var generalProfileIdc = spsMap.get("general_profile_idc");
            if (generalProfileIdc === 4 || generalProfileCompatibilityFlag[4] ||
                generalProfileIdc === 5 || generalProfileCompatibilityFlag[5] ||
                generalProfileIdc === 6 || generalProfileCompatibilityFlag[6] ||
                generalProfileIdc === 7 || generalProfileCompatibilityFlag[7]) {
                spsMap.set("general_max_12bit_constraint_flag", read_bits(pSPSBytes, 1));
                spsMap.set("general_max_10bit_constraint_flag", read_bits(pSPSBytes, 1));
                spsMap.set("general_max_8bit_constraint_flag", read_bits(pSPSBytes, 1));
                spsMap.set("general_max_422chroma_constraint_flag", read_bits(pSPSBytes, 1));
                spsMap.set("general_max_420chroma_constraint_flag", read_bits(pSPSBytes, 1));
                spsMap.set("general_max_monochrome_constraint_flag", read_bits(pSPSBytes, 1));
                spsMap.set("general_intra_constraint_flag", read_bits(pSPSBytes, 1));
                spsMap.set("general_one_picture_only_constraint_flag", read_bits(pSPSBytes, 1));
                spsMap.set("general_lower_bit_rate_constraint_flag", read_bits(pSPSBytes, 1));
                spsMap.set("general_reserved_zero_34bits", read_bits(pSPSBytes, 34));
            } else {
                spsMap.set("general_reserved_zero_43bits", read_bits(pSPSBytes, 43));
            }

            if ((generalProfileIdc >= 1 && generalProfileIdc <= 5) ||
                generalProfileCompatibilityFlag[1] || generalProfileCompatibilityFlag[2] ||
                generalProfileCompatibilityFlag[3] || generalProfileCompatibilityFlag[4] ||
                generalProfileCompatibilityFlag[5]) {
                /* The number of bits in this syntax structure is not affected by this condition */
                spsMap.set("general_inbld_flag", read_bits(pSPSBytes, 1));
            } else {
                spsMap.set("general_reserved_zero_bit", read_bits(pSPSBytes, 1));
            }
        }

        spsMap.set("general_level_idc", read_bits(pSPSBytes, 8));
        var subLayerProfilePresentFlag = new Array(maxNumSubLayersMinus1);
        var subLayerLevelPresentFlag = new Array(maxNumSubLayersMinus1);

        for (i = 0; i < maxNumSubLayersMinus1; i++) {
            subLayerProfilePresentFlag[i] = read_bits(pSPSBytes, 1);
            subLayerLevelPresentFlag[i] = read_bits(pSPSBytes, 1);
        }

        var reservedZero2bits = new Array(8);
        var subLayerProfileIdc = new Array(maxNumSubLayersMinus1);

        if (maxNumSubLayersMinus1 > 0) {
            for (var i = maxNumSubLayersMinus1; i < 8; i++) {
                reservedZero2bits[i] = read_bits(pSPSBytes, 2);
            }
        }

        for (var i = 0; i < maxNumSubLayersMinus1; i++) {
            if (subLayerProfilePresentFlag[i]) {
                spsMap.set("sub_layer_profile_space", read_bits(pSPSBytes, 2));
                spsMap.set("sub_layer_tier_flag", read_bits(pSPSBytes, 1));
                subLayerProfileIdc[i] = read_bits(pSPSBytes, 5);

                for (var j = 0; j < 32; j++) {
                    subLayerProfileCompatibilityFlag[i][j] = read_bits(pSPSBytes, 1);
                }

                spsMap.set("sub_layer_progressive_source_flag", read_bits(pSPSBytes, 1));
                spsMap.set("sub_layer_interlaced_source_flag", read_bits(pSPSBytes, 1));
                spsMap.set("sub_layer_non_packed_constraint_flag", read_bits(pSPSBytes, 1));
                spsMap.set("sub_layer_frame_only_constraint_flag", read_bits(pSPSBytes, 1));

                if (subLayerProfileIdc[i] === 4 || subLayerProfileCompatibilityFlag[i][4] ||
                    subLayerProfileIdc[i] === 5 || subLayerProfileCompatibilityFlag[i][5] ||
                    subLayerProfileIdc[i] === 6 || subLayerProfileCompatibilityFlag[i][6] ||
                    subLayerProfileIdc[i] === 7 || subLayerProfileCompatibilityFlag[i][7]) {
                    /* The number of bits in this syntax structure is not affected by this condition */
                    spsMap.set("sub_layer_max_12bit_constraint_flag", read_bits(pSPSBytes, 1));
                    spsMap.set("sub_layer_max_10bit_constraint_flag", read_bits(pSPSBytes, 1));
                    spsMap.set("sub_layer_max_8bit_constraint_flag", read_bits(pSPSBytes, 1));
                    spsMap.set("sub_layer_max_422chroma_constraint_flag", read_bits(pSPSBytes, 1));
                    spsMap.set("sub_layer_max_420chroma_constraint_flag", read_bits(pSPSBytes, 1));
                    spsMap.set("sub_layer_max_monochrome_constraint_flag", read_bits(pSPSBytes, 1));
                    spsMap.set("sub_layer_intra_constraint_flag", read_bits(pSPSBytes, 1));
                    spsMap.set("sub_layer_one_picture_only_constraint_flag", read_bits(pSPSBytes, 1));
                    spsMap.set("sub_layer_lower_bit_rate_constraint_flag", read_bits(pSPSBytes, 1));
                    spsMap.set("sub_layer_reserved_zero_34bits", read_bits(pSPSBytes, 34));
                } else {
                    spsMap.set("sub_layer_reserved_zero_43bits", read_bits(pSPSBytes, 43));
                }

                if ((subLayerProfileIdc[i] >= 1 && subLayerProfileIdc[i] <= 5) ||
                    subLayerProfileCompatibilityFlag[1] || subLayerProfileCompatibilityFlag[2] ||
                    subLayerProfileCompatibilityFlag[3] || subLayerProfileCompatibilityFlag[4] ||
                    subLayerProfileCompatibilityFlag[5]) {
                    /* The number of bits in this syntax structure is not affected by this condition */
                    spsMap.set("sub_layer_inbld_flag", read_bits(pSPSBytes, 1));
                } else {
                    spsMap.set("sub_layer_reserved_zero_bit", read_bits(pSPSBytes, 1));
                }
            }

            if (subLayerLevelPresentFlag[i]) {
                spsMap.set("sub_layer_level_idc", read_bits(pSPSBytes, 8));
            }
        }
    }

    Constructor.prototype = {
        parse: function (spsPayload) {
            pSPSBytes = spsPayload;
            //console.log("=========================SPS START=========================");
            vBitCount = 0;
            spsMap.clear();

            spsMap.set("forbidden_zero_bit", read_bits(pSPSBytes, 1));
            spsMap.set("nal_unit_type", read_bits(pSPSBytes, 6));
            spsMap.set("nuh_layer_id", read_bits(pSPSBytes, 6));
            spsMap.set("nuh_temporal_id_plus1", read_bits(pSPSBytes, 3));

            spsMap.set("sps_video_parameter_set_id", read_bits(pSPSBytes, 4));
            if (spsMap.get("nuh_layer_id") === 0) {
                spsMap.set("sps_max_sub_layers_minus1", read_bits(pSPSBytes, 3));
            } else {
                spsMap.set("sps_ext_or_max_sub_layers_minus1", read_bits(pSPSBytes, 3));
            }
            var MultiLayerExtSpsFlag = (spsMap.get("nuh_layer_id") !== 0 && spsMap.get("sps_ext_or_max_sub_layers_minus1") === 7);

            if (!MultiLayerExtSpsFlag) {
                spsMap.set("sps_max_sub_layers_minus1", read_bits(pSPSBytes, 1));
                //profile_tier_level(1, spsMap.get("sps_max_sub_layers_minus1"));
            }

            read_bits(pSPSBytes, 84);

            spsMap.set("sps_seq_parameter_set_id", ue(pSPSBytes, 0));
            if (MultiLayerExtSpsFlag) {
                spsMap.set("update_rep_format_flag", read_bits(pSPSBytes, 1));
                if (spsMap.get("update_rep_format_flag")) {
                    spsMap.set("sps_rep_format_idx", read_bits(pSPSBytes, 8));
                }
            } else {
                spsMap.set("chroma_format_idc", ue(pSPSBytes, 0));

                if (spsMap.get("chroma_format_idc") === 3) {
                    spsMap.set("separate_colour_plane_flag", read_bits(pSPSBytes, 1));
                }

                spsMap.set("pic_width_in_luma_samples", ue(pSPSBytes, 0));
                spsMap.set("pic_height_in_luma_samples", ue(pSPSBytes, 0));

                spsMap.set("conformance_window_flag", read_bits(pSPSBytes, 1));

                if (spsMap.get("conformance_window_flag")) {
                    spsMap.set("conf_win_left_offset", ue(pSPSBytes, 0));
                    spsMap.set("conf_win_right_offset", ue(pSPSBytes, 0));
                    spsMap.set("conf_win_top_offset", ue(pSPSBytes, 0));
                    spsMap.set("conf_win_bottom_offset", ue(pSPSBytes, 0));
                }
            }

            //console.log("=========================SPS END=========================");
            return true;
        },
        getSizeInfo: function () {
            var width = spsMap.get("pic_width_in_luma_samples");
            var height = spsMap.get("pic_height_in_luma_samples");
            if (spsMap.get("conformance_window_flag")) {
                var chromaFormatIdc = spsMap.get("chroma_format_idc");
                var separateColourPlaneFlag = spsMap.get("separate_colour_plane_flag");
                if (typeof separateColourPlaneFlag === "undefined") {
                    separateColourPlaneFlag = 0;
                }
                var subWidthC = ((1 === chromaFormatIdc) || (2 === chromaFormatIdc)) && (0 === separateColourPlaneFlag) ? 2 : 1;
                var subHeightC = (1 === chromaFormatIdc) && (0 === separateColourPlaneFlag) ? 2 : 1;
                width -= (subWidthC * spsMap.get("conf_win_right_offset") + subWidthC * spsMap.get("conf_win_left_offset"));
                height -= (subHeightC * spsMap.get("conf_win_bottom_offset") + subHeightC * spsMap.get("conf_win_top_offset"));
            }
            var decodeSize = width * height;

            var sizeInfo = {
                'width': width,
                'height': height,
                'decodeSize': decodeSize
            };

            return sizeInfo;
        },
        getSpsValue: function (key) {
            return spsMap.get(key);
        },
    };
    return new Constructor();
}
