/**
 * D3.js Erweiterungen für verbesserte Mindmap-Funktionalität
 * Diese Datei enthält zusätzliche Hilfsfunktionen und Erweiterungen für D3.js
 */

class D3Extensions {
  /**
   * Erstellt einen verbesserten radialen Farbverlauf
   * @param {Object} defs - Das D3 defs Element
   * @param {string} id - ID für den Gradienten
   * @param {string} baseColor - Grundfarbe in hexadezimal oder RGB
   * @returns {Object} - Das erstellte Gradient-Element
   */
  static createEnhancedRadialGradient(defs, id, baseColor) {
    // Farben berechnen
    const d3Color = d3.color(baseColor);
    const lightColor = d3Color.brighter(0.7);
    const darkColor = d3Color.darker(0.3);
    const midColor = d3Color;
    
    // Gradient erstellen
    const gradient = defs.append('radialGradient')
      .attr('id', id)
      .attr('cx', '30%')
      .attr('cy', '30%')
      .attr('r', '70%');
      
    // Farbstops hinzufügen für realistischeren Verlauf
    gradient.append('stop')
      .attr('offset', '0%')
      .attr('stop-color', lightColor.formatHex());
      
    gradient.append('stop')
      .attr('offset', '50%')
      .attr('stop-color', midColor.formatHex());
      
    gradient.append('stop')
      .attr('offset', '100%')
      .attr('stop-color', darkColor.formatHex());
      
    return gradient;
  }
  
  /**
   * Erstellt einen Glüheffekt-Filter
   * @param {Object} defs - D3-Referenz auf den defs-Bereich
   * @param {String} id - ID des Filters
   * @param {String} color - Farbe des Glüheffekts (Hex-Code)
   * @param {Number} strength - Stärke des Glüheffekts
   * @returns {Object} D3-Referenz auf den erstellten Filter
   */
  static createGlowFilter(defs, id, color = '#b38fff', strength = 5) {
    const filter = defs.append('filter')
      .attr('id', id)
      .attr('x', '-50%')
      .attr('y', '-50%')
      .attr('width', '200%')
      .attr('height', '200%');
      
    // Unschärfe-Effekt
    filter.append('feGaussianBlur')
      .attr('in', 'SourceGraphic')
      .attr('stdDeviation', strength)
      .attr('result', 'blur');
      
    // Farbverstärkung für den Glüheffekt
    filter.append('feColorMatrix')
      .attr('in', 'blur')
      .attr('type', 'matrix')
      .attr('values', '0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 18 -7')
      .attr('result', 'glow');
      
    // Farbflut mit der angegebenen Farbe
    filter.append('feFlood')
      .attr('flood-color', color)
      .attr('flood-opacity', '0.7')
      .attr('result', 'color');
      
    // Zusammensetzen des Glüheffekts mit der Farbe
    filter.append('feComposite')
      .attr('in', 'color')
      .attr('in2', 'glow')
      .attr('operator', 'in')
      .attr('result', 'glow-color');
      
    // Zusammenfügen aller Ebenen
    const feMerge = filter.append('feMerge');
    feMerge.append('feMergeNode')
      .attr('in', 'glow-color');
    feMerge.append('feMergeNode')
      .attr('in', 'SourceGraphic');
      
    return filter;
  }
  
  /**
   * Berechnet eine konsistente Farbe aus einem String
   * @param {string} str - Eingabestring
   * @returns {string} - Generierte Farbe als Hex-String
   */
  static stringToColor(str) {
    let hash = 0;
    for (let i = 0; i < str.length; i++) {
      hash = str.charCodeAt(i) + ((hash << 5) - hash);
    }
    
    // Basis-Farbpalette für konsistente Farben
    const colorPalette = [
      "#4299E1", // Blau
      "#9F7AEA", // Lila
      "#ED64A6", // Pink
      "#48BB78", // Grün
      "#ECC94B", // Gelb
      "#F56565", // Rot
      "#38B2AC", // Türkis
      "#ED8936", // Orange
      "#667EEA", // Indigo
    ];
    
    // Farbe aus der Palette wählen basierend auf dem Hash
    const colorIndex = Math.abs(hash) % colorPalette.length;
    return colorPalette[colorIndex];
  }
  
  /**
   * Erstellt einen Schatteneffekt-Filter
   * @param {Object} defs - D3-Referenz auf den defs-Bereich
   * @param {String} id - ID des Filters
   * @returns {Object} D3-Referenz auf den erstellten Filter
   */
  static createShadowFilter(defs, id) {
    const filter = defs.append('filter')
      .attr('id', id)
      .attr('x', '-50%')
      .attr('y', '-50%')
      .attr('width', '200%')
      .attr('height', '200%');
      
    // Einfacher Schlagschatten
    filter.append('feDropShadow')
      .attr('dx', 0)
      .attr('dy', 4)
      .attr('stdDeviation', 4)
      .attr('flood-color', 'rgba(0, 0, 0, 0.3)');
      
    return filter;
  }
  
  /**
   * Erstellt einen Glasmorphismus-Effekt-Filter
   * @param {Object} defs - D3-Referenz auf den defs-Bereich
   * @param {String} id - ID des Filters
   * @returns {Object} D3-Referenz auf den erstellten Filter
   */
  static createGlassMorphismFilter(defs, id) {
    const filter = defs.append('filter')
      .attr('id', id)
      .attr('x', '-50%')
      .attr('y', '-50%')
      .attr('width', '200%')
      .attr('height', '200%');
      
    // Hintergrund-Unschärfe für den Glaseffekt
    filter.append('feGaussianBlur')
      .attr('in', 'SourceGraphic')
      .attr('stdDeviation', 8)
      .attr('result', 'blur');
      
    // Hellere Farbe für den Glaseffekt
    filter.append('feColorMatrix')
      .attr('in', 'blur')
      .attr('type', 'matrix')
      .attr('values', '1 0 0 0 0.1 0 1 0 0 0.1 0 0 1 0 0.1 0 0 0 0.6 0')
      .attr('result', 'glass');
      
    // Überlagerung mit dem Original
    const feMerge = filter.append('feMerge');
    feMerge.append('feMergeNode')
      .attr('in', 'glass');
    feMerge.append('feMergeNode')
      .attr('in', 'SourceGraphic');
      
    return filter;
  }
  
  /**
   * Erstellt einen verstärkten Glasmorphismus-Effekt mit Farbverlauf
   * @param {Object} defs - D3-Referenz auf den defs-Bereich
   * @param {String} id - ID des Filters
   * @param {String} color1 - Erste Farbe des Verlaufs (Hex-Code)
   * @param {String} color2 - Zweite Farbe des Verlaufs (Hex-Code)
   * @returns {Object} D3-Referenz auf den erstellten Filter
   */
  static createEnhancedGlassMorphismFilter(defs, id, color1 = '#b38fff', color2 = '#58a9ff') {
    // Farbverlauf für den Glaseffekt definieren
    const gradientId = `gradient-${id}`;
    const gradient = defs.append('linearGradient')
      .attr('id', gradientId)
      .attr('x1', '0%')
      .attr('y1', '0%')
      .attr('x2', '100%')
      .attr('y2', '100%');
      
    gradient.append('stop')
      .attr('offset', '0%')
      .attr('stop-color', color1)
      .attr('stop-opacity', '0.3');
      
    gradient.append('stop')
      .attr('offset', '100%')
      .attr('stop-color', color2)
      .attr('stop-opacity', '0.3');
    
    // Filter erstellen
    const filter = defs.append('filter')
      .attr('id', id)
      .attr('x', '-50%')
      .attr('y', '-50%')
      .attr('width', '200%')
      .attr('height', '200%');
    
    // Hintergrund-Unschärfe  
    filter.append('feGaussianBlur')
      .attr('in', 'SourceGraphic')
      .attr('stdDeviation', 6)
      .attr('result', 'blur');
      
    // Farbverlauf einfügen
    const feImage = filter.append('feImage')
      .attr('xlink:href', `#${gradientId}`)
      .attr('result', 'gradient')
      .attr('x', '0%')
      .attr('y', '0%')
      .attr('width', '100%')
      .attr('height', '100%')
      .attr('preserveAspectRatio', 'none');
      
    // Zusammenfügen aller Ebenen
    const feMerge = filter.append('feMerge');
    feMerge.append('feMergeNode')
      .attr('in', 'blur');
    feMerge.append('feMergeNode')
      .attr('in', 'gradient');
    feMerge.append('feMergeNode')
      .attr('in', 'SourceGraphic');
      
    return filter;
  }
  
  /**
   * Erstellt einen 3D-Glaseffekt mit verbesserter Tiefe und Reflexionen
   * @param {Object} defs - D3-Referenz auf den defs-Bereich
   * @param {String} id - ID des Filters
   * @returns {Object} D3-Referenz auf den erstellten Filter
   */
  static create3DGlassEffect(defs, id) {
    const filter = defs.append('filter')
      .attr('id', id)
      .attr('x', '-50%')
      .attr('y', '-50%')
      .attr('width', '200%')
      .attr('height', '200%');
      
    // Farbmatrix für Transparenz
    filter.append('feColorMatrix')
      .attr('type', 'matrix')
      .attr('in', 'SourceGraphic')
      .attr('values', '1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0.7 0')
      .attr('result', 'transparent');
    
    // Hintergrund-Unschärfe für Tiefe
    filter.append('feGaussianBlur')
      .attr('in', 'transparent')
      .attr('stdDeviation', '4')
      .attr('result', 'blurred');
    
    // Lichtquelle und Schattierung hinzufügen
    const lightSource = filter.append('feSpecularLighting')
      .attr('in', 'blurred')
      .attr('surfaceScale', '6')
      .attr('specularConstant', '1')
      .attr('specularExponent', '30')
      .attr('lighting-color', '#ffffff')
      .attr('result', 'specular');
      
    lightSource.append('fePointLight')
      .attr('x', '100')
      .attr('y', '100')
      .attr('z', '200');
    
    // Lichtreflexion verstärken
    filter.append('feComposite')
      .attr('in', 'specular')
      .attr('in2', 'SourceGraphic')
      .attr('operator', 'in')
      .attr('result', 'specularHighlight');
    
    // Inneren Schatten erzeugen
    const innerShadow = filter.append('feOffset')
      .attr('in', 'SourceAlpha')
      .attr('dx', '0')
      .attr('dy', '1')
      .attr('result', 'offsetblur');
      
    innerShadow.append('feGaussianBlur')
      .attr('in', 'offsetblur')
      .attr('stdDeviation', '2')
      .attr('result', 'innerShadow');
      
    filter.append('feComposite')
      .attr('in', 'innerShadow')
      .attr('in2', 'SourceGraphic')
      .attr('operator', 'out')
      .attr('result', 'innerShadowEffect');
    
    // Schichten kombinieren
    const feMerge = filter.append('feMerge');
    feMerge.append('feMergeNode')
      .attr('in', 'blurred');
    feMerge.append('feMergeNode')
      .attr('in', 'innerShadowEffect');
    feMerge.append('feMergeNode')
      .attr('in', 'specularHighlight');
    feMerge.append('feMergeNode')
      .attr('in', 'SourceGraphic');
      
    return filter;
  }
  
  /**
   * Fügt einen Partikelsystem-Effekt für interaktive Knoten hinzu
   * @param {Object} parent - Das übergeordnete SVG-Element
   * @param {number} x - X-Koordinate des Zentrums
   * @param {number} y - Y-Koordinate des Zentrums
   * @param {string} color - Partikelfarbe (Hex-Code)
   * @param {number} count - Anzahl der Partikel
   */
  static createParticleEffect(parent, x, y, color = '#b38fff', count = 5) {
    const particles = [];
    
    for (let i = 0; i < count; i++) {
      const particle = parent.append('circle')
        .attr('cx', x)
        .attr('cy', y)
        .attr('r', 0)
        .attr('fill', color)
        .style('opacity', 0.8);
        
      particles.push(particle);
      
      // Partikel animieren
      animateParticle(particle);
    }
    
    function animateParticle(particle) {
      // Zufällige Richtung und Geschwindigkeit
      const angle = Math.random() * Math.PI * 2;
      const speed = 1 + Math.random() * 2;
      const distance = 20 + Math.random() * 30;
      
      // Zielposition berechnen
      const targetX = x + Math.cos(angle) * distance;
      const targetY = y + Math.sin(angle) * distance;
      
      // Animation mit zufälliger Dauer
      const duration = 1000 + Math.random() * 500;
      
      particle
        .attr('r', 0)
        .style('opacity', 0.8)
        .transition()
        .duration(duration)
        .attr('cx', targetX)
        .attr('cy', targetY)
        .attr('r', 2 + Math.random() * 3)
        .style('opacity', 0)
        .on('end', function() {
          // Partikel entfernen
          particle.remove();
        });
    }
  }
  
  /**
   * Führt eine Pulsanimation auf einem Knoten durch
   * @param {Object} node - D3-Knoten-Selektion
   * @returns {void}
   */
  static pulseAnimation(node) {
    if (!node) return;
    
    const circle = node.select('circle');
    const originalRadius = parseFloat(circle.attr('r'));
    const originalFill = circle.attr('fill');
    
    // Pulsanimation
    circle
      .transition()
      .duration(400)
      .attr('r', originalRadius * 1.3)
      .attr('fill', '#b38fff')
      .transition()
      .duration(400)
      .attr('r', originalRadius)
      .attr('fill', originalFill);
  }
  
  /**
   * Berechnet eine adaptive Schriftgröße basierend auf der Textlänge
   * @param {string} text - Der anzuzeigende Text
   * @param {number} maxSize - Maximale Schriftgröße in Pixel
   * @param {number} minSize - Minimale Schriftgröße in Pixel
   * @returns {number} - Die berechnete Schriftgröße
   */
  static getAdaptiveFontSize(text, maxSize = 14, minSize = 10) {
    if (!text) return maxSize;
    
    // Linear die Schriftgröße basierend auf der Textlänge anpassen
    const length = text.length;
    if (length <= 6) return maxSize;
    if (length >= 20) return minSize;
    
    // Lineare Interpolation
    const factor = (length - 6) / (20 - 6);
    return maxSize - factor * (maxSize - minSize);
  }
  
  /**
   * Fügt einen Pulsierenden Effekt zu einer Selektion hinzu
   * @param {Object} selection - D3-Selektion
   * @param {number} duration - Dauer eines Puls-Zyklus in ms
   * @param {number} minOpacity - Minimale Opazität
   * @param {number} maxOpacity - Maximale Opazität
   */
  static addPulseEffect(selection, duration = 1500, minOpacity = 0.4, maxOpacity = 0.9) {
    function pulse() {
      selection
        .transition()
        .duration(duration / 2)
        .style('opacity', minOpacity)
        .transition()
        .duration(duration / 2)
        .style('opacity', maxOpacity)
        .on('end', pulse);
    }
    
    // Initialen Stil setzen
    selection.style('opacity', maxOpacity);
    
    // Pulsanimation starten
    pulse();
  }
  
  /**
   * Verarbeitet Daten aus der Datenbank für die Mindmap-Visualisierung
   * @param {Array} databaseNodes - Knotendaten aus der Datenbank
   * @param {Array} links - Verbindungsdaten oder null für automatische Extraktion
   * @returns {Object} Aufbereitete Daten für D3.js
   */
  static processDbNodesForVisualization(databaseNodes, links = null) {
    // Überprüfe, ob Daten vorhanden sind
    if (!databaseNodes || databaseNodes.length === 0) {
      console.warn('Keine Knotendaten zum Verarbeiten vorhanden');
      return { nodes: [], links: [] };
    }
    
    // Knoten mit D3-Kompatiblem Format erstellen
    const nodes = databaseNodes.map(node => {
      // Farbgenerierung, falls keine vorhanden
      const nodeColor = node.color_code || 
                       node.color || 
                       D3Extensions.stringToColor(node.name || 'default');
      
      return {
        id: node.id,
        name: node.name,
        description: node.description || '',
        thought_count: node.thought_count || 0,
        color: nodeColor,
        // Zusätzliche Attribute
        category_id: node.category_id,
        is_public: node.is_public !== undefined ? node.is_public : true,
        // Position, falls vorhanden
        x: node.x_position,
        y: node.y_position,
        // Größe, falls vorhanden
        scale: node.scale || 1.0
      };
    });
    
    // Verbindungen verarbeiten
    let processedLinks = [];
    
    if (links && Array.isArray(links)) {
      // Verwende übergebene Verbindungen
      processedLinks = links.map(link => {
        return {
          source: link.source,
          target: link.target,
          // Zusätzliche Attribute
          type: link.type || 'default',
          strength: link.strength || 1
        };
      });
    } else {
      // Extrahiere Verbindungen aus den Knoten
      databaseNodes.forEach(node => {
        if (node.connections && Array.isArray(node.connections)) {
          node.connections.forEach(conn => {
            processedLinks.push({
              source: node.id,
              target: conn.target,
              type: conn.type || 'default',
              strength: conn.strength || 1
            });
          });
        }
      });
    }
    
    return { nodes, links: processedLinks };
  }
}

// Globale Verfügbarkeit sicherstellen
window.D3Extensions = D3Extensions;
