/* eslint-disable camelcase, max-statements */
import {
  copyPaddedStringToDataView,
  copyPaddedArrayBufferToDataView
} from '@loaders.gl/loader-utils';
// import type {GLB} from '../types/glb-types';

const MAGIC_glTF = 0x46546c67; // glTF in ASCII
const MAGIC_JSON = 0x4e4f534a; // JSON in ASCII
const MAGIC_BIN = 0x004e4942; // BIN\0 in ASCII

const LE = true; // Binary GLTF is little endian.

export type GLBEncodeOptions = {};

/**
 * Encode the full GLB buffer with header etc
 *
 * @param glb
 * @param dataView - if `null`, does not encode but just calculates length
 * @param byteOffset
 * @param options
 * @returns
 *
 * @see https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#glb-file-format-specification
 * @todo type GLB argument
 */
export function encodeGLBSync(
  glb,
  dataView: DataView | null,
  byteOffset = 0,
  options: GLBEncodeOptions = {}
) {
  const {magic = MAGIC_glTF, version = 2, json = {}, binary} = glb;

  const byteOffsetStart = byteOffset;

  // Write GLB Header
  if (dataView) {
    dataView.setUint32(byteOffset + 0, magic, LE); // Magic number (the ASCII string 'glTF').
    dataView.setUint32(byteOffset + 4, version, LE); // Version 2 of binary glTF container format uint32
    dataView.setUint32(byteOffset + 8, 0, LE); // Total byte length of generated file (uint32), will be set last
  }
  const byteOffsetFileLength = byteOffset + 8;
  byteOffset += 12; // GLB_FILE_HEADER_SIZE

  // Write the JSON chunk header
  const byteOffsetJsonHeader = byteOffset;
  if (dataView) {
    dataView.setUint32(byteOffset + 0, 0, LE); // Byte length of json chunk (will be written later)
    dataView.setUint32(byteOffset + 4, MAGIC_JSON, LE); // Chunk type
  }
  byteOffset += 8; // GLB_CHUNK_HEADER_SIZE

  // Write the JSON chunk
  const jsonString = JSON.stringify(json);
  byteOffset = copyPaddedStringToDataView(dataView, byteOffset, jsonString, 4);

  // Now we know the JSON chunk length so we can write it.
  if (dataView) {
    const jsonByteLength = byteOffset - byteOffsetJsonHeader - 8; // GLB_CHUNK_HEADER_SIZE
    dataView.setUint32(byteOffsetJsonHeader + 0, jsonByteLength, LE); // Byte length of json chunk (uint32)
  }

  // Write the BIN chunk if present. The BIN chunk is optional.
  if (binary) {
    const byteOffsetBinHeader = byteOffset;

    // Write the BIN chunk header
    if (dataView) {
      dataView.setUint32(byteOffset + 0, 0, LE); // Byte length BIN (uint32)
      dataView.setUint32(byteOffset + 4, MAGIC_BIN, LE); // Chunk type
    }
    byteOffset += 8; // GLB_CHUNK_HEADER_SIZE

    byteOffset = copyPaddedArrayBufferToDataView(dataView, byteOffset, binary, 4);

    // Now we know the BIN chunk length so we can write it.
    if (dataView) {
      const binByteLength = byteOffset - byteOffsetBinHeader - 8; // GLB_CHUNK_HEADER_SIZE
      dataView.setUint32(byteOffsetBinHeader + 0, binByteLength, LE); // Byte length BIN (uint32)
    }
  }

  // Now we know the glb file length so we can write it.
  if (dataView) {
    const fileByteLength = byteOffset - byteOffsetStart;
    dataView.setUint32(byteOffsetFileLength, fileByteLength, LE); // Total byte length of generated file (uint32)
  }

  return byteOffset;
}
