/**
 * Neural Network Background Animation
 * Modern, darker, mystical theme using WebGL
 * Subtle flowing network aesthetic
 */

class NeuralNetworkBackground {
  constructor(options = {}) {
    this.canvas = document.createElement('canvas');
    this.canvas.style.position = 'fixed';
    this.canvas.style.top = '0';
    this.canvas.style.left = '0';
    this.canvas.style.width = '100%';
    this.canvas.style.height = '100%';
    this.canvas.style.zIndex = '-1';
    this.canvas.style.pointerEvents = 'none';
    this.canvas.style.opacity = '0.6'; // Noch weiter reduzierte Opazität für subtileren Hintergrund
    
    // Standardkonfiguration mit subtileren Werten
    this.config = {
      nodeCount: 30,            // Weniger Knoten
      nodeSize: 1.2,            // Kleinere Knoten
      connectionDistance: 150,  // Reduzierte Verbindungsdistanz
      connectionOpacity: 0.3,   // Sanftere Verbindungslinien
      clusterCount: 6,          // Weniger Cluster
      clusterRadius: 380,       // Größerer Cluster-Radius für mehr Verteilung
      animationSpeed: 0.25,     // Langsamere Animation
      flowDensity: 0.05,        // Deutlich weniger Flussanimationen
      flowSpeed: 0.04,          // Langsamerer Fluss
      flowLength: 0.04,         // Kürzere Flussstreifen
      pulseSpeed: 0.0004,       // Sehr sanftes Pulsieren
      activationFrequency: 0.4, // Seltenere Aktivierungen
      minConnectionsPerNode: 1,
      maxConnectionsPerNode: 3, // Weniger maximale Verbindungen
      lineUnderFlow: true,      // Strichlinie unter Fluss anzeigen
      lineUnderFlowOpacity: 0.2, // Opazität der Strichlinie
      lineUnderFlowWidth: 1.0,  // Breite der Strichlinie
      sequentialFlows: true,    // Flüsse nacheinander anzeigen
      maxSimultaneousFlows: 3   // Maximale Anzahl gleichzeitiger Flüsse
    };
    
    // Benutzerkonfiguration übernehmen (falls vorhanden)
    this.config = { ...this.config, ...options.config };
    
    // Dunkler Modus mit subtileren Farben
    this.darkModeColors = {
      background: '#050a12',     // Dunklerer Hintergrund
      nodeColor: '#5a79b7',      // Sanfteres Blau
      connectionColor: '#2a3c68', // Gedämpfteres Blau
      flowColor: '#4d6ea8',      // Sanfteres Flussblau
      lineUnderFlowColor: '#223a5c', // Dunkleres Blau für Strichlinie
      nodePulse: { r: 120, g: 150, b: 200 } // Sanftere Pulse-Farbe
    };
    
    // Heller Modus mit subtileren Farben
    this.lightModeColors = {
      background: '#f9fbff',     // Hellerer Hintergrund
      nodeColor: '#b9d0f8',      // Helleres, sanfteres Blau
      connectionColor: '#e0f0ff', // Sehr helles Blau für subtilere Verbindungen
      flowColor: '#92b2e6',      // Sanftes mittleres Blau
      lineUnderFlowColor: '#c7d8f2', // Helles Blau für Strichlinie
      nodePulse: { r: 110, g: 140, b: 200 } // Sanftere Pulse-Farbe
    };
    
    // Erweiterte Konfiguration
    this.nodes = [];
    this.connections = [];
    this.flows = [];
    this.isDarkMode = options.isDarkMode !== undefined ? options.isDarkMode : true;
    this.useWebGL = options.useWebGL !== undefined ? options.useWebGL : true;
    this.frameRate = options.frameRate || 30; // Reduzierte Framerate für bessere Performance
    this.lastFrameTime = 0;
    this.frameInterval = 1000 / this.frameRate;
    this.lastFlowTime = 0;       // Zeit des letzten Fluss-Starts
    this.flowInterval = 2000;    // Mindestzeit zwischen neuen Flüssen (ms)
    
    // Ereignisbehandlung für Fenstergröße und Theme-Wechsel
    this.resizeTimeout = null;
    window.addEventListener('resize', this.onResize.bind(this));
    document.addEventListener('theme-changed', (e) => {
      this.setDarkMode(e.detail.isDarkMode);
    });
    
    // Initialisierung
    this.webglSetupComplete = false;
    if (this.useWebGL && this.setupWebGL()) {
      this.webglSetupComplete = true;
    } else {
      this.ctx = this.canvas.getContext('2d');
    }
    
    // If canvas already exists, remove it first
    const existingCanvas = document.getElementById('neural-network-background');
    if (existingCanvas) {
      existingCanvas.remove();
    }
    
    // Append to body as first child to ensure it's behind everything
    if (document.body.firstChild) {
      document.body.insertBefore(this.canvas, document.body.firstChild);
    } else {
      document.body.appendChild(this.canvas);
    }
    
    // Animation properties
    this.animationFrameId = null;
    
    // Initialize
    this.init();
    
    // Event listeners
    document.addEventListener('darkModeToggled', (event) => {
      this.isDarkMode = event.detail.isDark;
    });
    
    // Log that the background is initialized
    console.log('Neural Network Background initialized');
  }
  
  init() {
    this.resizeCanvas();
    
    if (this.useWebGL) {
      this.initWebGL();
    }
    
    this.createNodes();
    this.createConnections();
    this.startAnimation();
  }
  
  resizeCanvas() {
    const pixelRatio = window.devicePixelRatio || 1;
    const width = window.innerWidth;
    const height = window.innerHeight;
    
    this.canvas.style.width = width + 'px';
    this.canvas.style.height = height + 'px';
    this.canvas.width = width * pixelRatio;
    this.canvas.height = height * pixelRatio;
    
    if (this.useWebGL) {
      this.gl.viewport(0, 0, this.canvas.width, this.canvas.height);
    } else if (this.ctx) {
      this.ctx.scale(pixelRatio, pixelRatio);
    }
    
    // Recalculate node positions after resize
    if (this.nodes.length) {
      this.createNodes();
      this.createConnections();
    }
  }
  
  initWebGL() {
    // Vertex shader
    const vsSource = `
      attribute vec2 aVertexPosition;
      attribute float aPointSize;
      uniform vec2 uResolution;
      
      void main() {
        // Convert from pixel to clip space
        vec2 position = (aVertexPosition / uResolution) * 2.0 - 1.0;
        // Flip Y coordinate
        position.y = -position.y;
        
        gl_Position = vec4(position, 0, 1);
        gl_PointSize = aPointSize;
      }
    `;
    
    // Fragment shader - Softer glow effect
    const fsSource = `
      precision mediump float;
      uniform vec4 uColor;
      
      void main() {
        float distance = length(gl_PointCoord - vec2(0.5, 0.5));
        
        // Softer glow with smoother falloff
        float alpha = 1.0 - smoothstep(0.1, 0.5, distance);
        alpha = pow(alpha, 1.5); // Make the glow even softer
        
        gl_FragColor = vec4(uColor.rgb, uColor.a * alpha);
      }
    `;
    
    // Initialize shaders
    const vertexShader = this.loadShader(this.gl.VERTEX_SHADER, vsSource);
    const fragmentShader = this.loadShader(this.gl.FRAGMENT_SHADER, fsSource);
    
    // Create shader program
    this.shaderProgram = this.gl.createProgram();
    this.gl.attachShader(this.shaderProgram, vertexShader);
    this.gl.attachShader(this.shaderProgram, fragmentShader);
    this.gl.linkProgram(this.shaderProgram);
    
    if (!this.gl.getProgramParameter(this.shaderProgram, this.gl.LINK_STATUS)) {
      console.error('Unable to initialize the shader program: ' + 
                   this.gl.getProgramInfoLog(this.shaderProgram));
      return;
    }
    
    // Get attribute and uniform locations
    this.programInfo = {
      program: this.shaderProgram,
      attribLocations: {
        vertexPosition: this.gl.getAttribLocation(this.shaderProgram, 'aVertexPosition'),
        pointSize: this.gl.getAttribLocation(this.shaderProgram, 'aPointSize')
      },
      uniformLocations: {
        resolution: this.gl.getUniformLocation(this.shaderProgram, 'uResolution'),
        color: this.gl.getUniformLocation(this.shaderProgram, 'uColor')
      }
    };
    
    // Create buffers
    this.positionBuffer = this.gl.createBuffer();
    this.sizeBuffer = this.gl.createBuffer();
    
    // Set clear color for WebGL context
    const bgColor = this.hexToRgb(this.darkModeColors.background);
    
    this.gl.clearColor(bgColor.r/255, bgColor.g/255, bgColor.b/255, 1.0);
  }
  
  loadShader(type, source) {
    const shader = this.gl.createShader(type);
    this.gl.shaderSource(shader, source);
    this.gl.compileShader(shader);
    
    if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) {
      console.error('An error occurred compiling the shaders: ' + 
                  this.gl.getShaderInfoLog(shader));
      this.gl.deleteShader(shader);
      return null;
    }
    
    return shader;
  }
  
  createNodes() {
    this.nodes = [];
    const width = this.canvas.width / (window.devicePixelRatio || 1);
    const height = this.canvas.height / (window.devicePixelRatio || 1);
    
    // Erstelle Cluster-Zentren für neuronale Netzwerkmuster
    const clusterCount = Math.floor(5 + Math.random() * 4); // 5-8 Cluster
    const clusters = [];
    
    for (let i = 0; i < clusterCount; i++) {
      clusters.push({
        x: Math.random() * width,
        y: Math.random() * height,
        radius: 100 + Math.random() * 150
      });
    }
    
    // Create nodes with random positions and properties
    for (let i = 0; i < this.config.nodeCount; i++) {
      // Entscheide, ob dieser Knoten zu einem Cluster gehört oder nicht
      const useCluster = Math.random() < this.config.clusteringFactor;
      let x, y;
      
      if (useCluster && clusters.length > 0) {
        // Wähle ein zufälliges Cluster
        const cluster = clusters[Math.floor(Math.random() * clusters.length)];
        const angle = Math.random() * Math.PI * 2;
        const distance = Math.random() * cluster.radius;
        
        // Platziere in der Nähe des Clusters mit einiger Streuung
        x = cluster.x + Math.cos(angle) * distance;
        y = cluster.y + Math.sin(angle) * distance;
        
        // Stelle sicher, dass es innerhalb des Bildschirms bleibt
        x = Math.max(0, Math.min(width, x));
        y = Math.max(0, Math.min(height, y));
      } else {
        // Zufällige Position außerhalb von Clustern
        x = Math.random() * width;
        y = Math.random() * height;
      }
      
      // Bestimme die Knotengröße - wichtigere Knoten (in Clustern) sind größer
      const nodeImportance = useCluster ? 1.2 : 0.8;
      const size = this.config.nodeSize * nodeImportance + Math.random() * this.config.nodeVariation;
      
      const node = {
        x: x,
        y: y,
        size: size,
        speed: {
          x: (Math.random() - 0.5) * this.config.animationSpeed,
          y: (Math.random() - 0.5) * this.config.animationSpeed
        },
        pulsePhase: Math.random() * Math.PI * 2, // Random starting phase
        connections: [],
        isActive: Math.random() < 0.3, // Some nodes start active for neural firing effect
        lastFired: 0,                  // For neural firing animation
        firingRate: 1000 + Math.random() * 4000 // Random firing rate in ms
      };
      
      this.nodes.push(node);
    }
  }
  
  createConnections() {
    this.connections = [];
    this.flows = []; // Reset flows
    
    // Create connections between nearby nodes
    for (let i = 0; i < this.nodes.length; i++) {
      const nodeA = this.nodes[i];
      nodeA.connections = [];
      
      // Sortiere andere Knoten nach Entfernung für bevorzugte nahe Verbindungen
      const potentialConnections = [];
      
      for (let j = 0; j < this.nodes.length; j++) {
        if (i === j) continue;
        
        const nodeB = this.nodes[j];
        const dx = nodeB.x - nodeA.x;
        const dy = nodeB.y - nodeA.y;
        const distance = Math.sqrt(dx * dx + dy * dy);
        
        if (distance < this.config.connectionDistance) {
          potentialConnections.push({
            index: j,
            distance: distance
          });
        }
      }
      
      // Sortiere nach Entfernung
      potentialConnections.sort((a, b) => a.distance - b.distance);
      
      // Wähle die nächsten N Verbindungen, maximal maxConnections
      const maxConn = Math.min(
        this.config.maxConnections, 
        potentialConnections.length, 
        1 + Math.floor(Math.random() * this.config.maxConnections)
      );
      
      for (let c = 0; c < maxConn; c++) {
        const connection = potentialConnections[c];
        const j = connection.index;
        const nodeB = this.nodes[j];
        const distance = connection.distance;
        
        // Create weighted connection (closer = stronger)
        const connectionStrength = Math.max(0, 1 - distance / this.config.connectionDistance);
        const connOpacity = connectionStrength * this.config.connectionOpacity;
        
        // Check if connection already exists
        if (!this.connections.some(conn => 
          (conn.from === i && conn.to === j) || (conn.from === j && conn.to === i)
        )) {
          // Neue Verbindung startet mit progress=0 für animierten Aufbau
          this.connections.push({
            from: i,
            to: j,
            distance: distance,
            opacity: connOpacity,
            strength: connectionStrength,
            hasFlow: false,
            lastActivated: 0,
            progress: 0 // Animationsfortschritt für Verbindungsaufbau
          });
          nodeA.connections.push(j);
          nodeB.connections.push(i);
        }
      }
    }
  }
  
  startAnimation() {
    this.animationFrameId = requestAnimationFrame(this.animate.bind(this));
  }
  
  animate() {
    // Update nodes
    const width = this.canvas.width / (window.devicePixelRatio || 1);
    const height = this.canvas.height / (window.devicePixelRatio || 1);
    const now = Date.now();
    
    // Simulate neural firing
    for (let i = 0; i < this.nodes.length; i++) {
      const node = this.nodes[i];
      
      // Check if node should fire based on its firing rate
      if (now - node.lastFired > node.firingRate) {
        node.isActive = true;
        node.lastFired = now;
        
        // Activate connected nodes with probability based on connection strength
        for (const connIndex of node.connections) {
          // Find the connection
          const conn = this.connections.find(c => 
            (c.from === i && c.to === connIndex) || (c.from === connIndex && c.to === i)
          );
          
          if (conn) {
            // Mark connection as recently activated
            conn.lastActivated = now;
            
            // Create a flow along this connection
            if (Math.random() < conn.strength * 0.8) {
              this.flows.push({
                connection: conn,
                progress: 0,
                direction: conn.from === i, // Flow from activated node
                length: this.config.flowLength + Math.random() * 0.1,
                intensity: 0.7 + Math.random() * 0.3 // Random intensity for variation
              });
            }
            
            // Probability for connected node to activate
            if (Math.random() < conn.strength * 0.5) {
              this.nodes[connIndex].isActive = true;
              this.nodes[connIndex].lastFired = now - Math.random() * 500; // Slight variation
            }
          }
        }
      } else if (now - node.lastFired > 300) { // Deactivate after short period
        node.isActive = false;
      }
    }
    
    // Animierter Verbindungsaufbau: progress inkrementieren
    for (const connection of this.connections) {
      if (connection.progress < 1) {
        // Langsamer Aufbau: Geschwindigkeit kann angepasst werden
        connection.progress += 0.012; // Sehr langsam, für subtilen Effekt
        if (connection.progress > 1) connection.progress = 1;
      }
    }
    
    // Update flows
    this.updateFlows();
    
    // Occasionally create new flows along connections
    if (Math.random() < this.config.flowDensity) {
      this.createNewFlow();
    }
    
    // Recalculate connections occasionally for a living network
    if (Math.random() < 0.01) { // Only recalculate 1% of the time for performance
      this.createConnections();
    }
    
    // Render
    if (this.useWebGL) {
      this.renderWebGL();
    } else {
      this.renderCanvas();
    }
    
    // Continue animation
    this.animationFrameId = requestAnimationFrame(this.animate.bind(this));
  }
  
  // New method to update flow animations
  updateFlows() {
    // Update existing flows
    const now = Date.now();
    
    for (let i = this.flows.length - 1; i >= 0; i--) {
      const flow = this.flows[i];
      
      // Update flow progress
      flow.progress += this.config.flowSpeed / flow.connection.distance;
      
      // Update line progress (langsamer als der Blitz)
      flow.lineProgress = Math.min(flow.progress * 1.5, 1.0);
      
      // Remove completed flows
      if (flow.progress > 1.0) {
        this.flows.splice(i, 1);
      }
    }
    
    // Generate new flows selten und kontrolliert
    if (Math.random() < this.config.flowDensity && 
        (!this.config.sequentialFlows || 
         now - this.lastFlowTime >= this.flowInterval)) {
      this.createNewFlow();
    }
  }
  
  // New method to create flow animations
  createNewFlow() {
    if (this.connections.length === 0) return;
    
    // Überprüfen, ob maximale Anzahl gleichzeitiger Flüsse erreicht ist
    if (this.config.sequentialFlows && 
        this.flows.length >= this.config.maxSimultaneousFlows) {
      return;
    }
    
    // Überprüfen, ob genügend Zeit seit dem letzten Fluss vergangen ist
    const now = Date.now();
    if (this.config.sequentialFlows && 
        now - this.lastFlowTime < this.flowInterval) {
      return;
    }
    
    // Zeit des letzten Flusses aktualisieren
    this.lastFlowTime = now;
    
    // Select a random connection with preference for more connected nodes
    let connectionIdx = Math.floor(Math.random() * this.connections.length);
    let attempts = 0;
    
    // Try to find a connection with more connected nodes
    while (attempts < 5) {
      const testIdx = Math.floor(Math.random() * this.connections.length);
      const testConn = this.connections[testIdx];
      const fromNode = this.nodes[testConn.from];
      
      if (fromNode.connections.length > 2) {
        connectionIdx = testIdx;
        break;
      }
      attempts++;
    }
    
    const connection = this.connections[connectionIdx];
    
    // Create a new flow along this connection
    this.flows.push({
      connection: connection,
      progress: 0,
      direction: Math.random() > 0.5, // Randomly decide direction
      length: this.config.flowLength + Math.random() * 0.1, // Slightly vary lengths
      lineProgress: 0 // Fortschritt der unterliegenden Strichlinie (startet bei 0)
    });
  }
  
  renderWebGL() {
    this.gl.clear(this.gl.COLOR_BUFFER_BIT);
    
    const width = this.canvas.width / (window.devicePixelRatio || 1);
    const height = this.canvas.height / (window.devicePixelRatio || 1);
    
    // Select shader program
    this.gl.useProgram(this.programInfo.program);
    
    // Set resolution uniform
    this.gl.uniform2f(this.programInfo.uniformLocations.resolution, width, height);
    
    // Draw connections first (behind nodes)
    this.renderConnectionsWebGL();
    
    // Draw flows on top of connections
    this.renderFlowsWebGL();
    
    // Draw nodes
    this.renderNodesWebGL();
  }
  
  renderNodesWebGL() {
    // Prepare node positions for WebGL
    const positions = new Float32Array(this.nodes.length * 2);
    const sizes = new Float32Array(this.nodes.length);
    
    const now = Date.now();
    
    for (let i = 0; i < this.nodes.length; i++) {
      const node = this.nodes[i];
      positions[i * 2] = node.x;
      positions[i * 2 + 1] = node.y;
      
      // Maximales Pulsieren und sehr große Knoten
      let pulse = Math.sin(node.pulsePhase) * 0.38 + 1.35;
      const connectivityFactor = 1 + (node.connections.length / this.config.maxConnections) * 1.1;
      sizes[i] = node.size * pulse * connectivityFactor;
    }
    
    // Bind position buffer
    this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.positionBuffer);
    this.gl.bufferData(this.gl.ARRAY_BUFFER, positions, this.gl.STATIC_DRAW);
    this.gl.vertexAttribPointer(
      this.programInfo.attribLocations.vertexPosition,
      2,              // components per vertex
      this.gl.FLOAT,  // data type
      false,          // normalize
      0,              // stride
      0               // offset
    );
    this.gl.enableVertexAttribArray(this.programInfo.attribLocations.vertexPosition);
    
    // Bind size buffer
    this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.sizeBuffer);
    this.gl.bufferData(this.gl.ARRAY_BUFFER, sizes, this.gl.STATIC_DRAW);
    this.gl.vertexAttribPointer(
      this.programInfo.attribLocations.pointSize,
      1,              // components per vertex
      this.gl.FLOAT,  // data type
      false,          // normalize
      0,              // stride
      0               // offset
    );
    this.gl.enableVertexAttribArray(this.programInfo.attribLocations.pointSize);
    
    // Enable blending for all nodes
    this.gl.enable(this.gl.BLEND);
    this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE);  // Additive blending for glow
    
    // Draw each node individually with its own color
    for (let i = 0; i < this.nodes.length; i++) {
      const node = this.nodes[i];
      
      // Set node color - more visible with active highlighting
      const colorObj = this.isDarkMode ? this.darkModeColors : this.lightModeColors;
      const nodeColor = this.hexToRgb(colorObj.nodeColor);
      const nodePulseColor = this.hexToRgb(colorObj.nodePulse);
      
      // Use pulse color for active nodes
      let r = nodeColor.r / 255;
      let g = nodeColor.g / 255;
      let b = nodeColor.b / 255;
      
      // Active nodes get brighter color
      if (node.isActive) {
        r = (r + nodePulseColor.r / 255) / 2;
        g = (g + nodePulseColor.g / 255) / 2;
        b = (b + nodePulseColor.b / 255) / 2;
      }
      
      // Sehr sichtbare, maximal leuchtende Knoten
      this.gl.uniform4f(
        this.programInfo.uniformLocations.color,
        r, g, b,
        node.isActive ? 1.0 : 0.92 // Maximal sichtbar
      );
      
      // Draw each node individually for better control
      this.gl.drawArrays(this.gl.POINTS, i, 1);
    }
  }
  
  renderConnectionsWebGL() {
    // Permanente Verbindungsstriche werden nicht mehr gezeichnet
    // Diese Methode bleibt leer, damit keine Linien mehr erscheinen
  }
  
  // New method to render the flowing animations
  renderFlowsWebGL() {
    // Für jeden Flow zuerst die unterliegende Strichlinie zeichnen, dann den Blitz
    for (const flow of this.flows) {
      const connection = flow.connection;
      const fromNode = this.nodes[connection.from];
      const toNode = this.nodes[connection.to];
      const direction = flow.direction ? 1 : -1;
      
      // 1. Die unterliegende Strichlinie zeichnen
      if (this.config.lineUnderFlow) {
        const lineStart = direction > 0 ? fromNode : toNode;
        const lineEnd = direction > 0 ? toNode : fromNode;
        
        // Strichlinie als gerade Linie mit voller Länge, aber mit Fortschritt
        const lineEndPoint = {
          x: lineStart.x + (lineEnd.x - lineStart.x) * flow.lineProgress,
          y: lineStart.y + (lineEnd.y - lineStart.y) * flow.lineProgress
        };
        
        const positions = new Float32Array([
          lineStart.x, lineStart.y,
          lineEndPoint.x, lineEndPoint.y
        ]);
        
        this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.positionBuffer);
        this.gl.bufferData(this.gl.ARRAY_BUFFER, positions, this.gl.STATIC_DRAW);
        this.gl.vertexAttribPointer(
          this.programInfo.attribLocations.vertexPosition,
          2,
          this.gl.FLOAT,
          false,
          0,
          0
        );
        this.gl.enableVertexAttribArray(this.programInfo.attribLocations.vertexPosition);
        this.gl.disableVertexAttribArray(this.programInfo.attribLocations.pointSize);
        
        // Sanftere, transparentere Strichlinie
        const colorObj = this.isDarkMode ? this.darkModeColors : this.lightModeColors;
        const lineColor = this.hexToRgb(colorObj.lineUnderFlowColor || colorObj.connectionColor);
        
        this.gl.uniform4f(
          this.programInfo.uniformLocations.color,
          lineColor.r / 255,
          lineColor.g / 255,
          lineColor.b / 255,
          this.config.lineUnderFlowOpacity // Sehr subtile Linie
        );
        
        this.gl.enable(this.gl.BLEND);
        this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA); // Normales Alpha-Blending
        this.gl.lineWidth(this.config.lineUnderFlowWidth); // Dünne Linie
        this.gl.drawArrays(this.gl.LINES, 0, 2);
      }
      
      // 2. Den Blitz selbst zeichnen
      const startProgress = flow.progress;
      const endProgress = Math.min(1, startProgress + flow.length);
      if (startProgress >= 1 || endProgress <= 0) continue;
      
      let p1, p2;
      if (direction > 0) {
        p1 = {
          x: fromNode.x + (toNode.x - fromNode.x) * startProgress,
          y: fromNode.y + (toNode.y - fromNode.y) * startProgress
        };
        p2 = {
          x: fromNode.x + (toNode.x - fromNode.x) * endProgress,
          y: fromNode.y + (toNode.y - fromNode.y) * endProgress
        };
      } else {
        p1 = {
          x: toNode.x + (fromNode.x - toNode.x) * startProgress,
          y: toNode.y + (fromNode.y - toNode.y) * startProgress
        };
        p2 = {
          x: toNode.x + (fromNode.x - toNode.x) * endProgress,
          y: toNode.y + (fromNode.y - toNode.y) * endProgress
        };
      }
      
      // Zickzack-Blitz generieren (weniger ausgeprägt)
      const zigzag = this.generateZigZagPoints(p1, p2, 3, 5); // Weniger Segmente, kleinere Amplitude
      for (let i = 0; i < zigzag.length - 1; i++) {
        const positions = new Float32Array([
          zigzag[i].x, zigzag[i].y,
          zigzag[i + 1].x, zigzag[i + 1].y
        ]);
        this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.positionBuffer);
        this.gl.bufferData(this.gl.ARRAY_BUFFER, positions, this.gl.STATIC_DRAW);
        this.gl.vertexAttribPointer(
          this.programInfo.attribLocations.vertexPosition,
          2,
          this.gl.FLOAT,
          false,
          0,
          0
        );
        this.gl.enableVertexAttribArray(this.programInfo.attribLocations.vertexPosition);
        this.gl.disableVertexAttribArray(this.programInfo.attribLocations.pointSize);
        
        // Dezenter, subtilerer Blitz
        const colorObj = this.isDarkMode ? this.darkModeColors : this.lightModeColors;
        const flowColor = this.hexToRgb(colorObj.flowColor);
        this.gl.uniform4f(
          this.programInfo.uniformLocations.color,
          flowColor.r / 255,
          flowColor.g / 255,
          flowColor.b / 255,
          0.45 // Geringere Sichtbarkeit
        );
        this.gl.enable(this.gl.BLEND);
        this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE);
        this.gl.lineWidth(1.0); // Dünnerer Blitz für subtileren Effekt
        this.gl.drawArrays(this.gl.LINES, 0, 2);
      }
      
      // Funken erzeugen - weniger und subtilere elektrische Funken
      const sparks = this.generateSparkPoints(zigzag, 2 + Math.floor(Math.random() * 2)); // Weniger Funken
      for (const spark of sparks) {
        // Sanfteres Weiß-Blau für subtilere elektrische Funken
        this.gl.uniform4f(
          this.programInfo.uniformLocations.color,
          0.85, 0.9, 0.95, 0.4 * spark.intensity // Geringere Intensität
        );
        
        // Position für den Funken setzen
        const sparkPos = new Float32Array([spark.x, spark.y]);
        this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.positionBuffer);
        this.gl.bufferData(this.gl.ARRAY_BUFFER, sparkPos, this.gl.STATIC_DRAW);
        
        // Punktgröße setzen
        const sizes = new Float32Array([spark.size * 2.0]); // Kleinere Funken
        this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.sizeBuffer);
        this.gl.bufferData(this.gl.ARRAY_BUFFER, sizes, this.gl.STATIC_DRAW);
        
        this.gl.vertexAttribPointer(
          this.programInfo.attribLocations.vertexPosition,
          2,
          this.gl.FLOAT,
          false,
          0,
          0
        );
        this.gl.enableVertexAttribArray(this.programInfo.attribLocations.vertexPosition);
        
        this.gl.vertexAttribPointer(
          this.programInfo.attribLocations.pointSize,
          1,
          this.gl.FLOAT,
          false,
          0,
          0
        );
        this.gl.enableVertexAttribArray(this.programInfo.attribLocations.pointSize);
        
        this.gl.drawArrays(this.gl.POINTS, 0, 1);
      }
    }
  }
  
  renderCanvas() {
    if (!this.ctx) return;

    const { background, nodeColor, connectionColor } = this.isDarkMode ? this.darkModeColors : this.lightModeColors;
    
    // Hintergrund sanft löschen mit leichter Transparenz für Nachleuchten
    this.ctx.fillStyle = background;
    this.ctx.globalAlpha = 0.22; // Sehr subtile Überblendung für sanfte Bewegung
    this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
    this.ctx.globalAlpha = 1.0;
    
    // Verbindungen zeichnen mit distanzabhängiger Transparenz
    this.connections.forEach(connection => {
      const { source, target, strength } = connection;
      const dx = target.x - source.x;
      const dy = target.y - source.y;
      const distance = Math.sqrt(dx * dx + dy * dy);
      
      // Berechne Opazität basierend auf Distanz - je weiter entfernt, desto transparenter
      const maxOpacity = 0.02; // Sehr subtile maximale Opazität
      const opacityFactor = 1 - (distance / this.config.connectionDistance);
      let opacity = maxOpacity * opacityFactor * strength * this.config.connectionOpacity;
      
      // Subtilere Linien
      this.ctx.beginPath();
      this.ctx.moveTo(source.x, source.y);
      this.ctx.lineTo(target.x, target.y);
      this.ctx.lineWidth = 0.4; // Sehr dünne Linie
      this.ctx.strokeStyle = connectionColor;
      this.ctx.globalAlpha = opacity;
      this.ctx.stroke();
      this.ctx.globalAlpha = 1.0;
    });
    
    // Knoten zeichnen mit subtilerem Glühen und Pulsieren
    this.nodes.forEach(node => {
      const nodeBaseSize = node.size * this.config.nodeSize;
      const pulse = Math.sin(this.time * this.config.pulseSpeed * node.pulseRate) * 0.1 + 1; // Reduzierte Pulsamplitude
      const glowSize = nodeBaseSize * 1.5; // Kleinerer Glowradius
      
      // Aktive Knoten speziell hervorheben, aber subtiler
      if (node.active) {
        // Subtiles Glühen für aktive Knoten
        const gradient = this.ctx.createRadialGradient(
          node.x, node.y, 0,
          node.x, node.y, glowSize * 2
        );
        this.ctx.globalAlpha = 0.09; // Sehr niedrige Opazität für subtiles Glühen
        gradient.addColorStop(0, 'rgba(255, 255, 255, 0.15)');
        gradient.addColorStop(1, 'rgba(255, 255, 255, 0)');
        
        this.ctx.fillStyle = gradient;
        this.ctx.beginPath();
        this.ctx.arc(node.x, node.y, glowSize * 2, 0, Math.PI * 2);
        this.ctx.fill();
        
        // Zeichne aktiven Knoten
        this.ctx.globalAlpha = 0.9;
        this.ctx.fillStyle = nodeColor;
        this.ctx.beginPath();
        this.ctx.arc(node.x, node.y, nodeBaseSize * pulse, 0, Math.PI * 2);
        this.ctx.fill();
      } else {
        // Inaktive Knoten sanfter darstellen
        this.ctx.globalAlpha = 0.8;
        this.ctx.fillStyle = nodeColor;
        this.ctx.beginPath();
        this.ctx.arc(node.x, node.y, nodeBaseSize * pulse * 0.8, 0, Math.PI * 2);
        this.ctx.fill();
      }
      this.ctx.globalAlpha = 1.0;
    });
  }
  
  renderFlowsCanvas() {
    if (!this.ctx) return;
    
    const { flowColor } = this.isDarkMode ? this.darkModeColors : this.lightModeColors;
    
    this.flows.forEach(flow => {
      if (flow.progress > 1) return;
      
      const { source, target, progress } = flow;
      
      // Berechne aktuelle Position
      const dx = target.x - source.x;
      const dy = target.y - source.y;
      const currentX = source.x + dx * progress;
      const currentY = source.y + dy * progress;
      
      // Zick-Zack-Effekt reduzieren für sanftere Bewegung
      const segmentCount = 4; // Weniger Segmente
      const amplitude = 2; // Geringere Amplitude
      const waveLength = 0.2;
      
      let prevX = source.x;
      let prevY = source.y;
      
      this.ctx.beginPath();
      this.ctx.moveTo(prevX, prevY);
      
      // Sanftere "Elektrizitäts"-Linie
      for (let i = 0; i <= segmentCount; i++) {
        const segmentProgress = i / segmentCount;
        if (segmentProgress > progress) break;
        
        const segX = source.x + dx * segmentProgress;
        const segY = source.y + dy * segmentProgress;
        
        // Sanftere Wellen
        const perpX = -dy * Math.sin(segmentProgress * Math.PI * 10) * amplitude;
        const perpY = dx * Math.sin(segmentProgress * Math.PI * 10) * amplitude;
        
        const pointX = segX + perpX * Math.sin(this.time * 0.01);
        const pointY = segY + perpY * Math.sin(this.time * 0.01);
        
        this.ctx.lineTo(pointX, pointY);
        prevX = pointX;
        prevY = pointY;
      }
      
      // Linienausrichtung und -stil
      this.ctx.lineCap = 'round';
      this.ctx.lineWidth = 1.2; // Dünnere Linie
      
      // Subtiler Gloweffekt 
      this.ctx.shadowColor = flowColor;
      this.ctx.shadowBlur = 6; // Reduzierter Unschärferadius
      
      this.ctx.strokeStyle = flowColor;
      this.ctx.globalAlpha = 0.7 - 0.5 * Math.abs(progress - 0.5); // Sanfteres Ein- und Ausblenden
      this.ctx.stroke();
      this.ctx.globalAlpha = 1;
      this.ctx.shadowBlur = 0;
      
      // Funkeneffekte am Ende der Linie, aber weniger und kleiner
      if (progress > 0.9 && Math.random() < 0.3) { // Weniger Funken generieren
        const sparkCount = Math.floor(Math.random() * 2) + 1; // Maximal 3 Funken
        
        for (let i = 0; i < sparkCount; i++) {
          const sparkSize = Math.random() * 0.8 + 0.4; // Kleinere Funken
          const sparkAngle = Math.random() * Math.PI * 2;
          const sparkDistance = Math.random() * 5 + 2;
          
          const sparkX = currentX + Math.cos(sparkAngle) * sparkDistance;
          const sparkY = currentY + Math.sin(sparkAngle) * sparkDistance;
          
          // Weniger intensive Funken
          this.ctx.beginPath();
          this.ctx.arc(sparkX, sparkY, sparkSize, 0, Math.PI * 2);
          this.ctx.fillStyle = flowColor;
          this.ctx.globalAlpha = 0.4; // Reduzierte Opazität
          this.ctx.fill();
          this.ctx.globalAlpha = 1;
        }
      }
    });
  }
  
  // Helper method to convert hex to RGB
  hexToRgb(hex) {
    // Remove # if present
    hex = hex.replace(/^#/, '');
    
    // Handle rgba hex format
    let alpha = 1;
    if (hex.length === 8) {
      alpha = parseInt(hex.slice(6, 8), 16) / 255;
      hex = hex.slice(0, 6);
    }
    
    // Parse hex values
    const bigint = parseInt(hex, 16);
    const r = (bigint >> 16) & 255;
    const g = (bigint >> 8) & 255;
    const b = bigint & 255;
    
    return { r, g, b, a: alpha };
  }
  
  // Cleanup method
  destroy() {
    if (this.animationFrameId) {
      cancelAnimationFrame(this.animationFrameId);
    }
    
    window.removeEventListener('resize', this.resizeCanvas.bind(this));
    
    if (this.canvas && this.canvas.parentNode) {
      this.canvas.parentNode.removeChild(this.canvas);
    }
    
    if (this.gl) {
      // Clean up WebGL resources
      this.gl.deleteBuffer(this.positionBuffer);
      this.gl.deleteBuffer(this.sizeBuffer);
      this.gl.deleteProgram(this.shaderProgram);
    }
  }
  
  // Hilfsfunktion: Erzeuge Zickzack-Punkte für einen Blitz
  generateZigZagPoints(start, end, segments = 5, amplitude = 8) {
    const points = [start];
    for (let i = 1; i < segments; i++) {
      const t = i / segments;
      const x = start.x + (end.x - start.x) * t;
      const y = start.y + (end.y - start.y) * t;
      // Versatz für Zickzack
      const angle = Math.atan2(end.y - start.y, end.x - start.x) + Math.PI / 2;
      const offset = (Math.random() - 0.5) * amplitude * (i % 2 === 0 ? 1 : -1);
      points.push({
        x: x + Math.cos(angle) * offset,
        y: y + Math.sin(angle) * offset
      });
    }
    points.push(end);
    return points;
  }
  
  // Hilfsfunktion: Erzeuge Funkenpunkte entlang eines Zickzack-Blitzes
  generateSparkPoints(zigzag, sparkCount = 4) {
    const sparks = [];
    // Moderatere Anzahl an Funken für subtileren Effekt
    const actualSparkCount = sparkCount + Math.floor(Math.random() * 2);
    
    for (let i = 0; i < actualSparkCount; i++) {
      // Entlang des gesamten Blitzes verteilen
      const seg = i * (zigzag.length - 1) / actualSparkCount;
      const segIndex = Math.floor(seg);
      const t = seg - segIndex;
      
      // Basis-Position
      const x = zigzag[segIndex].x + (zigzag[segIndex + 1].x - zigzag[segIndex].x) * t;
      const y = zigzag[segIndex].y + (zigzag[segIndex + 1].y - zigzag[segIndex].y) * t;
      
      // Sanfterer Versatz vom Blitz
      const angle = Math.random() * Math.PI * 2;
      const distance = 2 + Math.random() * 4; // Reduzierter Abstand für subtileres Sprühen
      
      sparks.push({
        x: x + Math.cos(angle) * distance,
        y: y + Math.sin(angle) * distance,
        size: 0.5 + Math.random() * 1 // Kleinere Funken
      });
    }
    return sparks;
  }

  onResize() {
    // Verhindere mehrfache Resize-Events in kurzer Zeit
    if (this.resizeTimeout) clearTimeout(this.resizeTimeout);
    
    this.resizeTimeout = setTimeout(() => {
      this.resizeCanvas();
      
      // Knoten und Verbindungen neu erstellen, um sie an die neue Größe anzupassen
      this.createNodes();
      this.createConnections();
    }, 200); // Warte 200ms, bevor die Größenanpassung durchgeführt wird
  }
  
  setDarkMode(isDarkMode) {
    this.isDarkMode = isDarkMode;
    
    // WebGL-Hintergrundfarbe aktualisieren
    if (this.useWebGL && this.gl) {
      const bgColor = this.hexToRgb(
        this.isDarkMode ? this.darkModeColors.background : this.lightModeColors.background
      );
      this.gl.clearColor(bgColor.r/255, bgColor.g/255, bgColor.b/255, 1.0);
    }
  }
}

// Initialize when DOM is loaded
document.addEventListener('DOMContentLoaded', () => {
  // Short delay to ensure DOM is fully loaded
  setTimeout(() => {
    if (!window.neuralNetworkBackground) {
      console.log('Creating Neural Network Background');
      window.neuralNetworkBackground = new NeuralNetworkBackground();
    }
  }, 100);
});

// Re-initialize when page is fully loaded (for safety)
window.addEventListener('load', () => {
  if (!window.neuralNetworkBackground) {
    console.log('Re-initializing Neural Network Background on full load');
    window.neuralNetworkBackground = new NeuralNetworkBackground();
  }
});

// Clean up when window is closed
window.addEventListener('beforeunload', () => {
  if (window.neuralNetworkBackground) {
    window.neuralNetworkBackground.destroy();
  }
}); 