/**
 * Copyright 2015 CANAL+ Group
 *
 * 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 is feature-switchable.
 * It always should be imported through the `features` object.
 */

import { le2toi, le4toi } from "../../utils/byte_parsing";
import { utf8ToStr } from "../../utils/string_parsing";

export interface IBifThumbnail {
  index: number;
  duration: number;
  ts: number;
  data: Uint8Array<ArrayBuffer>;
}

export interface IBifObject {
  fileFormat: string;
  version: string;
  imageCount: number;
  timescale: number;
  format: string;
  width: number;
  height: number;
  aspectRatio: string;
  isVod: boolean;
  thumbs: IBifThumbnail[];
}

/**
 * @param {UInt8Array} buf
 * @returns {Object}
 */
function parseBif(buf: Uint8Array): IBifObject {
  let pos = 0;

  const length = buf.length;

  const fileFormat = utf8ToStr(buf.subarray(pos + 1, pos + 8));
  pos += 8;
  if (buf[0] !== 0x89 || fileFormat !== "BIF\r\n\u001a\n") {
    throw new Error("Invalid BIF file");
  }

  const minorVersion = buf[pos];
  pos += 1;
  const majorVersion = buf[pos];
  pos += 1;
  const patchVersion = buf[pos];
  pos += 1;
  const increVersion = buf[pos];
  pos += 1;

  const version = [minorVersion, majorVersion, patchVersion, increVersion].join(".");

  if (majorVersion > 0) {
    throw new Error(`Unhandled version: ${majorVersion}`);
  }

  const imageCount = le4toi(buf, pos);
  pos += 4;
  const framewiseSeparation = le4toi(buf, pos);
  pos += 4;

  const format = utf8ToStr(buf.subarray(pos, pos + 4));
  pos += 4;

  const width = le2toi(buf, pos);
  pos += 2;
  const height = le2toi(buf, pos);
  pos += 2;

  const aspectRatio = [buf[pos], buf[pos + 1]].join(":");
  pos += 2;

  const isVod = buf[pos] === 1;

  // bytes 0x1F to 0x40 is unused data for now
  pos = 0x40;

  const thumbs: IBifThumbnail[] = [];

  if (imageCount === 0) {
    throw new Error("bif: no images to parse");
  }

  let index = 0;
  let previousImageInfo = null;
  while (pos < length) {
    const currentImageTimestamp = le4toi(buf, pos);
    pos += 4;
    const currentImageOffset = le4toi(buf, pos);
    pos += 4;

    if (previousImageInfo !== null) {
      // calculate for index-1
      const ts = previousImageInfo.timestamp * framewiseSeparation;
      const duration = framewiseSeparation;
      const data = buf.slice(previousImageInfo.offset, currentImageOffset);

      thumbs.push({ index, duration, ts, data });

      index++;
    }

    if (currentImageTimestamp === 0xffffffff) {
      break;
    }

    previousImageInfo = {
      timestamp: currentImageTimestamp,
      offset: currentImageOffset,
    };
  }

  return {
    fileFormat: "BIF",
    version,
    imageCount,
    timescale: 1000,
    format,
    width,
    height,
    aspectRatio,
    isVod,
    thumbs,
  };
}

export default parseBif;
