'use strict';

const { Fmp4Parser } = require('../src/fmp4Parser');

// Baut eine ISO-BMFF-Box: [4B size][4B type][payload]. size deckt size+type ab.
function box(type, payload = Buffer.alloc(0)) {
  const head = Buffer.alloc(8);
  head.writeUInt32BE(8 + payload.length, 0);
  head.write(type, 4, 'latin1');
  return Buffer.concat([head, payload]);
}

// Typischer FFmpeg-fMP4-Stream: ftyp moov | (moof mdat)…
function ftyp() { return box('ftyp', Buffer.from('isom')); }
function moov() { return box('moov', Buffer.from([1, 2, 3, 4])); }
function frag(n) { return Buffer.concat([box('moof', Buffer.from([n])), box('mdat', Buffer.from([n, n, n]))]); }

describe('Fmp4Parser', () => {
  test('Init-Segment = ftyp + moov, einmalig', () => {
    const inits = []; const segs = [];
    const p = new Fmp4Parser({ onInit: (b) => inits.push(b), onSegment: (b) => segs.push(b) });
    p.push(Buffer.concat([ftyp(), moov()]));
    expect(inits).toHaveLength(1);
    expect(inits[0].equals(Buffer.concat([ftyp(), moov()]))).toBe(true);
    expect(segs).toHaveLength(0);
  });

  test('Fragmente = moof + mdat', () => {
    const inits = []; const segs = [];
    const p = new Fmp4Parser({ onInit: (b) => inits.push(b), onSegment: (b) => segs.push(b) });
    p.push(Buffer.concat([ftyp(), moov(), frag(1), frag(2)]));
    expect(inits).toHaveLength(1);
    expect(segs).toHaveLength(2);
    expect(segs[0].equals(frag(1))).toBe(true);
    expect(segs[1].equals(frag(2))).toBe(true);
  });

  test('über beliebige Chunk-Grenzen gesplittet', () => {
    const inits = []; const segs = [];
    const p = new Fmp4Parser({ onInit: (b) => inits.push(b), onSegment: (b) => segs.push(b) });
    const stream = Buffer.concat([ftyp(), moov(), frag(1), frag(2), frag(3)]);
    for (let i = 0; i < stream.length; i += 3) p.push(stream.subarray(i, i + 3)); // 3-Byte-Häppchen
    expect(inits).toHaveLength(1);
    expect(segs).toHaveLength(3);
    expect(segs[2].equals(frag(3))).toBe(true);
  });

  test('Byte-für-Byte (Extremfall)', () => {
    const inits = []; const segs = [];
    const p = new Fmp4Parser({ onInit: (b) => inits.push(b), onSegment: (b) => segs.push(b) });
    const stream = Buffer.concat([ftyp(), moov(), frag(7)]);
    for (const byte of stream) p.push(Buffer.from([byte]));
    expect(inits).toHaveLength(1);
    expect(segs).toHaveLength(1);
    expect(segs[0].equals(frag(7))).toBe(true);
  });

  test('64-Bit largesize (size==1) wird verstanden', () => {
    const inits = [];
    const p = new Fmp4Parser({ onInit: (b) => inits.push(b) });
    // ftyp normal, moov als 64-Bit-Box
    const payload = Buffer.from([9, 9]);
    const head = Buffer.alloc(16);
    head.writeUInt32BE(1, 0);                 // size == 1 → largesize folgt
    head.write('moov', 4, 'latin1');
    head.writeUInt32BE(0, 8);                 // largesize high
    head.writeUInt32BE(16 + payload.length, 12); // largesize low
    const moov64 = Buffer.concat([head, payload]);
    p.push(Buffer.concat([ftyp(), moov64]));
    expect(inits).toHaveLength(1);
    expect(inits[0].equals(Buffer.concat([ftyp(), moov64]))).toBe(true);
  });
});
