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

class NeuralNetworkBackground {
  constructor() {
    // Canvas setup
    this.canvas = document.createElement('canvas');
    this.canvas.id = 'neural-network-background';
    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 = '-10'; // Ensure it's behind content but visible
    this.canvas.style.pointerEvents = 'none';
    this.canvas.style.opacity = '1'; // Force visibility
    
    // 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);
    }
    
    // WebGL context
    this.gl = this.canvas.getContext('webgl') || this.canvas.getContext('experimental-webgl');
    if (!this.gl) {
      console.warn('WebGL not supported, falling back to canvas rendering');
      this.gl = null;
      this.ctx = this.canvas.getContext('2d');
      this.useWebGL = false;
    } else {
      this.useWebGL = true;
    }
    
    // Animation properties
    this.nodes = [];
    this.connections = [];
    this.flows = []; // Flow animations along connections
    this.animationFrameId = null;
    this.isDarkMode = true; // Always use dark mode for the background
    
    // Colors - Updated to be more subtle
    this.darkModeColors = {
      background: '#050a14', // Darker blue-black background
      nodeColor: '#4a5568',  // Darker nodes for subtlety
      nodePulse: '#718096',  // Subtle blue pulse
      connectionColor: '#2d3748', // Darker connections
      flowColor: '#4a88ff80'   // Semi-transparent flow highlight
    };
    
    this.lightModeColors = {
      background: '#f9fafb',
      nodeColor: '#7c3aed',
      nodePulse: '#8b5cf6',
      connectionColor: '#a78bfa',
      flowColor: '#c4b5fd'
    };
    
    // Config - Updated to be more subtle, efficient, and elegant
    this.config = {
      nodeCount: 70,             // Reduced node count for better performance
      nodeSize: 0.6,             // Smaller nodes for subtlety
      nodeVariation: 0.3,        // Less variation for uniformity
      connectionDistance: 150,   // Shorter connections for cleaner look
      connectionOpacity: 0.15,   // More subtle connections
      animationSpeed: 0.04,      // Slower, more elegant movement
      pulseSpeed: 0.002,         // Very slow pulse for subtlety
      flowSpeed: 0.4,            // Slower flow animations
      flowDensity: 0.0008,       // Less frequent flow animations
      flowLength: 0.15           // Shorter flow animations
    };
    
    // Initialize
    this.init();
    
    // Event listeners
    window.addEventListener('resize', this.resizeCanvas.bind(this));
    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);
    
    // Create nodes with random positions and properties
    for (let i = 0; i < this.config.nodeCount; i++) {
      const node = {
        x: Math.random() * width,
        y: Math.random() * height,
        size: this.config.nodeSize + Math.random() * this.config.nodeVariation,
        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: []
      };
      
      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 = [];
      
      for (let j = i + 1; j < this.nodes.length; j++) {
        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) {
          // Create connection
          const connection = {
            from: i,
            to: j,
            distance: distance,
            opacity: Math.max(0, 1 - distance / this.config.connectionDistance) * this.config.connectionOpacity,
            hasFlow: false // Each connection can have a flow
          };
          
          this.connections.push(connection);
          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);
    
    // Update node positions - slower, more flowing movement
    for (let i = 0; i < this.nodes.length; i++) {
      const node = this.nodes[i];
      
      // Move node with slight randomness for organic feel
      node.speed.x += (Math.random() - 0.5) * 0.001;
      node.speed.y += (Math.random() - 0.5) * 0.001;
      
      // Dampen speeds for stability
      node.speed.x *= 0.99;
      node.speed.y *= 0.99;
      
      // Apply speed limits
      node.speed.x = Math.max(-this.config.animationSpeed, Math.min(this.config.animationSpeed, node.speed.x));
      node.speed.y = Math.max(-this.config.animationSpeed, Math.min(this.config.animationSpeed, node.speed.y));
      
      // Move node
      node.x += node.speed.x;
      node.y += node.speed.y;
      
      // Boundary check with smooth bounce
      if (node.x < 0 || node.x > width) {
        node.speed.x *= -0.8; // Softer bounce
        node.x = Math.max(0, Math.min(node.x, width));
      }
      
      if (node.y < 0 || node.y > height) {
        node.speed.y *= -0.8; // Softer bounce
        node.y = Math.max(0, Math.min(node.y, height));
      }
      
      // Update pulse phase
      node.pulsePhase += this.config.pulseSpeed;
      if (node.pulsePhase > Math.PI * 2) {
        node.pulsePhase -= Math.PI * 2;
      }
    }
    
    // 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
    for (let i = this.flows.length - 1; i >= 0; i--) {
      const flow = this.flows[i];
      flow.progress += this.config.flowSpeed / flow.connection.distance;
      
      // Remove completed flows
      if (flow.progress > 1.0) {
        this.flows.splice(i, 1);
      }
    }
  }
  
  // New method to create flow animations
  createNewFlow() {
    if (this.connections.length === 0) return;
    
    // 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
    });
  }
  
  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);
    
    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;
      
      // Size with subtle pulse effect
      const pulse = Math.sin(node.pulsePhase) * 0.2 + 1;
      sizes[i] = node.size * pulse * (node.connections.length > 3 ? 1.3 : 1);
    }
    
    // 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);
    
    // Set node color - more subtle
    const colorObj = this.isDarkMode ? this.darkModeColors : this.lightModeColors;
    const nodeColor = this.hexToRgb(colorObj.nodeColor);
    this.gl.uniform4f(
      this.programInfo.uniformLocations.color,
      nodeColor.r / 255,
      nodeColor.g / 255,
      nodeColor.b / 255,
      0.7  // Lower opacity for subtlety
    );
    
    // Draw nodes
    this.gl.enable(this.gl.BLEND);
    this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE);  // Additive blending for glow
    this.gl.drawArrays(this.gl.POINTS, 0, this.nodes.length);
  }
  
  renderConnectionsWebGL() {
    // For each connection, draw a line
    for (const connection of this.connections) {
      const fromNode = this.nodes[connection.from];
      const toNode = this.nodes[connection.to];
      
      // Line positions
      const positions = new Float32Array([
        fromNode.x, fromNode.y,
        toNode.x, toNode.y
      ]);
      
      // 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);
      
      // Disable point size attribute for lines
      this.gl.disableVertexAttribArray(this.programInfo.attribLocations.pointSize);
      
      // Set line color with connection opacity - darker, more subtle
      const colorObj = this.isDarkMode ? this.darkModeColors : this.lightModeColors;
      const lineColor = this.hexToRgb(colorObj.connectionColor);
      this.gl.uniform4f(
        this.programInfo.uniformLocations.color,
        lineColor.r / 255,
        lineColor.g / 255,
        lineColor.b / 255,
        connection.opacity * 0.8  // Reduced for subtlety
      );
      
      // Draw the line
      this.gl.enable(this.gl.BLEND);
      this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE);
      this.gl.lineWidth(1);
      this.gl.drawArrays(this.gl.LINES, 0, 2);
    }
  }
  
  // New method to render the flowing animations
  renderFlowsWebGL() {
    // For each flow, draw a segment along its connection
    for (const flow of this.flows) {
      const connection = flow.connection;
      const fromNode = this.nodes[connection.from];
      const toNode = this.nodes[connection.to];
      
      // Calculate flow position
      const startProgress = flow.progress;
      const endProgress = Math.min(1, startProgress + flow.length);
      
      // If flow hasn't started yet or has finished
      if (startProgress >= 1 || endProgress <= 0) continue;
      
      // Calculate actual positions
      const direction = flow.direction ? 1 : -1;
      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
        };
      }
      
      // Line positions for the flow
      const positions = new Float32Array([
        p1.x, p1.y,
        p2.x, p2.y
      ]);
      
      // 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);
      
      // Disable point size attribute for lines
      this.gl.disableVertexAttribArray(this.programInfo.attribLocations.pointSize);
      
      // Fade the flow at the beginning and end
      const fadeEdge = 0.2;
      const fadeOpacity = Math.min(
        startProgress / fadeEdge,
        (1 - endProgress) / fadeEdge,
        1
      );
      
      // Flow color - subtle glow
      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.4 * fadeOpacity // Subtle flow opacity
      );
      
      // Draw the flow line
      this.gl.enable(this.gl.BLEND);
      this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE);
      this.gl.lineWidth(1.5); // Slightly thicker for visibility
      this.gl.drawArrays(this.gl.LINES, 0, 2);
    }
  }
  
  renderCanvas() {
    // Clear canvas
    const width = this.canvas.width / (window.devicePixelRatio || 1);
    const height = this.canvas.height / (window.devicePixelRatio || 1);
    
    this.ctx.clearRect(0, 0, width, height);
    
    // Set background
    const backgroundColor = this.isDarkMode 
      ? this.darkModeColors.background 
      : this.lightModeColors.background;
      
    this.ctx.fillStyle = backgroundColor;
    this.ctx.fillRect(0, 0, width, height);
    
    // Draw connections
    const connectionColor = this.isDarkMode 
      ? this.darkModeColors.connectionColor 
      : this.lightModeColors.connectionColor;
      
    for (const connection of this.connections) {
      const fromNode = this.nodes[connection.from];
      const toNode = this.nodes[connection.to];
      
      this.ctx.beginPath();
      this.ctx.moveTo(fromNode.x, fromNode.y);
      this.ctx.lineTo(toNode.x, toNode.y);
      
      const rgbColor = this.hexToRgb(connectionColor);
      this.ctx.strokeStyle = `rgba(${rgbColor.r}, ${rgbColor.g}, ${rgbColor.b}, ${connection.opacity})`;
      this.ctx.stroke();
    }
    
    // Draw flows
    this.renderFlowsCanvas();
    
    // Draw nodes
    const nodeColor = this.isDarkMode 
      ? this.darkModeColors.nodeColor 
      : this.lightModeColors.nodeColor;
      
    const nodePulse = this.isDarkMode 
      ? this.darkModeColors.nodePulse 
      : this.lightModeColors.nodePulse;
      
    for (const node of this.nodes) {
      // Node with subtle glow effect
      const pulse = Math.sin(node.pulsePhase) * 0.2 + 1;
      const nodeSize = node.size * pulse * (node.connections.length > 3 ? 1.3 : 1);
      
      // Glow effect
      const glow = this.ctx.createRadialGradient(
        node.x, node.y, 0,
        node.x, node.y, nodeSize * 2
      );
      
      const rgbNodeColor = this.hexToRgb(nodeColor);
      const rgbPulseColor = this.hexToRgb(nodePulse);
      
      glow.addColorStop(0, `rgba(${rgbPulseColor.r}, ${rgbPulseColor.g}, ${rgbPulseColor.b}, 0.6)`);
      glow.addColorStop(0.5, `rgba(${rgbNodeColor.r}, ${rgbNodeColor.g}, ${rgbNodeColor.b}, 0.2)`);
      glow.addColorStop(1, `rgba(${rgbNodeColor.r}, ${rgbNodeColor.g}, ${rgbNodeColor.b}, 0)`);
      
      this.ctx.beginPath();
      this.ctx.arc(node.x, node.y, nodeSize * 2, 0, Math.PI * 2);
      this.ctx.fillStyle = glow;
      this.ctx.fill();
      
      // Main node
      this.ctx.beginPath();
      this.ctx.arc(node.x, node.y, nodeSize, 0, Math.PI * 2);
      this.ctx.fillStyle = nodeColor;
      this.ctx.fill();
    }
  }
  
  // New method to render flows in Canvas mode
  renderFlowsCanvas() {
    if (!this.ctx) return;
    
    const flowColor = this.isDarkMode 
      ? this.darkModeColors.flowColor 
      : this.lightModeColors.flowColor;
    
    const rgbFlowColor = this.hexToRgb(flowColor);
    
    for (const flow of this.flows) {
      const connection = flow.connection;
      const fromNode = this.nodes[connection.from];
      const toNode = this.nodes[connection.to];
      
      // Calculate flow position
      const startProgress = flow.progress;
      const endProgress = Math.min(1, startProgress + flow.length);
      
      // If flow hasn't started yet or has finished
      if (startProgress >= 1 || endProgress <= 0) continue;
      
      // Calculate actual positions
      const direction = flow.direction ? 1 : -1;
      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) * startProgress
        };
      } 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
        };
      }
      
      // Fade the flow at the beginning and end
      const fadeEdge = 0.2;
      const fadeOpacity = Math.min(
        startProgress / fadeEdge,
        (1 - endProgress) / fadeEdge,
        1
      );
      
      // Draw flow
      this.ctx.beginPath();
      this.ctx.moveTo(p1.x, p1.y);
      this.ctx.lineTo(p2.x, p2.y);
      this.ctx.strokeStyle = `rgba(${rgbFlowColor.r}, ${rgbFlowColor.g}, ${rgbFlowColor.b}, ${0.4 * fadeOpacity})`;
      this.ctx.lineWidth = 1.5;
      this.ctx.stroke();
    }
  }
  
  // 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);
    }
  }
}

// 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();
  }
}); 