import errors from '../errors';
import urlTypeConverter from './urlType';
import { parseByDuration } from './durationTimeParser';
import window from 'global/window';

/**
 * Translates SegmentBase into a set of segments.
 * (DASH SPEC Section 5.3.9.3.2) contains a set of <SegmentURL> nodes.  Each
 * node should be translated into segment.
 *
 * @param {Object} attributes
 *   Object containing all inherited attributes from parent elements with attribute
 *   names as keys
 * @return {Object.<Array>} list of segments
 */
export const segmentsFromBase = (attributes) => {
  const {
    baseUrl,
    initialization = {},
    sourceDuration,
    indexRange = '',
    periodStart,
    presentationTime,
    number = 0,
    duration
  } = attributes;

  // base url is required for SegmentBase to work, per spec (Section 5.3.9.2.1)
  if (!baseUrl) {
    throw new Error(errors.NO_BASE_URL);
  }

  const initSegment = urlTypeConverter({
    baseUrl,
    source: initialization.sourceURL,
    range: initialization.range
  });

  const segment = urlTypeConverter({ baseUrl, source: baseUrl, indexRange });

  segment.map = initSegment;

  // If there is a duration, use it, otherwise use the given duration of the source
  // (since SegmentBase is only for one total segment)
  if (duration) {
    const segmentTimeInfo = parseByDuration(attributes);

    if (segmentTimeInfo.length) {
      segment.duration = segmentTimeInfo[0].duration;
      segment.timeline = segmentTimeInfo[0].timeline;
    }
  } else if (sourceDuration) {
    segment.duration = sourceDuration;
    segment.timeline = periodStart;
  }

  // If presentation time is provided, these segments are being generated by SIDX
  // references, and should use the time provided. For the general case of SegmentBase,
  // there should only be one segment in the period, so its presentation time is the same
  // as its period start.
  segment.presentationTime = presentationTime || periodStart;
  segment.number = number;

  return [segment];
};

/**
 * Given a playlist, a sidx box, and a baseUrl, update the segment list of the playlist
 * according to the sidx information given.
 *
 * playlist.sidx has metadadata about the sidx where-as the sidx param
 * is the parsed sidx box itself.
 *
 * @param {Object} playlist the playlist to update the sidx information for
 * @param {Object} sidx the parsed sidx box
 * @return {Object} the playlist object with the updated sidx information
 */
export const addSidxSegmentsToPlaylist = (playlist, sidx, baseUrl) => {
  // Retain init segment information
  const initSegment = playlist.sidx.map ? playlist.sidx.map : null;
  // Retain source duration from initial main manifest parsing
  const sourceDuration = playlist.sidx.duration;
  // Retain source timeline
  const timeline = playlist.timeline || 0;
  const sidxByteRange = playlist.sidx.byterange;
  const sidxEnd = sidxByteRange.offset + sidxByteRange.length;
  // Retain timescale of the parsed sidx
  const timescale = sidx.timescale;
  // referenceType 1 refers to other sidx boxes
  const mediaReferences = sidx.references.filter(r => r.referenceType !== 1);
  const segments = [];
  const type = playlist.endList ? 'static' : 'dynamic';
  const periodStart = playlist.sidx.timeline;
  let presentationTime = periodStart;
  let number = playlist.mediaSequence || 0;

  // firstOffset is the offset from the end of the sidx box
  let startIndex;

  // eslint-disable-next-line
  if (typeof sidx.firstOffset === 'bigint') {
    startIndex = window.BigInt(sidxEnd) + sidx.firstOffset;
  } else {
    startIndex = sidxEnd + sidx.firstOffset;
  }

  for (let i = 0; i < mediaReferences.length; i++) {
    const reference = sidx.references[i];
    // size of the referenced (sub)segment
    const size = reference.referencedSize;
    // duration of the referenced (sub)segment, in  the  timescale
    // this will be converted to seconds when generating segments
    const duration = reference.subsegmentDuration;
    // should be an inclusive range
    let endIndex;

    // eslint-disable-next-line
    if (typeof startIndex === 'bigint') {
      endIndex = startIndex + window.BigInt(size) - window.BigInt(1);
    } else {
      endIndex = startIndex + size - 1;
    }
    const indexRange = `${startIndex}-${endIndex}`;

    const attributes = {
      baseUrl,
      timescale,
      timeline,
      periodStart,
      presentationTime,
      number,
      duration,
      sourceDuration,
      indexRange,
      type
    };

    const segment = segmentsFromBase(attributes)[0];

    if (initSegment) {
      segment.map = initSegment;
    }

    segments.push(segment);
    if (typeof startIndex === 'bigint') {
      startIndex += window.BigInt(size);
    } else {
      startIndex += size;
    }
    presentationTime += duration / timescale;
    number++;
  }

  playlist.segments = segments;

  return playlist;
};
