// Apache-2.0 https://github.com/1wheel/npyjs

const dtypes = {
  '<u1': {
    name: 'uint8',
    size: 8,
    arrayConstructor: Uint8Array,
  },
  '|u1': {
    name: 'uint8',
    size: 8,
    arrayConstructor: Uint8Array,
  },
  '<u2': {
    name: 'uint16',
    size: 16,
    arrayConstructor: Uint16Array,
  },
  '|i1': {
    name: 'int8',
    size: 8,
    arrayConstructor: Int8Array,
  },
  '<i2': {
    name: 'int16',
    size: 16,
    arrayConstructor: Int16Array,
  },
  '<u4': {
    name: 'uint32',
    size: 32,
    arrayConstructor: Int32Array,
  },
  '<i4': {
    name: 'int32',
    size: 32,
    arrayConstructor: Int32Array,
  },
  '<u8': {
    name: 'uint64',
    size: 64,
    arrayConstructor: BigUint64Array,
  },
  '<i8': {
    name: 'int64',
    size: 64,
    arrayConstructor: BigInt64Array,
  },
  '<f4': {
    name: 'float32',
    size: 32,
    arrayConstructor: Float32Array
  },
  '<f8': {
    name: 'float64',
    size: 64,
    arrayConstructor: Float64Array
  },
};

function parse(buffer){
  const buf = new Uint8Array(buffer);
  if (buf[6] != 1) throw 'Only npy version 1 is supported';

  const headerLength = buf[8] + buf[9]*256;
  const offsetBytes = 10 + headerLength;

  const header = JSON.parse(
    new TextDecoder('utf-8')
      .decode(buf.slice(10, 10 + headerLength))
      .replace(/'/g, '"')
      .replace('False', 'false')
      .replace('(', '[')
      .replace(/,*\),*/g, ']')
  );

  if (header.fortan_order) throw 'Fortran-contiguous array data are not supported';
  const dtype = dtypes[header.descr];

  return {
    data: new dtype['arrayConstructor'](buf.slice(offsetBytes).buffer),
    shape: header.shape,
    dtype: dtype.name,
  };
}

function format(typedArray, shape){
  let dtype = null;
  for (let d in dtypes){
    if (dtypes[d].arrayConstructor == typedArray.constructor) dtype = d;
  }
  if (dtype === null) throw 'Invalid typedArray';

  const header = `{'descr': '${dtype}', 'fortran_order': False, 'shape': (${shape.join(',')},), }\n`;
  const spacepad = Array.from({length: 64 - (8 + header.length) % 64}, d => '\x20').join('');

  const hl = (header + spacepad).length;

  return Buffer.concat([
    Buffer.from('\x93NUMPY\x01\x00', 'latin1'),
    // convert to little-endian
    Buffer.from(new Uint8Array([hl % 256, hl/256 | 0])),
    Buffer.from(header + spacepad, 'latin1'),
    Buffer.from(typedArray.buffer)
  ]);
}

export default {parse, format};