/*! @license
 * Shaka Player
 * Copyright 2016 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */

goog.provide('shaka.transmuxer.Ac3');


/**
 * AC3 utils
 */
shaka.transmuxer.Ac3 = class {
  /**
   * @param {!Uint8Array} data
   * @param {!number} offset
   * @param {!shaka.transmuxer.Ac3.Ac3Frame} frame
   * @return {boolean}
   */
  static parseFrame(data, offset, frame) {
    if (offset + 8 > data.length) {
      // not enough bytes left
      return false;
    }

    if (data[offset] !== 0x0b || data[offset + 1] !== 0x77) {
      // invalid magic
      return false;
    }

    // get sample rate
    const samplingRateCode = data[offset + 4] >> 6;
    if (samplingRateCode >= 3) {
      // invalid sampling rate
      return false;
    }

    const samplingRateMap = [48000, 44100, 32000];

    // get frame size
    const frameSizeCode = data[offset + 4] & 0x3f;
    const frameSizeMap = [
      64, 69, 96, 64, 70, 96, 80, 87, 120, 80, 88, 120, 96, 104, 144, 96, 105,
      144, 112, 121, 168, 112, 122, 168, 128, 139, 192, 128, 140, 192, 160,
      174, 240, 160, 175, 240, 192, 208, 288, 192, 209, 288, 224, 243, 336,
      224, 244, 336, 256, 278, 384, 256, 279, 384, 320, 348, 480, 320, 349,
      480, 384, 417, 576, 384, 418, 576, 448, 487, 672, 448, 488, 672, 512,
      557, 768, 512, 558, 768, 640, 696, 960, 640, 697, 960, 768, 835, 1152,
      768, 836, 1152, 896, 975, 1344, 896, 976, 1344, 1024, 1114, 1536, 1024,
      1115, 1536, 1152, 1253, 1728, 1152, 1254, 1728, 1280, 1393, 1920, 1280,
      1394, 1920,
    ];

    const frameLength = frameSizeMap[frameSizeCode * 3 + samplingRateCode] * 2;
    if (offset + frameLength > data.length) {
      return false;
    }

    // get channel count
    const channelMode = data[offset + 6] >> 5;
    let skipCount = 0;
    if (channelMode === 2) {
      skipCount += 2;
    } else {
      if ((channelMode & 1) && channelMode !== 1) {
        skipCount += 2;
      }
      if (channelMode & 4) {
        skipCount += 2;
      }
    }

    const lowFrequencyEffectsChannelOn =
      (((data[offset + 6] << 8) | data[offset + 7]) >> (12 - skipCount)) & 1;

    const channelsMap = [2, 1, 2, 3, 3, 4, 4, 5];

    // Audio config for DAC3 box
    const bitStreamIdentification = data[offset + 5] >> 3;
    const bitStreamMode = data[offset + 5] & 7;

    const config = new Uint8Array([
      (samplingRateCode << 6) |
          (bitStreamIdentification << 1) |
          (bitStreamMode >> 2),
      ((bitStreamMode & 3) << 6) |
          (channelMode << 3) |
          (lowFrequencyEffectsChannelOn << 2) |
          (frameSizeCode >> 4),
      (frameSizeCode << 4) & 0xe0,
    ]);

    // All properties of the `frame` object need to be written
    // when true is returned, as the object is reused by the caller
    // for multiple calls to this method.
    frame.sampleRate = samplingRateMap[samplingRateCode];
    frame.channelCount =
      channelsMap[channelMode] + lowFrequencyEffectsChannelOn;
    frame.audioConfig = config;
    frame.frameLength = frameLength;

    return true;
  }

  /**
   * @param {!Uint8Array} data
   * @param {!number} offset
   * @return {boolean}
   */
  static probe(data, offset) {
    // look for the ac-3 sync bytes
    if (data[offset] === 0x0b &&
      data[offset + 1] === 0x77) {
      // check the bsid (bitStreamIdentification) to confirm ac-3
      let bsid = 0;
      let numBits = 5;
      offset += numBits;
      /** @type {?number} */
      let temp = null;
      /** @type {?number} */
      let mask = null;
      /** @type {?number} */
      let byte = null;
      while (numBits > 0) {
        byte = data[offset];
        // read remaining bits, upto 8 bits at a time
        const bits = Math.min(numBits, 8);
        const shift = 8 - bits;
        mask = (0xff000000 >>> (24 + shift)) << shift;
        temp = (byte & mask) >> shift;
        bsid = !bsid ? temp : (bsid << bits) | temp;
        offset += 1;
        numBits -= bits;
      }
      if (bsid < 16) {
        return true;
      }
    }
    return false;
  }
};

/**
 * @const {number}
 */
shaka.transmuxer.Ac3.AC3_SAMPLES_PER_FRAME = 1536;

/**
 * @typedef {{
 *   sampleRate: number,
 *   channelCount: number,
 *   audioConfig: !Uint8Array,
 *   frameLength: number,
 * }}
 *
 * @property {number} sampleRate
 * @property {number} channelCount
 * @property {!Uint8Array} audioConfig
 * @property {number} frameLength
 */
shaka.transmuxer.Ac3.Ac3Frame;
