//
//  Created by Mingliang Chen on 17/12/21.
//  illuspas[a]gmail.com
//  Copyright (c) 2018 Nodemedia. All rights reserved.
//

const Bitop = require('./node_core_bitop');
const AAC_SAMPLE_RATE = [
  96000, 88200, 64000, 48000,
  44100, 32000, 24000, 22050,
  16000, 12000, 11025, 8000,
  7350, 0, 0, 0
];

const AAC_CHANNELS = [
  0, 1, 2, 3, 4, 5, 6, 8
];

const AUDIO_CODEC_NAME = [
  '',
  'ADPCM',
  'MP3',
  'LinearLE',
  'Nellymoser16',
  'Nellymoser8',
  'Nellymoser',
  'G711A',
  'G711U',
  '',
  'AAC',
  'Speex',
  '',
  '',
  'MP3-8K',
  'DeviceSpecific',
  'Uncompressed'
];

const AUDIO_SOUND_RATE = [
  5512, 11025, 22050, 44100
];

const VIDEO_CODEC_NAME = [
  '',
  'Jpeg',
  'Sorenson-H263',
  'ScreenVideo',
  'On2-VP6',
  'On2-VP6-Alpha',
  'ScreenVideo2',
  'H264',
  '',
  '',
  '',
  '',
  'H265'
];

function getObjectType(bitop) {
  let audioObjectType = bitop.read(5);
  if (audioObjectType === 31) {
    audioObjectType = bitop.read(6) + 32;
  }
  return audioObjectType;
}

function getSampleRate(bitop, info) {
  info.sampling_index = bitop.read(4);
  return info.sampling_index == 0x0f ? bitop.read(24) : AAC_SAMPLE_RATE[info.sampling_index];
}

function readAACSpecificConfig(aacSequenceHeader) {
  let info = {};
  let bitop = new Bitop(aacSequenceHeader);
  bitop.read(16);
  info.object_type = getObjectType(bitop);
  info.sample_rate = getSampleRate(bitop, info);
  info.chan_config = bitop.read(4);
  if (info.chan_config < AAC_CHANNELS.length) {
    info.channels = AAC_CHANNELS[info.chan_config];
  }
  info.sbr = -1;
  info.ps = -1;
  if (info.object_type == 5 || info.object_type == 29) {
    if (info.object_type == 29) {
      info.ps = 1;
    }
    info.ext_object_type = 5;
    info.sbr = 1;
    info.sample_rate = getSampleRate(bitop, info);
    info.object_type = getObjectType(bitop);
  }

  return info;
}

function getAACProfileName(info) {
  switch (info.object_type) {
    case 1:
      return 'Main';
    case 2:
      if (info.ps > 0) {
        return 'HEv2';
      }
      if (info.sbr > 0) {
        return 'HE';
      }
      return 'LC';
    case 3:
      return 'SSR';
    case 4:
      return 'LTP';
    case 5:
      return 'SBR';
    default:
      return '';
  }
}

function readH264SpecificConfig(avcSequenceHeader) {
  let info = {};
  let profile_idc, width, height, crop_left, crop_right,
    crop_top, crop_bottom, frame_mbs_only, n, cf_idc,
    num_ref_frames;
  let bitop = new Bitop(avcSequenceHeader);
  bitop.read(48);
  info.width = 0;
  info.height = 0;

  do {
    info.profile = bitop.read(8);
    info.compat = bitop.read(8);
    info.level = bitop.read(8);
    info.nalu = (bitop.read(8) & 0x03) + 1;
    info.nb_sps = bitop.read(8) & 0x1F;
    if (info.nb_sps == 0) {
      break;
    }
    /* nal size */
    bitop.read(16);

    /* nal type */
    if (bitop.read(8) != 0x67) {
      break;
    }
    /* SPS */
    profile_idc = bitop.read(8);

    /* flags */
    bitop.read(8);

    /* level idc */
    bitop.read(8);

    /* SPS id */
    bitop.read_golomb();

    if (profile_idc == 100 || profile_idc == 110 ||
      profile_idc == 122 || profile_idc == 244 || profile_idc == 44 ||
      profile_idc == 83 || profile_idc == 86 || profile_idc == 118) {
      /* chroma format idc */
      cf_idc = bitop.read_golomb();

      if (cf_idc == 3) {

        /* separate color plane */
        bitop.read(1);
      }

      /* bit depth luma - 8 */
      bitop.read_golomb();

      /* bit depth chroma - 8 */
      bitop.read_golomb();

      /* qpprime y zero transform bypass */
      bitop.read(1);

      /* seq scaling matrix present */
      if (bitop.read(1)) {

        for (n = 0; n < (cf_idc != 3 ? 8 : 12); n++) {

          /* seq scaling list present */
          if (bitop.read(1)) {

            /* TODO: scaling_list()
            if (n < 6) {
            } else {
            }
            */
          }
        }
      }
    }

    /* log2 max frame num */
    bitop.read_golomb();

    /* pic order cnt type */
    switch (bitop.read_golomb()) {
      case 0:

        /* max pic order cnt */
        bitop.read_golomb();
        break;

      case 1:

        /* delta pic order alwys zero */
        bitop.read(1);

        /* offset for non-ref pic */
        bitop.read_golomb();

        /* offset for top to bottom field */
        bitop.read_golomb();

        /* num ref frames in pic order */
        num_ref_frames = bitop.read_golomb();

        for (n = 0; n < num_ref_frames; n++) {

          /* offset for ref frame */
          bitop.read_golomb();
        }
    }

    /* num ref frames */
    info.avc_ref_frames = bitop.read_golomb();

    /* gaps in frame num allowed */
    bitop.read(1);

    /* pic width in mbs - 1 */
    width = bitop.read_golomb();

    /* pic height in map units - 1 */
    height = bitop.read_golomb();

    /* frame mbs only flag */
    frame_mbs_only = bitop.read(1);

    if (!frame_mbs_only) {

      /* mbs adaprive frame field */
      bitop.read(1);
    }

    /* direct 8x8 inference flag */
    bitop.read(1);

    /* frame cropping */
    if (bitop.read(1)) {

      crop_left = bitop.read_golomb();
      crop_right = bitop.read_golomb();
      crop_top = bitop.read_golomb();
      crop_bottom = bitop.read_golomb();

    } else {
      crop_left = 0;
      crop_right = 0;
      crop_top = 0;
      crop_bottom = 0;
    }
    info.level = info.level / 10.0;
    info.width = (width + 1) * 16 - (crop_left + crop_right) * 2;
    info.height = (2 - frame_mbs_only) * (height + 1) * 16 - (crop_top + crop_bottom) * 2;

  } while (0);

  return info;
}

function HEVCParsePtl(bitop, hevc, max_sub_layers_minus1) {
  let general_ptl = {};

  general_ptl.profile_space = bitop.read(2);
  general_ptl.tier_flag = bitop.read(1);
  general_ptl.profile_idc = bitop.read(5);
  general_ptl.profile_compatibility_flags = bitop.read(32);
  general_ptl.general_progressive_source_flag = bitop.read(1);
  general_ptl.general_interlaced_source_flag = bitop.read(1);
  general_ptl.general_non_packed_constraint_flag = bitop.read(1);
  general_ptl.general_frame_only_constraint_flag = bitop.read(1);
  bitop.read(32);
  bitop.read(12);
  general_ptl.level_idc = bitop.read(8);

  general_ptl.sub_layer_profile_present_flag = [];
  general_ptl.sub_layer_level_present_flag = [];

  for (let i = 0; i < max_sub_layers_minus1; i++) {
    general_ptl.sub_layer_profile_present_flag[i] = bitop.read(1);
    general_ptl.sub_layer_level_present_flag[i] = bitop.read(1);
  }

  if (max_sub_layers_minus1 > 0) {
    for (let i = max_sub_layers_minus1; i < 8; i++) {
      bitop.read(2)
    }
  }

  general_ptl.sub_layer_profile_space = [];
  general_ptl.sub_layer_tier_flag = [];
  general_ptl.sub_layer_profile_idc = [];
  general_ptl.sub_layer_profile_compatibility_flag = [];
  general_ptl.sub_layer_progressive_source_flag = [];
  general_ptl.sub_layer_interlaced_source_flag = [];
  general_ptl.sub_layer_non_packed_constraint_flag = [];
  general_ptl.sub_layer_frame_only_constraint_flag = [];
  general_ptl.sub_layer_level_idc = [];

  for (let i = 0; i < max_sub_layers_minus1; i++) {
    if (general_ptl.sub_layer_profile_present_flag[i]) {
      general_ptl.sub_layer_profile_space[i] = bitop.read(2);
      general_ptl.sub_layer_tier_flag[i] = bitop.read(1);
      general_ptl.sub_layer_profile_idc[i] = bitop.read(5);
      general_ptl.sub_layer_profile_compatibility_flag[i] = bitop.read(32);
      general_ptl.sub_layer_progressive_source_flag[i] = bitop.read(1);
      general_ptl.sub_layer_interlaced_source_flag[i] = bitop.read(1);
      general_ptl.sub_layer_non_packed_constraint_flag[i] = bitop.read(1);
      general_ptl.sub_layer_frame_only_constraint_flag[i] = bitop.read(1);
      bitop.read(32);
      bitop.read(12);
    }
    if (general_ptl.sub_layer_level_present_flag[i]) {
      general_ptl.sub_layer_level_idc[i] = bitop.read(8);
    }
    else {
      general_ptl.sub_layer_level_idc[i] = 1;
    }
  }
  return general_ptl;
}

function HEVCParseSPS(SPS, hevc) {
  let psps = {};
  let NumBytesInNALunit = SPS.length;
  let NumBytesInRBSP = 0;
  let rbsp_array = [];
  let bitop = new Bitop(SPS);

  bitop.read(1);//forbidden_zero_bit
  bitop.read(6);//nal_unit_type
  bitop.read(6);//nuh_reserved_zero_6bits
  bitop.read(3);//nuh_temporal_id_plus1

  for (let i = 2; i < NumBytesInNALunit; i++) {
    if (i + 2 < NumBytesInNALunit && bitop.look(24) == 0x000003) {
      rbsp_array.push(bitop.read(8));
      rbsp_array.push(bitop.read(8));
      i += 2;
      let emulation_prevention_three_byte = bitop.read(8); /* equal to 0x03 */
    } else {
      rbsp_array.push(bitop.read(8));
    }
  }
  let rbsp = Buffer.from(rbsp_array);
  let rbspBitop = new Bitop(rbsp);
  psps.sps_video_parameter_set_id = rbspBitop.read(4);
  psps.sps_max_sub_layers_minus1 = rbspBitop.read(3);
  psps.sps_temporal_id_nesting_flag = rbspBitop.read(1);
  psps.profile_tier_level = HEVCParsePtl(rbspBitop, hevc, psps.sps_max_sub_layers_minus1);
  psps.sps_seq_parameter_set_id = rbspBitop.read_golomb();
  psps.chroma_format_idc = rbspBitop.read_golomb();
  if (psps.chroma_format_idc == 3) {
    psps.separate_colour_plane_flag = rbspBitop.read(1);
  } else {
    psps.separate_colour_plane_flag = 0;
  }
  psps.pic_width_in_luma_samples = rbspBitop.read_golomb();
  psps.pic_height_in_luma_samples = rbspBitop.read_golomb();
  psps.conformance_window_flag = rbspBitop.read(1);
  if (psps.conformance_window_flag) {
    let vert_mult = 1 + (psps.chroma_format_idc < 2);
    let horiz_mult = 1 + (psps.chroma_format_idc < 3);
    psps.conf_win_left_offset = rbspBitop.read_golomb() * horiz_mult;
    psps.conf_win_right_offset = rbspBitop.read_golomb() * horiz_mult;
    psps.conf_win_top_offset = rbspBitop.read_golomb() * vert_mult;
    psps.conf_win_bottom_offset = rbspBitop.read_golomb() * vert_mult;
  }
  // Logger.debug(psps);
  return psps;
}

function readHEVCSpecificConfig(hevcSequenceHeader) {
  let info = {};
  info.width = 0;
  info.height = 0;
  info.profile = 0;
  info.level = 0;
  // let bitop = new Bitop(hevcSequenceHeader);
  // bitop.read(48);
  hevcSequenceHeader = hevcSequenceHeader.slice(5);

  do {
    let hevc = {};
    if (hevcSequenceHeader.length < 23) {
      break;
    }

    hevc.configurationVersion = hevcSequenceHeader[0];
    if (hevc.configurationVersion != 1) {
      break;
    }
    hevc.general_profile_space = (hevcSequenceHeader[1] >> 6) & 0x03;
    hevc.general_tier_flag = (hevcSequenceHeader[1] >> 5) & 0x01;
    hevc.general_profile_idc = hevcSequenceHeader[1] & 0x1F;
    hevc.general_profile_compatibility_flags = (hevcSequenceHeader[2] << 24) | (hevcSequenceHeader[3] << 16) | (hevcSequenceHeader[4] << 8) | hevcSequenceHeader[5];
    hevc.general_constraint_indicator_flags = ((hevcSequenceHeader[6] << 24) | (hevcSequenceHeader[7] << 16) | (hevcSequenceHeader[8] << 8) | hevcSequenceHeader[9]);
    hevc.general_constraint_indicator_flags = (hevc.general_constraint_indicator_flags << 16) | (hevcSequenceHeader[10] << 8) | hevcSequenceHeader[11];
    hevc.general_level_idc = hevcSequenceHeader[12];
    hevc.min_spatial_segmentation_idc = ((hevcSequenceHeader[13] & 0x0F) << 8) | hevcSequenceHeader[14];
    hevc.parallelismType = hevcSequenceHeader[15] & 0x03;
    hevc.chromaFormat = hevcSequenceHeader[16] & 0x03;
    hevc.bitDepthLumaMinus8 = hevcSequenceHeader[17] & 0x07;
    hevc.bitDepthChromaMinus8 = hevcSequenceHeader[18] & 0x07;
    hevc.avgFrameRate = (hevcSequenceHeader[19] << 8) | hevcSequenceHeader[20];
    hevc.constantFrameRate = (hevcSequenceHeader[21] >> 6) & 0x03;
    hevc.numTemporalLayers = (hevcSequenceHeader[21] >> 3) & 0x07;
    hevc.temporalIdNested = (hevcSequenceHeader[21] >> 2) & 0x01;
    hevc.lengthSizeMinusOne = hevcSequenceHeader[21] & 0x03;
    let numOfArrays = hevcSequenceHeader[22];
    let p = hevcSequenceHeader.slice(23);
    for (let i = 0; i < numOfArrays; i++) {
      if (p.length < 3) {
        brak;
      }
      let nalutype = p[0];
      let n = (p[1]) << 8 | p[2];
      // Logger.debug(nalutype, n);
      p = p.slice(3);
      for (let j = 0; j < n; j++) {
        if (p.length < 2) {
          break;
        }
        k = (p[0] << 8) | p[1];
        // Logger.debug('k', k);
        if (p.length < 2 + k) {
          break;
        }
        p = p.slice(2);
        if (nalutype == 33) {
          //SPS
          let sps = Buffer.alloc(k);
          p.copy(sps, 0, 0, k);
          // Logger.debug(sps, sps.length);
          hevc.psps = HEVCParseSPS(sps, hevc);
          info.profile = hevc.general_profile_idc;
          info.level = hevc.general_level_idc / 30.0;
          info.width = hevc.psps.pic_width_in_luma_samples - (hevc.psps.conf_win_left_offset + hevc.psps.conf_win_right_offset);
          info.height = hevc.psps.pic_height_in_luma_samples - (hevc.psps.conf_win_top_offset + hevc.psps.conf_win_bottom_offset);
        }
        p = p.slice(k);
      }
    }
  } while (0);

  return info;
}

function readAVCSpecificConfig(avcSequenceHeader) {
  let codec_id = avcSequenceHeader[0] & 0x0f;
  if (codec_id == 7) {
    return readH264SpecificConfig(avcSequenceHeader);
  } else if (codec_id == 12) {
    return readHEVCSpecificConfig(avcSequenceHeader);
  }
}


function getAVCProfileName(info) {
  switch (info.profile) {
    case 1:
      return 'Main';
    case 2:
      return 'Main 10';
    case 3:
      return 'Main Still Picture';
    case 66:
      return 'Baseline';
    case 77:
      return 'Main';
    case 100:
      return 'High';
    default:
      return '';
  }
}

module.exports = {
  AUDIO_SOUND_RATE,
  AUDIO_CODEC_NAME,
  VIDEO_CODEC_NAME,
  readAACSpecificConfig,
  getAACProfileName,
  readAVCSpecificConfig,
  getAVCProfileName,
};
