/**
 * zoom-controller-enhanced.ts — Advanced zoom controller with smooth transitions
 * 
 * Provides smooth zoom animations, adaptive quality during zoom,
 * and intelligent zoom level management.
 */

interface EnhancedZoomControllerOptions {
  animate?: boolean;
  qualityOverride?: number | null;
}

class EnhancedZoomController {
  private container: HTMLElement;
  private pageManager: any; // Replace with proper type when available
  private onScaleChange: (scale: number, visibleSet: Set<number>, bufferSet: Set<number>) => void;
  private getVisibilityState: () => { visibleSet: Set<number>, bufferSet: Set<number> };
  private zoomLabel: HTMLElement | null;
  
  // Zoom state
  private scale: number;
  private targetScale: number;
  private minScale: number;
  private maxScale: number;
  private zoomAnimation: number | null;
  private isZooming: boolean;
  private zoomQuality: number;
  
  constructor(container: HTMLElement, pageManager: any, onScaleChange: (scale: number, visibleSet: Set<number>, bufferSet: Set<number>) => void, getVisibilityState: () => { visibleSet: Set<number>, bufferSet: Set<number> }, zoomLabel: HTMLElement | null) {
    this.container = container;
    this.pageManager = pageManager;
    this.onScaleChange = onScaleChange;
    this.getVisibilityState = getVisibilityState;
    this.zoomLabel = zoomLabel;
    
    // Zoom state
    this.scale = 1.0;
    this.targetScale = 1.0;
    this.minScale = 0.1;
    this.maxScale = 5.0;
    this.zoomAnimation = null;
    this.isZooming = false;
    this.zoomQuality = 1.0;
    
    // Setup event listeners
    this.setupEventListeners();
    
    // Update zoom label
    this.updateZoomLabel();
  }

  private setupEventListeners(): void {
    // Smooth zoom with animation
    this.container.addEventListener('wheel', (event) => {
      if (event.ctrlKey || event.metaKey) {
        this.handleZoomWheel(event);
        event.preventDefault();
      }
    }, { passive: false });
    
    // Double-click for zoom to fit
    this.container.addEventListener('dblclick', (event) => {
      this.zoomToFit();
    });
  }

  private handleZoomWheel(event: WheelEvent): void {
    // Calculate zoom factor based on wheel delta
    const zoomFactor = event.deltaY > 0 ? 0.9 : 1.1;
    const newScale = this.scale * zoomFactor;
    
    // Apply constraints
    const constrainedScale = Math.max(this.minScale, 
                                     Math.min(this.maxScale, newScale));
    
    if (Math.abs(constrainedScale - this.scale) > 0.01) {
      this.applyScale(constrainedScale);
    }
  }

  public applyScale(newScale: number, options: EnhancedZoomControllerOptions = {}): void {
    const { animate = true, qualityOverride = null } = options;
    
    // Constrain scale
    this.targetScale = Math.max(this.minScale, 
                               Math.min(this.maxScale, newScale));
    
    if (Math.abs(this.targetScale - this.scale) < 0.01) return;
    
    if (animate) {
      this.startZoomAnimation(qualityOverride);
    } else {
      this.scale = this.targetScale;
      const { visibleSet, bufferSet } = this.getVisibilityState();
      this.onScaleChange(this.scale, visibleSet, bufferSet);
      this.updateZoomLabel();
    }
  }

  private startZoomAnimation(qualityOverride: number | null = null): void {
    if (this.zoomAnimation) {
      cancelAnimationFrame(this.zoomAnimation);
    }
    
    this.isZooming = true;
    const startScale = this.scale;
    const endScale = this.targetScale;
    const startTime = performance.now();
    const duration = 150; // 150ms for smooth zoom
    
    // Set lower quality during zoom for performance
    this.zoomQuality = qualityOverride !== null ? qualityOverride : 0.8;
    
    const animate = (currentTime: number) => {
      const elapsed = currentTime - startTime;
      const progress = Math.min(elapsed / duration, 1);
      
      // Ease-in-out animation for natural feel
      const easedProgress = this.easeInOutCubic(progress);
      this.scale = startScale + (endScale - startScale) * easedProgress;
      
      // Apply the scale
      this.applyScaleToContainer();
      
      // Notify about scale change with current quality
      const { visibleSet, bufferSet } = this.getVisibilityState();
      this.onScaleChange(this.scale, visibleSet, bufferSet);
      
      if (progress < 1) {
        this.zoomAnimation = requestAnimationFrame(animate);
      } else {
        this.isZooming = false;
        this.scale = endScale;
        this.zoomQuality = 1.0; // Restore full quality
        
        // Final high-quality render
        const { visibleSet, bufferSet } = this.getVisibilityState();
        this.onScaleChange(this.scale, visibleSet, bufferSet);
        
        this.updateZoomLabel();
        this.zoomAnimation = null;
      }
    };
    
    this.zoomAnimation = requestAnimationFrame(animate);
  }

  private applyScaleToContainer(): void {
    // Apply CSS transform for smooth zooming
    this.container.style.transform = `scale(${this.scale})`;
    
    // Also update the page manager
    if (this.pageManager.setScale) {
      this.pageManager.setScale(this.scale);
    }
  }

  private easeInOutCubic(t: number): number {
    return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
  }

  private zoomToFit(): void {
    if (!this.pageManager || !this.pageManager.viewports) return;
    
    const firstViewport = this.pageManager.viewports[0];
    if (!firstViewport) return;
    
    const containerWidth = this.container.clientWidth;
    const containerHeight = this.container.clientHeight;
    
    // Calculate fit scale
    const scaleW = (containerWidth - 40) / firstViewport.width;
    const scaleH = (containerHeight - 40) / firstViewport.height;
    const fitScale = Math.min(scaleW, scaleH);
    
    this.applyScale(Math.max(this.minScale, Math.min(this.maxScale, fitScale)));
  }

  public zoomIn(): void {
    this.applyScale(this.scale * 1.25);
  }

  public zoomOut(): void {
    this.applyScale(this.scale * 0.8);
  }

  public zoomToActualSize(): void {
    this.applyScale(1.0);
  }

  public getCurrentScale(): number {
    return this.scale;
  }

  public getCurrentZoomQuality(): number {
    return this.zoomQuality;
  }

  public isZoomingInProgress(): boolean {
    return this.isZooming;
  }

  private updateZoomLabel(): void {
    if (this.zoomLabel) {
      this.zoomLabel.textContent = `${Math.round(this.scale * 100)}%`;
    }
  }

  public cleanup(): void {
    if (this.zoomAnimation) {
      cancelAnimationFrame(this.zoomAnimation);
    }
  }

  public getZoomStats(): {
    currentScale: number;
    targetScale: number;
    isZooming: boolean;
    zoomQuality: number;
  } {
    return {
      currentScale: this.scale,
      targetScale: this.targetScale,
      isZooming: this.isZooming,
      zoomQuality: this.zoomQuality
    };
  }
}

// Export for ES6 modules
export { EnhancedZoomController };