Source

utils/aiFeatures.js

/**
 * @fileoverview AI-powered features for animated backgrounds
 * @module AIFeatures
 */

/**
 * Intelligent animation optimizer using machine learning principles
 */
export class IntelligentOptimizer {
  constructor() {
    this.performanceHistory = [];
    this.optimizationRules = new Map();
    this.deviceProfile = null;
    this.learningEnabled = true;
    this.adaptiveSettings = {
      particleCount: 100,
      animationSpeed: 1.0,
      effectIntensity: 0.8,
      renderQuality: 'medium'
    };
  }

  /**
   * Initialize device profiling and optimization
   */
  async initialize() {
    await this.profileDevice();
    this.loadOptimizationRules();
    this.startPerformanceMonitoring();
  }

  /**
   * Profile device capabilities
   */
  async profileDevice() {
    const canvas = document.createElement('canvas');
    const gl = canvas.getContext('webgl2') || canvas.getContext('webgl');
    
    this.deviceProfile = {
      // Hardware info
      gpu: this.getGPUInfo(gl),
      memory: this.getMemoryInfo(),
      cores: navigator.hardwareConcurrency || 4,
      
      // Performance benchmarks
      benchmarks: await this.runBenchmarks(canvas),
      
      // Device characteristics
      screen: {
        width: screen.width,
        height: screen.height,
        pixelRatio: window.devicePixelRatio
      },
      
      // Network info
      connection: this.getConnectionInfo(),
      
      // Battery info (if available)
      battery: await this.getBatteryInfo()
    };
  }

  /**
   * Get GPU information
   * @param {WebGLRenderingContext} gl - WebGL context
   * @returns {Object} GPU info
   */
  getGPUInfo(gl) {
    if (!gl) return { vendor: 'unknown', renderer: 'unknown' };
    
    const debugInfo = gl.getExtension('WEBGL_debug_renderer_info');
    return {
      vendor: debugInfo ? gl.getParameter(debugInfo.UNMASKED_VENDOR_WEBGL) : 'unknown',
      renderer: debugInfo ? gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL) : 'unknown',
      version: gl.getParameter(gl.VERSION),
      maxTextureSize: gl.getParameter(gl.MAX_TEXTURE_SIZE),
      maxVertexAttribs: gl.getParameter(gl.MAX_VERTEX_ATTRIBS)
    };
  }

  /**
   * Get memory information
   * @returns {Object} Memory info
   */
  getMemoryInfo() {
    if (performance.memory) {
      return {
        used: performance.memory.usedJSHeapSize,
        total: performance.memory.totalJSHeapSize,
        limit: performance.memory.jsHeapSizeLimit
      };
    }
    return { used: 0, total: 0, limit: 0 };
  }

  /**
   * Get connection information
   * @returns {Object} Connection info
   */
  getConnectionInfo() {
    if (navigator.connection) {
      return {
        effectiveType: navigator.connection.effectiveType,
        downlink: navigator.connection.downlink,
        rtt: navigator.connection.rtt,
        saveData: navigator.connection.saveData
      };
    }
    return { effectiveType: 'unknown', downlink: 0, rtt: 0, saveData: false };
  }

  /**
   * Get battery information
   * @returns {Promise<Object>} Battery info
   */
  async getBatteryInfo() {
    try {
      if (navigator.getBattery) {
        const battery = await navigator.getBattery();
        return {
          level: battery.level,
          charging: battery.charging,
          dischargingTime: battery.dischargingTime,
          chargingTime: battery.chargingTime
        };
      }
    } catch (error) {
      console.warn('Battery API not available');
    }
    return { level: 1, charging: true, dischargingTime: Infinity, chargingTime: 0 };
  }

  /**
   * Run performance benchmarks
   * @param {HTMLCanvasElement} canvas - Canvas element
   * @returns {Promise<Object>} Benchmark results
   */
  async runBenchmarks(canvas) {
    const benchmarks = {
      rendering: await this.benchmarkRendering(canvas),
      computation: await this.benchmarkComputation(),
      memory: await this.benchmarkMemory()
    };
    
    return benchmarks;
  }

  /**
   * Benchmark rendering performance
   * @param {HTMLCanvasElement} canvas - Canvas element
   * @returns {Promise<Object>} Rendering benchmark
   */
  async benchmarkRendering(canvas) {
    const ctx = canvas.getContext('2d');
    canvas.width = 800;
    canvas.height = 600;
    
    const startTime = performance.now();
    const iterations = 1000;
    
    // Simple rendering test
    for (let i = 0; i < iterations; i++) {
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      ctx.beginPath();
      ctx.arc(Math.random() * canvas.width, Math.random() * canvas.height, 10, 0, Math.PI * 2);
      ctx.fill();
    }
    
    const endTime = performance.now();
    const renderTime = endTime - startTime;
    
    return {
      renderTime,
      fps: (iterations / renderTime) * 1000,
      score: Math.min(100, Math.max(0, (60 * 1000) / renderTime))
    };
  }

  /**
   * Benchmark computation performance
   * @returns {Promise<Object>} Computation benchmark
   */
  async benchmarkComputation() {
    const startTime = performance.now();
    const iterations = 100000;
    
    // CPU-intensive calculation
    let result = 0;
    for (let i = 0; i < iterations; i++) {
      result += Math.sin(i) * Math.cos(i) * Math.sqrt(i);
    }
    
    const endTime = performance.now();
    const computeTime = endTime - startTime;
    
    return {
      computeTime,
      score: Math.min(100, Math.max(0, 1000 / computeTime))
    };
  }

  /**
   * Benchmark memory performance
   * @returns {Promise<Object>} Memory benchmark
   */
  async benchmarkMemory() {
    const startMemory = this.getMemoryInfo();
    
    // Create large arrays to test memory allocation
    const arrays = [];
    const arraySize = 100000;
    const arrayCount = 50;
    
    const startTime = performance.now();
    
    for (let i = 0; i < arrayCount; i++) {
      arrays.push(new Float32Array(arraySize));
    }
    
    const midTime = performance.now();
    const midMemory = this.getMemoryInfo();
    
    // Cleanup
    arrays.length = 0;
    
    const endTime = performance.now();
    const endMemory = this.getMemoryInfo();
    
    return {
      allocationTime: midTime - startTime,
      cleanupTime: endTime - midTime,
      memoryUsed: midMemory.used - startMemory.used,
      score: Math.min(100, Math.max(0, 1000 / (midTime - startTime)))
    };
  }

  /**
   * Load optimization rules based on device profile
   */
  loadOptimizationRules() {
    const profile = this.deviceProfile;
    
    // Mobile device optimizations
    if (this.isMobileDevice()) {
      this.optimizationRules.set('particleCount', () => Math.min(50, this.adaptiveSettings.particleCount));
      this.optimizationRules.set('animationSpeed', () => 0.8);
      this.optimizationRules.set('effectIntensity', () => 0.6);
    }
    
    // Low-end device optimizations
    if (this.isLowEndDevice()) {
      this.optimizationRules.set('renderQuality', () => 'low');
      this.optimizationRules.set('particleCount', () => Math.min(30, this.adaptiveSettings.particleCount));
    }
    
    // High-end device optimizations
    if (this.isHighEndDevice()) {
      this.optimizationRules.set('particleCount', () => Math.min(500, this.adaptiveSettings.particleCount * 2));
      this.optimizationRules.set('effectIntensity', () => 1.0);
      this.optimizationRules.set('renderQuality', () => 'high');
    }
    
    // Battery-based optimizations
    if (profile.battery && profile.battery.level < 0.2 && !profile.battery.charging) {
      this.optimizationRules.set('batteryMode', () => true);
      this.optimizationRules.set('particleCount', () => Math.min(20, this.adaptiveSettings.particleCount));
      this.optimizationRules.set('animationSpeed', () => 0.5);
    }
  }

  /**
   * Check if device is mobile
   * @returns {boolean} Is mobile device
   */
  isMobileDevice() {
    return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
  }

  /**
   * Check if device is low-end
   * @returns {boolean} Is low-end device
   */
  isLowEndDevice() {
    const profile = this.deviceProfile;
    return (
      profile.cores <= 2 ||
      profile.memory.limit < 1000000000 || // Less than 1GB heap
      profile.benchmarks.rendering.score < 30 ||
      profile.benchmarks.computation.score < 30
    );
  }

  /**
   * Check if device is high-end
   * @returns {boolean} Is high-end device
   */
  isHighEndDevice() {
    const profile = this.deviceProfile;
    return (
      profile.cores >= 8 &&
      profile.memory.limit > 4000000000 && // More than 4GB heap
      profile.benchmarks.rendering.score > 80 &&
      profile.benchmarks.computation.score > 80 &&
      profile.gpu.renderer.includes('GeForce') || profile.gpu.renderer.includes('Radeon')
    );
  }

  /**
   * Start performance monitoring
   */
  startPerformanceMonitoring() {
    setInterval(() => {
      this.collectPerformanceData();
      this.adaptSettings();
    }, 5000); // Check every 5 seconds
  }

  /**
   * Collect performance data
   */
  collectPerformanceData() {
    const now = performance.now();
    const memory = this.getMemoryInfo();
    
    const data = {
      timestamp: now,
      memory: memory.used,
      fps: this.getCurrentFPS(),
      batteryLevel: this.deviceProfile.battery ? this.deviceProfile.battery.level : 1
    };
    
    this.performanceHistory.push(data);
    
    // Keep only last 60 entries (5 minutes of data)
    if (this.performanceHistory.length > 60) {
      this.performanceHistory.shift();
    }
  }

  /**
   * Get current FPS estimate
   * @returns {number} Current FPS
   */
  getCurrentFPS() {
    // This would need to be integrated with the main animation loop
    // For now, return a placeholder
    return 60;
  }

  /**
   * Adapt settings based on performance history
   */
  adaptSettings() {
    if (!this.learningEnabled || this.performanceHistory.length < 5) return;
    
    const recentData = this.performanceHistory.slice(-5);
    const avgFPS = recentData.reduce((sum, data) => sum + data.fps, 0) / recentData.length;
    const memoryTrend = this.calculateMemoryTrend(recentData);
    
    // Adapt based on FPS
    if (avgFPS < 30) {
      this.adaptiveSettings.particleCount = Math.max(10, this.adaptiveSettings.particleCount * 0.8);
      this.adaptiveSettings.effectIntensity = Math.max(0.3, this.adaptiveSettings.effectIntensity * 0.9);
    } else if (avgFPS > 55) {
      this.adaptiveSettings.particleCount = Math.min(200, this.adaptiveSettings.particleCount * 1.1);
      this.adaptiveSettings.effectIntensity = Math.min(1.0, this.adaptiveSettings.effectIntensity * 1.05);
    }
    
    // Adapt based on memory usage
    if (memoryTrend > 0) { // Memory increasing
      this.adaptiveSettings.particleCount = Math.max(10, this.adaptiveSettings.particleCount * 0.9);
    }
    
    // Apply optimization rules
    this.optimizationRules.forEach((rule, key) => {
      this.adaptiveSettings[key] = rule();
    });
  }

  /**
   * Calculate memory usage trend
   * @param {Array} data - Performance data array
   * @returns {number} Memory trend (positive = increasing)
   */
  calculateMemoryTrend(data) {
    if (data.length < 2) return 0;
    
    const first = data[0].memory;
    const last = data[data.length - 1].memory;
    
    return (last - first) / first;
  }

  /**
   * Get optimized settings
   * @returns {Object} Optimized settings
   */
  getOptimizedSettings() {
    return { ...this.adaptiveSettings };
  }

  /**
   * Enable/disable learning
   * @param {boolean} enabled - Learning enabled
   */
  setLearningEnabled(enabled) {
    this.learningEnabled = enabled;
  }
}

/**
 * Content generation using AI-inspired algorithms
 */
export class ContentGenerator {
  constructor() {
    this.patterns = new Map();
    this.colorHarmony = new ColorHarmonyGenerator();
    this.animationPatterns = new AnimationPatternGenerator();
  }

  /**
   * Generate harmonious color palette
   * @param {string} baseColor - Base color in hex
   * @param {string} harmonyType - Type of color harmony
   * @returns {Array} Generated color palette
   */
  generateColorPalette(baseColor, harmonyType = 'complementary') {
    return this.colorHarmony.generate(baseColor, harmonyType);
  }

  /**
   * Generate animation sequence
   * @param {Object} parameters - Generation parameters
   * @returns {Object} Generated animation sequence
   */
  generateAnimationSequence(parameters) {
    return this.animationPatterns.generate(parameters);
  }
}

/**
 * Color harmony generation using color theory
 */
class ColorHarmonyGenerator {
  /**
   * Generate color palette based on harmony rules
   * @param {string} baseColor - Base color in hex
   * @param {string} harmonyType - Harmony type
   * @returns {Array} Color palette
   */
  generate(baseColor, harmonyType) {
    const hsl = this.hexToHsl(baseColor);
    const colors = [baseColor];
    
    switch (harmonyType) {
      case 'complementary':
        colors.push(this.hslToHex([(hsl[0] + 180) % 360, hsl[1], hsl[2]]));
        break;
        
      case 'triadic':
        colors.push(this.hslToHex([(hsl[0] + 120) % 360, hsl[1], hsl[2]]));
        colors.push(this.hslToHex([(hsl[0] + 240) % 360, hsl[1], hsl[2]]));
        break;
        
      case 'analogous':
        colors.push(this.hslToHex([(hsl[0] + 30) % 360, hsl[1], hsl[2]]));
        colors.push(this.hslToHex([(hsl[0] - 30 + 360) % 360, hsl[1], hsl[2]]));
        break;
        
      case 'tetradic':
        colors.push(this.hslToHex([(hsl[0] + 90) % 360, hsl[1], hsl[2]]));
        colors.push(this.hslToHex([(hsl[0] + 180) % 360, hsl[1], hsl[2]]));
        colors.push(this.hslToHex([(hsl[0] + 270) % 360, hsl[1], hsl[2]]));
        break;
        
      case 'monochromatic':
        colors.push(this.hslToHex([hsl[0], hsl[1] * 0.7, hsl[2] * 1.2]));
        colors.push(this.hslToHex([hsl[0], hsl[1] * 1.3, hsl[2] * 0.8]));
        break;
    }
    
    return colors;
  }

  /**
   * Convert hex to HSL
   * @param {string} hex - Hex color
   * @returns {Array} HSL values
   */
  hexToHsl(hex) {
    const r = parseInt(hex.slice(1, 3), 16) / 255;
    const g = parseInt(hex.slice(3, 5), 16) / 255;
    const b = parseInt(hex.slice(5, 7), 16) / 255;
    
    const max = Math.max(r, g, b);
    const min = Math.min(r, g, b);
    const diff = max - min;
    const sum = max + min;
    const l = sum / 2;
    
    let h, s;
    
    if (diff === 0) {
      h = s = 0;
    } else {
      s = l > 0.5 ? diff / (2 - sum) : diff / sum;
      
      switch (max) {
        case r: h = ((g - b) / diff + (g < b ? 6 : 0)) / 6; break;
        case g: h = ((b - r) / diff + 2) / 6; break;
        case b: h = ((r - g) / diff + 4) / 6; break;
      }
    }
    
    return [h * 360, s, l];
  }

  /**
   * Convert HSL to hex
   * @param {Array} hsl - HSL values
   * @returns {string} Hex color
   */
  hslToHex([h, s, l]) {
    h /= 360;
    
    const hue2rgb = (p, q, t) => {
      if (t < 0) t += 1;
      if (t > 1) t -= 1;
      if (t < 1/6) return p + (q - p) * 6 * t;
      if (t < 1/2) return q;
      if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
      return p;
    };
    
    let r, g, b;
    
    if (s === 0) {
      r = g = b = l;
    } else {
      const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
      const p = 2 * l - q;
      r = hue2rgb(p, q, h + 1/3);
      g = hue2rgb(p, q, h);
      b = hue2rgb(p, q, h - 1/3);
    }
    
    const toHex = (c) => {
      const hex = Math.round(c * 255).toString(16);
      return hex.length === 1 ? '0' + hex : hex;
    };
    
    return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
  }
}

/**
 * Animation pattern generation using procedural algorithms
 */
class AnimationPatternGenerator {
  /**
   * Generate animation pattern
   * @param {Object} parameters - Generation parameters
   * @returns {Object} Animation pattern
   */
  generate(parameters) {
    const {
      complexity = 'medium',
      style = 'organic',
      duration = 10000,
      elements = 100
    } = parameters;
    
    const pattern = {
      keyframes: this.generateKeyframes(complexity, duration),
      elements: this.generateElements(elements, style),
      timing: this.generateTiming(complexity)
    };
    
    return pattern;
  }

  /**
   * Generate keyframes for animation
   * @param {string} complexity - Animation complexity
   * @param {number} duration - Animation duration
   * @returns {Array} Keyframes
   */
  generateKeyframes(complexity, duration) {
    const frameCount = complexity === 'high' ? 20 : complexity === 'medium' ? 10 : 5;
    const keyframes = [];
    
    for (let i = 0; i <= frameCount; i++) {
      const progress = i / frameCount;
      const time = progress * duration;
      
      keyframes.push({
        time,
        transforms: {
          scale: 1 + Math.sin(progress * Math.PI * 2) * 0.2,
          rotation: progress * 360,
          opacity: 0.5 + Math.sin(progress * Math.PI) * 0.5
        },
        colors: this.interpolateColors(progress)
      });
    }
    
    return keyframes;
  }

  /**
   * Generate animation elements
   * @param {number} count - Element count
   * @param {string} style - Animation style
   * @returns {Array} Animation elements
   */
  generateElements(count, style) {
    const elements = [];
    
    for (let i = 0; i < count; i++) {
      const element = {
        id: i,
        position: {
          x: Math.random(),
          y: Math.random()
        },
        velocity: this.generateVelocity(style),
        properties: this.generateProperties(style)
      };
      
      elements.push(element);
    }
    
    return elements;
  }

  /**
   * Generate velocity based on style
   * @param {string} style - Animation style
   * @returns {Object} Velocity
   */
  generateVelocity(style) {
    switch (style) {
      case 'organic':
        return {
          x: (Math.random() - 0.5) * 0.02,
          y: (Math.random() - 0.5) * 0.02
        };
      case 'geometric':
        return {
          x: Math.random() < 0.5 ? -0.01 : 0.01,
          y: Math.random() < 0.5 ? -0.01 : 0.01
        };
      case 'chaotic':
        return {
          x: (Math.random() - 0.5) * 0.05,
          y: (Math.random() - 0.5) * 0.05
        };
      default:
        return { x: 0, y: 0 };
    }
  }

  /**
   * Generate element properties
   * @param {string} style - Animation style
   * @returns {Object} Properties
   */
  generateProperties(style) {
    return {
      size: Math.random() * 10 + 5,
      color: `hsl(${Math.random() * 360}, 70%, 60%)`,
      shape: style === 'geometric' ? 'square' : 'circle',
      lifespan: Math.random() * 10000 + 5000
    };
  }

  /**
   * Generate timing function
   * @param {string} complexity - Animation complexity
   * @returns {string} Timing function
   */
  generateTiming(complexity) {
    const functions = {
      low: 'linear',
      medium: 'ease-in-out',
      high: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)'
    };
    
    return functions[complexity] || 'ease';
  }

  /**
   * Interpolate colors for animation
   * @param {number} progress - Animation progress (0-1)
   * @returns {Object} Color values
   */
  interpolateColors(progress) {
    const hue1 = 240; // Blue
    const hue2 = 300; // Purple
    const currentHue = hue1 + (hue2 - hue1) * progress;
    
    return {
      primary: `hsl(${currentHue}, 70%, 60%)`,
      secondary: `hsl(${(currentHue + 60) % 360}, 50%, 40%)`,
      accent: `hsl(${(currentHue + 120) % 360}, 80%, 70%)`
    };
  }
}

// Export singleton instances
export const intelligentOptimizer = new IntelligentOptimizer();
export const contentGenerator = new ContentGenerator();