import { useEffect, useRef, useState, useCallback } from 'react'; import type { DetectionConfig, DetectionResult, DetectionMetrics } from './types'; import { DetectionEngine } from './detection-engine'; interface UseDetectionOptions { modelVariant?: 'quantized' | 'standard' | 'full'; enableContinuous?: boolean; enableTrigger?: boolean; onDetection?: (detection: DetectionResult | null) => void; onError?: (error: Error) => void; } interface UseDetectionReturn { // State isLoading: boolean; isDetecting: boolean; currentDetection: DetectionResult | null; metrics: DetectionMetrics | null; error: string | null; // Actions initialize: (videoElement: HTMLVideoElement) => Promise; startContinuous: () => void; stopContinuous: () => void; triggerDetection: () => Promise; updateConfig: (config: Partial) => Promise; // Config config: DetectionConfig | null; } /** * React hook for shoe detection functionality */ export function useDetection(options: UseDetectionOptions = {}): UseDetectionReturn { const { modelVariant = 'standard', enableContinuous = true, enableTrigger = true, onDetection, onError } = options; // State const [isLoading, setIsLoading] = useState(false); const [isDetecting, setIsDetecting] = useState(false); const [currentDetection, setCurrentDetection] = useState(null); const [metrics, setMetrics] = useState(null); const [error, setError] = useState(null); const [config, setConfig] = useState(null); // Refs const detectionEngineRef = useRef(null); const videoElementRef = useRef(null); const initializationPromiseRef = useRef | null>(null); // Initialize detection engine const initialize = useCallback(async (videoElement: HTMLVideoElement): Promise => { console.log('🚀 useDetection.initialize called:', { videoElement: !!videoElement }); // Prevent multiple initializations if (initializationPromiseRef.current) { console.log('⚠️ Initialization already in progress, returning existing promise'); return initializationPromiseRef.current; } setIsLoading(true); setError(null); const initPromise = (async () => { try { console.log('🏗️ Creating detection engine...'); // Create detection engine const engine = new DetectionEngine(); detectionEngineRef.current = engine; videoElementRef.current = videoElement; // Set up event listeners engine.onDetection((detection) => { setCurrentDetection(detection); onDetection?.(detection); }); engine.onMetrics((newMetrics) => { setMetrics(newMetrics); }); // Initialize with progress tracking await engine.initialize(modelVariant, (progress) => { // You could add progress state here if needed console.log(`Model loading: ${progress.toFixed(1)}%`); }); // Get initial configuration const initialConfig = engine.getConfig(); setConfig(initialConfig); console.log('Detection hook initialized successfully'); } catch (err) { const error = err instanceof Error ? err : new Error('Unknown initialization error'); console.error('Detection initialization failed:', error); setError(error.message); onError?.(error); throw error; } finally { setIsLoading(false); } })(); initializationPromiseRef.current = initPromise; return initPromise; }, [modelVariant, onDetection, onError]); // Start continuous detection const startContinuous = useCallback(() => { console.log('🔄 useDetection.startContinuous called:', { hasEngine: !!detectionEngineRef.current, hasVideo: !!videoElementRef.current, enableContinuous }); if (!detectionEngineRef.current || !videoElementRef.current) { console.warn('Detection engine or video element not available'); return; } if (!enableContinuous) { console.warn('Continuous detection is disabled'); return; } try { console.log('🚀 Starting continuous detection...'); detectionEngineRef.current.startContinuousDetection(videoElementRef.current); setIsDetecting(true); setError(null); console.log('✅ Continuous detection started successfully'); } catch (err) { const error = err instanceof Error ? err : new Error('Failed to start continuous detection'); console.error('❌ Start continuous detection failed:', error); setError(error.message); onError?.(error); } }, [enableContinuous, onError]); // Stop continuous detection const stopContinuous = useCallback(() => { if (!detectionEngineRef.current) { return; } try { detectionEngineRef.current.stopContinuousDetection(); setIsDetecting(false); setCurrentDetection(null); } catch (err) { console.error('Stop continuous detection failed:', err); } }, []); // Trigger single detection const triggerDetection = useCallback(async (): Promise => { if (!detectionEngineRef.current || !videoElementRef.current) { throw new Error('Detection engine or video element not available'); } if (!enableTrigger) { throw new Error('Trigger detection is disabled'); } try { setError(null); const detection = await detectionEngineRef.current.triggerDetection(videoElementRef.current); // Update current detection state setCurrentDetection(detection); onDetection?.(detection); return detection; } catch (err) { const error = err instanceof Error ? err : new Error('Trigger detection failed'); console.error('Trigger detection failed:', error); setError(error.message); onError?.(error); throw error; } }, [enableTrigger, onDetection, onError]); // Update configuration const updateConfig = useCallback(async (newConfig: Partial): Promise => { if (!detectionEngineRef.current) { throw new Error('Detection engine not available'); } try { await detectionEngineRef.current.updateConfig(newConfig); const updatedConfig = detectionEngineRef.current.getConfig(); setConfig(updatedConfig); setError(null); } catch (err) { const error = err instanceof Error ? err : new Error('Failed to update configuration'); console.error('Update config failed:', error); setError(error.message); onError?.(error); throw error; } }, [onError]); // Cleanup on unmount useEffect(() => { return () => { if (detectionEngineRef.current) { detectionEngineRef.current.destroy(); detectionEngineRef.current = null; } initializationPromiseRef.current = null; videoElementRef.current = null; }; }, []); return { // State isLoading, isDetecting, currentDetection, metrics, error, // Actions initialize, startContinuous, stopContinuous, triggerDetection, updateConfig, // Config config }; } /** * Hook for detection metrics monitoring */ export function useDetectionMetrics(detectionEngine: DetectionEngine | null) { const [metrics, setMetrics] = useState(null); useEffect(() => { if (!detectionEngine) return; detectionEngine.onMetrics(setMetrics); // Get initial metrics const initialMetrics = detectionEngine.getMetrics(); setMetrics(initialMetrics); }, [detectionEngine]); return metrics; } /** * Hook for performance monitoring and adjustments */ export function usePerformanceOptimization(detectionEngine: DetectionEngine | null) { const [recommendations, setRecommendations] = useState([]); useEffect(() => { if (!detectionEngine) return; const interval = setInterval(() => { const metrics = detectionEngine.getMetrics(); const newRecommendations: string[] = []; if (metrics.fps < 15) { newRecommendations.push('Consider increasing frame skip or switching to a lighter model'); } if (metrics.inferenceTime > 100) { newRecommendations.push('Inference time is high, consider switching to quantized model'); } if (metrics.memoryUsage > 100) { newRecommendations.push('High memory usage detected'); } setRecommendations(newRecommendations); }, 5000); // Check every 5 seconds return () => clearInterval(interval); }, [detectionEngine]); return recommendations; }