import { PassThrough, Transform, pipeline } from 'node:stream'
import fs from 'node:fs'

import config from '../config.js'
import { debugLog } from './utils.js'
import soundcloud from './sources/soundcloud.js'
import voiceUtils from './voice/utils.js'

import prism from 'prism-media'

const Coefficients = [{
  beta: 0.99847546664,
  alpha: 76226668143e-14,
  gamma: 1.9984647656
}, {
  beta: 0.99756184654,
  alpha: 0.0012190767289,
  gamma: 1.9975344645
}, {
  beta: 0.99616261379,
  alpha: 0.0019186931041,
  gamma: 1.9960947369
}, {
  beta: 0.99391578543,
  alpha: 0.0030421072865,
  gamma: 1.9937449618
}, {
  beta: 0.99028307215,
  alpha: 0.0048584639242,
  gamma: 1.9898465702
}, {
  beta: 0.98485897264,
  alpha: 0.0075705136795,
  gamma: 1.9837962543
}, {
  beta: 0.97588512657,
  alpha: 0.012057436715,
  gamma: 1.9731772447
}, {
  beta: 0.96228521814,
  alpha: 0.018857390928,
  gamma: 1.9556164694
}, {
  beta: 0.94080933132,
  alpha: 0.029595334338,
  gamma: 1.9242054384
}, {
  beta: 0.90702059196,
  alpha: 0.046489704022,
  gamma: 1.8653476166
}, {
  beta: 0.85868004289,
  alpha: 0.070659978553,
  gamma: 1.7600401337
}, {
  beta: 0.78409610788,
  alpha: 0.10795194606,
  gamma: 1.5450725522
}, {
  beta: 0.68332861002,
  alpha: 0.15833569499,
  gamma: 1.1426447155
}, {
  beta: 0.55267518228,
  alpha: 0.22366240886,
  gamma: 0.40186190803
}, {
  beta: 0.41811888447,
  alpha: 0.29094055777,
  gamma: -0.70905944223
}]

class ChannelProcessor {
  constructor(bandMultipliers) {
    this.history = new Array(15 * 6).fill(0)
    this.bandMultipliers = bandMultipliers
    this.current = 0
    this.m1 = 2
    this.m2 = 1
  }
  processInt(int) {
    let result = int * 0.25
    for (let bandIndex = 0; bandIndex < 15; bandIndex++) {
      const x = bandIndex * 6
      const y = x + 3
      const coefficients = Coefficients[bandIndex]
      const bandResult = coefficients.alpha * (int - this.history[x + this.m2]) + coefficients.gamma * this.history[y + this.m1] - coefficients.beta * this.history[y + this.m2]
      this.history[x + this.current] = int
      this.history[y + this.current] = bandResult
      result += bandResult * this.bandMultipliers[bandIndex]
    }
    const val = result * 4
    return val
  }
  process(samples, extremum, bytes) {
    const endIndex = Math.floor(samples.length / 2) * 2
    for (let sampleIndex = 0; sampleIndex < endIndex; sampleIndex += bytes) {
      const sample = samples.readInt16LE(sampleIndex)
      const result = this.processInt(sample)
      const val = Math.min(extremum - 1, Math.max(-extremum, result))
      samples.writeInt16LE(val, sampleIndex)
      this.step()
    }
    return samples
  }
  step() {
    if (++this.current === 3) {
      this.current = 0
    }
    if (++this.m1 === 3) {
      this.m1 = 0
    }
    if (++this.m2 === 3) {
      this.m2 = 0
    }
  }
}

class Equalizer extends Transform {
  constructor(channelCount, bandMultipliers) {
    super()

    bandMultipliers = bandMultipliers.map((mul) => mul.gain)

    this.channels = []
    this.bits = 16
    this.bytes = this.bits / 8
    this.extremum = Math.pow(2, this.bits - 1)
    this.channels = []

    for (let i = 0; i < channelCount; i++) {
      this.channels.push(new ChannelProcessor(bandMultipliers))
    }
  }
  process(input) {
    return this.channels.map((c, i) => {
      const { data, extremum, bytes } = input[i]
      return c.process(data, extremum, bytes)
    })
  }
  _transform(data, _encoding, callback) {
    this.process([{
      data,
      extremum: Math.pow(2, 16 - 1),
      bytes: 2
    }])

    this.push(data)

    return callback()
  }
}

class VibratoChannelProcessor {
  constructor(frequency, depth, sampleRate) {
    this.frequency = frequency;
    this.depth = depth;
    this.sampleRate = sampleRate;
    this.phase = 0;
  }

  processInt(sample) {
    // Your vibrato processing logic
    const vibratoValue = Math.sin(this.phase) * this.depth;
    this.phase += (this.frequency * 2 * Math.PI) / this.sampleRate;
    this.phase %= 2 * Math.PI;
  
    let processedSample = sample + vibratoValue;

    // Clamp the processed sample to the valid range
    processedSample = this.clamp16Bit(processedSample);

    return processedSample;
  }

  clamp16Bit(sample) {
    return Math.max(-32768, Math.min(sample, 32767));
  }

  process(samples) {
    const processedBuffer = Buffer.alloc(samples.length);

    // Process in pairs of bytes (16-bit samples)
    for (let i = 0; i < samples.length; i += 2) {
      const sample = samples.readInt16LE(i);
      const processedSample = this.processInt(sample);

      // Write processed sample back, clamping to ensure it’s in the 16-bit range
      processedBuffer.writeInt16LE(processedSample, i);
      console.log(processedBuffer)
    }

    // console.log(processedBuffer)
    return processedBuffer;
  }
}

class VibratoFilter extends Transform {
  constructor(channelCount, { frequency, depth }) {
    super();

    this.frequency = frequency;
    this.depth = depth;
    this.channels = [];

    for (let i = 0; i < channelCount; i++) {
      this.channels.push(new VibratoChannelProcessor(frequency, depth));
    }
  }
  _transform(data, _encoding, callback) {
    const extremum = Math.pow(2, 16 - 1);
    let processedData;

    try {
      // Process with Vibrato for each channel
      processedData = this.channels.map((channel) =>
        channel.process(data, extremum)
      ).reduce((acc, val) => {
        // Mix processed channel data
        for (let i = 0; i < val.length; i += 2) {
          const sampleAcc = acc.readInt16LE(i);
          const sampleVal = val.readInt16LE(i);
          acc.writeInt16LE(sampleAcc + sampleVal, i); // Mix channels
        }
        return acc;
      });
    } catch (error) {
      return callback(error);
    }

    // console.log(processedData) //zero

    if (processedData && processedData.length > 0) {
      this.push(processedData); // Push the processed data to the next stage of the pipeline
    }
    callback(); // Signal that processing is complete for this chunk
  }
}

class Filters {
  constructor() {
    this.command = []
    this.equalizer = Array(15).fill(0).map((_, i) => ({ band: i, gain: 0 }))
    this.result = {}
  }

  configure(filters) {
    const result = {}

    if (filters.volume && config.filters.list.volume) {
      result.volume = filters.volume

      this.command.push(`volume=${filters.volume}`)
    }

		if (filters.equalizer && Array.isArray(filters.equalizer) && filters.equalizer.length && config.filters.list.equalizer) {
      result.equalizer = filters.equalizer

      for (const equalizedBand of filters.equalizer) {
        const band = this.equalizer.find(i => i.band == equalizedBand.band)
        if (band) band.gain = Math.min(Math.max(equalizedBand.gain, -0.25), 1.0)
      }
		}

    if (filters.karaoke && filters.karaoke.level && filters.karaoke.monoLevel && filters.karaoke.filterBand && filters.karaoke.filterWidth && config.filters.list.karaoke) {
      result.karaoke = { level: filters.karaoke.level, monoLevel: filters.karaoke.monoLevel, filterBand: filters.karaoke.filterBand, filterWidth: filters.karaoke.filterWidth }

      this.command.push(`stereotools=mlev=${filters.karaoke.monoLevel}:mwid=${filters.karaoke.filterWidth}:k=${filters.karaoke.level}:kc=${filters.karaoke.filterBand}`)
    }
    if (filters.timescale && filters.timescale.speed && filters.timescale.pitch && filters.timescale.rate && config.filters.list.timescale) {
      result.timescale = { speed: filters.timescale.speed, pitch: filters.timescale.pitch, rate: filters.timescale.rate }

      const finalspeed = filters.timescale.speed + (1.0 - filters.timescale.pitch)
      const ratedif = 1.0 - filters.timescale.rate

      this.command.push(`asetrate=48000*${filters.timescale.pitch + ratedif},atempo=${finalspeed},aresample=48000`)
		}

    if (filters.tremolo && filters.tremolo.frequency && filters.tremolo.depth && config.filters.list.tremolo) {
      result.tremolo = { frequency: filters.tremolo.frequency, depth: filters.tremolo.depth }

      this.command.push(`tremolo=f=${filters.tremolo.frequency}:d=${filters.tremolo.depth}`)
    }

    if (filters.vibrato && filters.vibrato.frequency && filters.vibrato.depth && config.filters.list.vibrato) {
      result.vibrato = { frequency: filters.vibrato.frequency, depth: filters.vibrato.depth }
    }

    if (filters.rotation && filters.rotation.rotationHz && config.filters.list.rotation) {
      result.rotation = { rotationHz: filters.rotation.rotationHz }

      this.command.push(`apulsator=hz=${filters.rotation.rotationHz}`)
    }

    if (filters.distortion && filters.distortion.sinOffset && filters.distortion.sinScale && filters.distortion.cosOffset && filters.distortion.cosScale && filters.distortion.tanOffset && filters.distortion.tanScale && filters.distortion.offset && filters.distortion.scale && config.filters.list.distortion) {
      result.distortion = { sinOffset: filters.distortion.sinOffset, sinScale: filters.distortion.sinScale, cosOffset: filters.distortion.cosOffset, cosScale: filters.distortion.cosScale, tanOffset: filters.distortion.tanOffset, tanScale: filters.distortion.tanScale, offset: filters.distortion.offset, scale: filters.distortion.scale }

      this.command.push(`afftfilt=real='hypot(re,im)*sin(0.1*${filters.distortion.sinOffset}*PI*t)*${filters.distortion.sinScale}+hypot(re,im)*cos(0.1*${filters.distortion.cosOffset}*PI*t)*${filters.distortion.cosScale}+hypot(re,im)*tan(0.1*${filters.distortion.tanOffset}*PI*t)*${filters.distortion.tanScale}+${filters.distortion.offset}':imag='hypot(re,im)*sin(0.1*${filters.distortion.sinOffset}*PI*t)*${filters.distortion.sinScale}+hypot(re,im)*cos(0.1*${filters.distortion.cosOffset}*PI*t)*${filters.distortion.cosScale}+hypot(re,im)*tan(0.1*${filters.distortion.tanOffset}*PI*t)*${filters.distortion.tanScale}+${filters.distortion.offset}':win_size=512:overlap=0.75:scale=${filters.distortion.scale}`)
    }

    if (filters.channelMix && filters.channelMix.leftToLeft && filters.channelMix.leftToRight && filters.channelMix.rightToLeft && filters.channelMix.rightToRight && config.filters.list.channelMix) {
      result.channelMix = { leftToLeft: filters.channelMix.leftToLeft, leftToRight: filters.channelMix.leftToRight, rightToLeft: filters.channelMix.rightToLeft, rightToRight: filters.channelMix.rightToRight }

      this.command.push(`pan=stereo|c0<c0*${filters.channelMix.leftToLeft}+c1*${filters.channelMix.rightToLeft}|c1<c0*${filters.channelMix.leftToRight}+c1*${filters.channelMix.rightToRight}`)
    }

    if (filters.lowPass && filters.lowPass.smoothing && config.filters.list.lowPass) {
      result.lowPass = { smoothing: filters.lowPass.smoothing }

      this.command.push(`lowpass=f=${filters.lowPass.smoothing / 500}`)
    }

    this.result = result

    return result
  }

  getResource(guildId, decodedTrack, protocol, url, startTime, endTime, oldFFmpeg, additionalData) {
    return new Promise(async (resolve) => {
      // TODO: Deezer is not properly working, refactor this file's function.

      // const trackData = await sources.getTrackStream(decodedTrack, url, protocol, additionalData)

      // if (trackData.exception) {
      //   debugLog('retrieveStream', 4, { type: 2, sourceName: decodedTrack.sourceName, query: decodedTrack.title, message: trackData.exception.message })

      //   resolve({ status: 1, exception: { message: trackData.exception.message, severity: 'fault', cause: 'Unknown' } })
      // }

      if (decodedTrack.sourceName == 'soundcloud')
        url = await soundcloud.loadFilters(url, protocol)

      const ffmpeg = new prism.FFmpeg({
        args: [
          '-loglevel', '0',
          '-analyzeduration', '0',
          '-hwaccel', 'auto',
          '-threads', config.filters.threads,
          '-filter_threads', config.filters.threads,
          '-filter_complex_threads', config.filters.threads,
          ...(startTime ? ['-ss', `${startTime}ms`] : []),
          '-i', encodeURI(url),
          ...(this.command.length != 0 ? [ '-af', this.command.join(',') ] : [] ),
          ...(endTime ? ['-t', `${endTime}ms`] : []),
          '-f', 's16le',
          '-ar', '48000',
          '-ac', '2',
          '-crf', '0'
        ]
      })

      const stream = PassThrough()

      ffmpeg.process.stdout.on('data', (data) => stream.write(data))
      ffmpeg.process.stdout.on('end', () => stream.end())
      ffmpeg.on('error', (err) => {
        debugLog('retrieveStream', 4, { type: 2, sourceName: decodedTrack.sourceName, query: decodedTrack.title, message: err.message })

        resolve({ status: 1, exception: { message: err.message, severity: 'fault', cause: 'Unknown' } })
      })

      const file = fs.createWriteStream(`./${guildId}.pcm`)

      ffmpeg.process.stdout.once('readable', () => {
        pipeline([ stream, new prism.VolumeTransformer({ type: 's16le' }), new VibratoFilter(1, this.result.vibrato), file ], (err) => {
          console.log('I\'m fucked')
        })
        resolve({
          stream: new voiceUtils.NodeLinkStream(stream, [
            new prism.VolumeTransformer({ type: 's16le' }),
            // new Equalizer(1, this.equalizer),
            new VibratoFilter(1, this.result.vibrato),
            new prism.opus.Encoder({ rate: 48000, channels: 2, frameSize: 960 })
          ])
        })
      })
    })
  }
}

export default Filters

I have this code, I'm working on the native vibrato filter (ffmpeg worked), but the process of channel processor class is returning a zero-filled buffer, could you fix that?