// Moderne MotorSound-Lib mit Web Audio API (AudioWorklet statt ScriptProcessor)
// Geschwindigkeitsabhängiges Motorgeräusch für Taxi-Spiel

class MotorSound {
  constructor(context, generator) {
    this.context = context;
    this.generator = generator;
    this.speed = 0.6; // incoming control value (0.3..1.5)
    this.isPlaying = false;
    this.currentFrame = 0;
    this.workletNode = null;
    this.gainNode = null;
    this.data = null;
    this.sampleRate = context.sampleRate || 44100;
    this.step = 1; // table index increment per sample, derived from target frequency
    this.regenerate();
  }

  async init() {
    // Verwende ScriptProcessor für bessere Kompatibilität
    console.log('Initialisiere MotorSound mit ScriptProcessor');
    // Kleinere Buffergröße für geringere Latenz
    this.workletNode = this.context.createScriptProcessor(256);
    this.workletNode.onaudioprocess = this.process.bind(this);

    // Filter-Kette für motorähnlicheren Klang
    this.lowShelf = this.context.createBiquadFilter();
    this.lowShelf.type = 'lowshelf';
    this.lowShelf.frequency.value = 150;
    this.lowShelf.gain.value = 6;

    this.lowPass = this.context.createBiquadFilter();
    this.lowPass.type = 'lowpass';
    this.lowPass.frequency.value = 1200;
    this.lowPass.Q.value = 0.5;

    this.gainNode = this.context.createGain();
    this.gainNode.gain.setValueAtTime(0.25, this.context.currentTime);

    // Verbindung: script -> lowshelf -> lowpass -> gain
    this.workletNode.connect(this.lowShelf);
    this.lowShelf.connect(this.lowPass);
    this.lowPass.connect(this.gainNode);
    
    console.log('MotorSound ScriptProcessor initialisiert');
  }

  start() {
    if (!this.gainNode) return;
    if (this.context.state === 'suspended') {
      this.context.resume().catch(() => {});
    }
    this.gainNode.connect(this.context.destination);
    this.isPlaying = true;
  }

  stop() {
    if (!this.gainNode || !this.gainNode.context) return;
    try {
      this.gainNode.disconnect(this.context.destination);
    } catch (_) {
      // bereits disconnected
    }
    this.isPlaying = false;
  }

  regenerate() {
    this.data = this.generator.generate();
    console.log('MotorSound Daten regeneriert, Länge:', this.data.length);
  }

  setVolume(volume) {
    if (this.gainNode) this.gainNode.gain.value = volume;
  }

  setGenerator(generator) {
    this.generator = generator;
    this.regenerate();
  }

  setSpeed(speed) {
    // speed erwartet ca. 0.3..1.5 (von Spiel geliefert). Normiere auf 0..1
    const normalized = Math.max(0, Math.min(1, (speed - 0.3) / 1.2));
    // Ziel-Frequenz für Motorgrundton (Hz)
    const freq = 40 + normalized * 160; // 40..200 Hz
    // Tabellen-Schritt für gewünschte Frequenz
    this.step = (freq * this.data.length) / this.sampleRate;
  }

  process(event) {
    if (!this.data || this.data.length === 0) return;
    
    const output = event.outputBuffer;
    const ch0 = output.getChannelData(0);
    const ch1 = output.numberOfChannels > 1 ? output.getChannelData(1) : ch0;
    
    for (let i = 0; i < ch0.length; ++i) {
      this.currentFrame += this.step;
      const index = Math.floor(this.currentFrame) % this.data.length;
      const sample = this.data[index] * 0.9; // etwas dämpfen
      ch0[i] = sample;
      ch1[i] = sample;
    }
    this.currentFrame %= this.data.length;
  }
}

class LinearGenerator {
  constructor() {
    this.dataLength = 1024;
  }

  pushLinear(data, toValue, toPosition) {
    const lastPosition = data.length - 1;
    const lastValue = data.pop();
    const positionDiff = toPosition - lastPosition;
    const step = (toValue - lastValue) / positionDiff;
    for (let i = 0; i < positionDiff; i++) {
      data.push(lastValue + step * i);
    }
    return data;
  }

  generate() {
    const data = [];
    let lastValue = 1;
    data.push(lastValue);

    for (let i = 0.05; i < 1; i += Math.random() / 8 + 0.01) {
      const nextPosition = Math.floor(i * this.dataLength);
      const nextValue = Math.random() * 2 - 1;
      this.pushLinear(data, nextValue, nextPosition);
    }

    this.pushLinear(data, 1, this.dataLength);
    return data;
  }
}

class NoiseGenerator {
  constructor() {
    this.dataLength = 4096;
    this.linearLength = 30;
    this.smoothness = 3;
  }

  generate() {
    const data = [];
    let lastValue = 0.5;
    data.push(lastValue);

    for (let i = 1; i <= this.dataLength - this.linearLength; i++) {
      lastValue += (Math.random() - 0.5) / this.smoothness;
      lastValue = Math.min(1, lastValue);
      lastValue = Math.max(-1, lastValue);
      data.push(lastValue);
    }

    const step = (0.5 - lastValue) / this.linearLength;
    for (let j = 0; j < this.linearLength; j++) {
      data.push(lastValue + step * j);
    }
    data.push(0.5);
    return data;
  }
}

class CanvasGenerator {
  constructor() {
    this.canvas = document.createElement('canvas');
    this.canvas.width = 1024;
    this.canvas.height = 1;
    this.ctx = this.canvas.getContext('2d');
  }

  getRandomGradient() {
    const gradient = this.ctx.createLinearGradient(0, 0, this.canvas.width, 0);
    gradient.addColorStop(0, "rgba(0, 0, 0, 255)");
    for (let i = 0.05; i < 1; i += Math.random() / 8 + 0.01) {
      gradient.addColorStop(i, "rgba(0, 0, 0," + Math.random() + ")");
    }
    gradient.addColorStop(1, "rgba(0, 0, 0, 255)");
    return gradient;
  }

  generate() {
    this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
    this.ctx.fillStyle = this.getRandomGradient();
    this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);

    const imageData = this.ctx.getImageData(0, 0, this.canvas.width, 1).data;
    const data = [];
    for (let i = 3, len = imageData.length; i < len; i += 4) {
      data.push(imageData[i] / 128 - 1);
    }
    return data;
  }
}

export { MotorSound, LinearGenerator, NoiseGenerator, CanvasGenerator };
