import * as THREE from 'three'

export class MotionBlurEffect {
  private scene: THREE.Scene
  private camera: THREE.PerspectiveCamera
  private renderer: THREE.WebGLRenderer
  private renderTarget1: THREE.WebGLRenderTarget
  private renderTarget2: THREE.WebGLRenderTarget
  private blurMaterial: THREE.ShaderMaterial
  private quad: THREE.Mesh
  private intensity: number = 0.5
  private accumulation: number = 0.8

  constructor(
    scene: THREE.Scene,
    camera: THREE.PerspectiveCamera,
    renderer: THREE.WebGLRenderer
  ) {
    this.scene = scene
    this.camera = camera
    this.renderer = renderer

    const size = renderer.getSize(new THREE.Vector2())
    
    // Create render targets for accumulation
    this.renderTarget1 = new THREE.WebGLRenderTarget(size.x, size.y, {
      minFilter: THREE.LinearFilter,
      magFilter: THREE.LinearFilter,
      format: THREE.RGBAFormat,
      type: THREE.FloatType
    })
    
    this.renderTarget2 = new THREE.WebGLRenderTarget(size.x, size.y, {
      minFilter: THREE.LinearFilter,
      magFilter: THREE.LinearFilter,
      format: THREE.RGBAFormat,
      type: THREE.FloatType
    })

    // Motion blur shader material
    this.blurMaterial = new THREE.ShaderMaterial({
      uniforms: {
        tCurrent: { value: null },
        tPrevious: { value: null },
        intensity: { value: this.intensity },
        accumulation: { value: this.accumulation }
      },
      vertexShader: `
        varying vec2 vUv;
        void main() {
          vUv = uv;
          gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
        }
      `,
      fragmentShader: `
        uniform sampler2D tCurrent;
        uniform sampler2D tPrevious;
        uniform float intensity;
        uniform float accumulation;
        varying vec2 vUv;
        
        void main() {
          vec4 current = texture2D(tCurrent, vUv);
          vec4 previous = texture2D(tPrevious, vUv);
          
          // Blend current frame with accumulated previous frames
          vec4 result = mix(current, previous, accumulation * intensity);
          
          gl_FragColor = result;
        }
      `
    })

    // Create fullscreen quad
    const geometry = new THREE.PlaneGeometry(2, 2)
    this.quad = new THREE.Mesh(geometry, this.blurMaterial)
  }

  public render(_deltaTime: number): THREE.WebGLRenderTarget {
    // Render current frame to renderTarget1
    this.renderer.setRenderTarget(this.renderTarget1)
    this.renderer.render(this.scene, this.camera)

    // Apply motion blur by blending with previous frame
    this.blurMaterial.uniforms.tCurrent.value = this.renderTarget1.texture
    this.blurMaterial.uniforms.tPrevious.value = this.renderTarget2.texture

    // Render blurred result to renderTarget2
    this.renderer.setRenderTarget(this.renderTarget2)
    this.renderer.render(this.quad, new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1))

    // Swap render targets for next frame
    const temp = this.renderTarget1
    this.renderTarget1 = this.renderTarget2
    this.renderTarget2 = temp

    return this.renderTarget1
  }

  public setIntensity(intensity: number): void {
    this.intensity = intensity
    this.blurMaterial.uniforms.intensity.value = intensity
  }

  public setAccumulation(accumulation: number): void {
    this.accumulation = accumulation
    this.blurMaterial.uniforms.accumulation.value = accumulation
  }

  public resize(width: number, height: number): void {
    this.renderTarget1.setSize(width, height)
    this.renderTarget2.setSize(width, height)
  }

  public dispose(): void {
    this.renderTarget1.dispose()
    this.renderTarget2.dispose()
    this.blurMaterial.dispose()
    this.quad.geometry.dispose()
  }
}
