'use strict';

const { createInflateRaw, Z_DEFAULT_WINDOWBITS } = require('node:zlib');
const { isValidClientWindowBits } = require('./util');

const tail = Buffer.from([0x00, 0x00, 0xff, 0xff]);
const kBuffer = Symbol('kBuffer');
const kLength = Symbol('kLength');

class PerMessageDeflate {
  /** @type {import('node:zlib').InflateRaw} */
  #inflate;

  #options = {};

  constructor(extensions) {
    this.#options.serverNoContextTakeover = extensions.has(
      'server_no_context_takeover'
    );
    this.#options.serverMaxWindowBits = extensions.get(
      'server_max_window_bits'
    );
  }

  decompress(chunk, fin, callback) {
    // An endpoint uses the following algorithm to decompress a message.
    // 1.  Append 4 octets of 0x00 0x00 0xff 0xff to the tail end of the
    //     payload of the message.
    // 2.  Decompress the resulting data using DEFLATE.

    if (!this.#inflate) {
      let windowBits = Z_DEFAULT_WINDOWBITS;

      if (this.#options.serverMaxWindowBits) {
        // empty values default to Z_DEFAULT_WINDOWBITS
        if (!isValidClientWindowBits(this.#options.serverMaxWindowBits)) {
          callback(new Error('Invalid server_max_window_bits'));
          return;
        }

        windowBits = Number.parseInt(this.#options.serverMaxWindowBits);
      }

      this.#inflate = createInflateRaw({ windowBits });
      this.#inflate[kBuffer] = [];
      this.#inflate[kLength] = 0;

      this.#inflate.on('data', (data) => {
        this.#inflate[kBuffer].push(data);
        this.#inflate[kLength] += data.length;
      });

      this.#inflate.on('error', (err) => {
        this.#inflate = null;
        callback(err);
      });
    }

    this.#inflate.write(chunk);
    if (fin) {
      this.#inflate.write(tail);
    }

    this.#inflate.flush(() => {
      const full = Buffer.concat(
        this.#inflate[kBuffer],
        this.#inflate[kLength]
      );

      this.#inflate[kBuffer].length = 0;
      this.#inflate[kLength] = 0;

      callback(null, full);
    });
  }
}

module.exports = { PerMessageDeflate };