// Store the current script reference before the async function runs
const currentScriptTag = document.currentScript;

(async function() {
  // Import PlayCanvas
  const pc = await import("https://cdn.skypack.dev/playcanvas@v1.68.0");
  window.pc = pc;
  
  // Find the script tag using a more reliable method
  let scriptTag = currentScriptTag;
  
  // Fallback method if currentScriptTag is null
  if (!scriptTag) {
    const scripts = document.getElementsByTagName('script');
    for (let i = 0; i < scripts.length; i++) {
      if (scripts[i].src.includes('index.js') && scripts[i].hasAttribute('data-config')) {
        scriptTag = scripts[i];
        break;
      }
    }
    
    // If still not found, try the last script on the page
    if (!scriptTag && scripts.length > 0) {
      scriptTag = scripts[scripts.length - 1];
    }
  }
  
  // Check if we found a script tag
  if (!scriptTag) {
    console.error("Could not find the script tag with data-config attribute.");
    return;
  }
  
  const configUrl = scriptTag.getAttribute("data-config");
  let config = {};
  if (configUrl) {
    try {
      const response = await fetch(configUrl);
      config = await response.json();
    } catch (error) {
      console.error("Error loading config file:", error);
      return;
    }
  } else {
    console.error("No config file provided. Please set a data-config attribute on the script tag.");
    return;
  }

  // Load the external CSS file if provided in the config.
  if (config.css_url) {
    const linkEl = document.createElement("link");
    linkEl.rel = "stylesheet";
    linkEl.href = config.css_url;
    document.head.appendChild(linkEl);
  }

  // --- Outer scope variables ---
  let cameraEntity = null;
  let app = null;
  let modelEntity = null;
  
  // Generate a unique identifier for this widget instance.
  const instanceId = Math.random().toString(36).substr(2, 8);
  
  // Read configuration values from the JSON file.
  const gifUrl = config.gif_url;
  const plyUrl = config.ply_url;
  
  // Camera constraint parameters
  const minZoom = parseFloat(config.minZoom || "1");
  const maxZoom = parseFloat(config.maxZoom || "20");
  const minAngle = parseFloat(config.minAngle || "-45");
  const maxAngle = parseFloat(config.maxAngle || "90");
  const minAzimuth = config.minAzimuth !== undefined ? parseFloat(config.minAzimuth) : -360;
  const maxAzimuth = config.maxAzimuth !== undefined ? parseFloat(config.maxAzimuth) : 360;
  
  // Model position, scale, and rotation parameters
  const modelX = config.modelX !== undefined ? parseFloat(config.modelX) : 0;
  const modelY = config.modelY !== undefined ? parseFloat(config.modelY) : 0;
  const modelZ = config.modelZ !== undefined ? parseFloat(config.modelZ) : 0;
  
  const modelScale = config.modelScale !== undefined ? parseFloat(config.modelScale) : 1;
  
  const modelRotationX = config.modelRotationX !== undefined ? parseFloat(config.modelRotationX) : 0;
  const modelRotationY = config.modelRotationY !== undefined ? parseFloat(config.modelRotationY) : 0;
  const modelRotationZ = config.modelRotationZ !== undefined ? parseFloat(config.modelRotationZ) : 0;
  
  // Direct camera coordinates
  const cameraX = config.cameraX !== undefined ? parseFloat(config.cameraX) : 0;
  const cameraY = config.cameraY !== undefined ? parseFloat(config.cameraY) : 2;
  const cameraZ = config.cameraZ !== undefined ? parseFloat(config.cameraZ) : 5;
  
  // Camera coordinates for mobile devices
  const cameraXPhone = config.cameraXPhone !== undefined ? parseFloat(config.cameraXPhone) : cameraX;
  const cameraYPhone = config.cameraYPhone !== undefined ? parseFloat(config.cameraYPhone) : cameraY;
  const cameraZPhone = config.cameraZPhone !== undefined ? parseFloat(config.cameraZPhone) : cameraZ * 1.5;

  // Detect if the device is iOS.
  const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
  // Also detect Android devices.
  const isMobile = isIOS || /Android/i.test(navigator.userAgent);
  
  // Choose the appropriate coordinates based on device type
  const chosenCameraX = isMobile ? cameraXPhone : cameraX;
  const chosenCameraY = isMobile ? cameraYPhone : cameraY;
  const chosenCameraZ = isMobile ? cameraZPhone : cameraZ;
  
  // Determine the aspect ratio.
  let aspectPercent = "100%";
  if (config.aspect) {
    if (config.aspect.indexOf(":") !== -1) {
      const parts = config.aspect.split(":");
      const w = parseFloat(parts[0]);
      const h = parseFloat(parts[1]);
      if (!isNaN(w) && !isNaN(h) && w > 0) {
        aspectPercent = (h / w * 100) + "%";
      }
    } else {
      const aspectValue = parseFloat(config.aspect);
      if (!isNaN(aspectValue) && aspectValue > 0) {
        aspectPercent = (100 / aspectValue) + "%";
      }
    }
  } else {
    const parentContainer = scriptTag.parentNode;
    const containerWidth = parentContainer.offsetWidth;
    const containerHeight = parentContainer.offsetHeight;
    if (containerWidth > 0 && containerHeight > 0) {
      aspectPercent = (containerHeight / containerWidth * 100) + "%";
    }
  }
  
  // Create the widget container.
  const widgetContainer = document.createElement('div');
  widgetContainer.id = 'ply-widget-container-' + instanceId;
  widgetContainer.classList.add('ply-widget-container');
  // Add a mobile class if on a phone.
  if (isMobile) {
    widgetContainer.classList.add('mobile');
  }
  // Set inline style for aspect ratio.
  widgetContainer.style.height = "0";
  widgetContainer.style.paddingBottom = aspectPercent;

  widgetContainer.innerHTML = `
    <!-- GIF Preview Container -->
    <div id="gif-preview-container-${instanceId}" class="gif-preview-container">
      <img id="preview-image-${instanceId}" alt="Preview" crossorigin="anonymous">
    </div>
    <!-- Viewer Container -->
    <div id="viewer-container-${instanceId}" class="viewer-container">
      <canvas id="canvas-${instanceId}" class="ply-canvas"></canvas>
      <div id="progress-dialog-${instanceId}" class="progress-dialog">
        <progress id="progress-indicator-${instanceId}" max="100" value="0"></progress>
      </div>
      <button id="close-btn-${instanceId}" class="widget-button close-btn">X</button>
      <button id="fullscreen-toggle-${instanceId}" class="widget-button fullscreen-toggle">⇱</button>
      <button id="help-toggle-${instanceId}" class="widget-button help-toggle">?</button>
      <button id="reset-camera-btn-${instanceId}" class="widget-button reset-camera-btn">
        <span class="reset-icon">⟲</span>
      </button>
      <div id="menu-content-${instanceId}" class="menu-content"></div>
    </div>
  `;
  scriptTag.parentNode.appendChild(widgetContainer);
  
  // Grab element references.
  const gifPreview = document.getElementById('gif-preview-container-' + instanceId);
  const viewerContainer = document.getElementById('viewer-container-' + instanceId);
  const previewImage = document.getElementById('preview-image-' + instanceId);
  const closeBtn = document.getElementById('close-btn-' + instanceId);
  const fullscreenToggle = document.getElementById('fullscreen-toggle-' + instanceId);
  const helpToggle = document.getElementById('help-toggle-' + instanceId);
  const resetCameraBtn = document.getElementById('reset-camera-btn-' + instanceId);
  const menuContent = document.getElementById('menu-content-' + instanceId);
  const canvas = document.getElementById('canvas-' + instanceId);
  const progressDialog = document.getElementById('progress-dialog-' + instanceId);
  const progressIndicator = document.getElementById('progress-indicator-' + instanceId);
  
  // Flag to track if mouse is over the viewer
  let isMouseOverViewer = false;
  
  // Add mouse hover tracking for the viewer container
  viewerContainer.addEventListener('mouseenter', function() {
    isMouseOverViewer = true;
  });
  
  viewerContainer.addEventListener('mouseleave', function() {
    isMouseOverViewer = false;
  });
  
  // Set help instructions based on device type.
  if (isMobile) {
    menuContent.innerHTML = `
      - Pour vous déplacer, glissez deux doigts sur l'écran.<br>
      - Pour orbiter, utilisez un doigt.<br>
      - Pour zoomer, pincez avec deux doigts.
    `;
  } else {
    menuContent.innerHTML = `
      - orbitez avec le clic droit<br>
      - zoomez avec la molette<br>
      - déplacez vous avec le clic gauche
    `;
  }
  
  // If a gif_url is provided, set the preview image.
  // Otherwise, hide the preview container, show the viewer immediately,
  // and hide the "close" button since there's no preview to return to.
  if (gifUrl) {
    previewImage.src = gifUrl;
  } else {
    gifPreview.style.display = 'none';
    viewerContainer.style.display = 'block';
    closeBtn.style.display = 'none';
    initializeViewer();
  }
  
  // --- Button Event Handlers ---
  if (gifUrl) {
    gifPreview.addEventListener('click', function() {
      gifPreview.style.display = 'none';
      viewerContainer.style.display = 'block';
      initializeViewer();
    });
  }
  
  closeBtn.addEventListener('click', function() {
    if (document.fullscreenElement === widgetContainer) {
      if (document.exitFullscreen) {
        document.exitFullscreen();
      }
    }
    if (widgetContainer.classList.contains('fake-fullscreen')) {
      widgetContainer.classList.remove('fake-fullscreen');
      fullscreenToggle.textContent = '⇱';
      resetCamera();
    }
    viewerContainer.style.display = 'none';
    gifPreview.style.display = 'block';
  });
  
  fullscreenToggle.addEventListener('click', function() {
    if (isIOS) {
      if (!widgetContainer.classList.contains('fake-fullscreen')) {
        widgetContainer.classList.add('fake-fullscreen');
      } else {
        widgetContainer.classList.remove('fake-fullscreen');
        resetCamera();
      }
      fullscreenToggle.textContent = widgetContainer.classList.contains('fake-fullscreen') ? '⇲' : '⇱';
    } else {
      if (!document.fullscreenElement) {
        if (widgetContainer.requestFullscreen) {
          widgetContainer.requestFullscreen();
        } else if (widgetContainer.webkitRequestFullscreen) {
          widgetContainer.webkitRequestFullscreen();
        } else if (widgetContainer.mozRequestFullScreen) {
          widgetContainer.mozRequestFullScreen();
        } else if (widgetContainer.msRequestFullscreen) {
          widgetContainer.msRequestFullscreen();
        }
      } else {
        if (document.exitFullscreen) {
          document.exitFullscreen();
        }
      }
    }
  });
  
  // Listen for native fullscreen changes.
  document.addEventListener('fullscreenchange', function() {
    if (document.fullscreenElement === widgetContainer) {
      fullscreenToggle.textContent = '⇲';
      widgetContainer.style.height = '100%';
      widgetContainer.style.paddingBottom = '0';
      resetCamera();
    } else {
      fullscreenToggle.textContent = '⇱';
      widgetContainer.style.height = '0';
      widgetContainer.style.paddingBottom = aspectPercent;
      resetCamera();
    }
  });
  
  helpToggle.addEventListener('click', function(e) {
    e.stopPropagation();
    menuContent.style.display = (menuContent.style.display === 'block') ? 'none' : 'block';
  });
  
  // --- Camera Reset Function ---
  function resetCamera() {
    if (!cameraEntity || !modelEntity || !app) return;
    
    try {
      // Get the orbit camera script
      const orbitCam = cameraEntity.script.orbitCamera;
      if (!orbitCam) return;
      
      // Store model position
      const modelPos = modelEntity.getPosition();
      
      // 1. Create a temporary entity to help calculate new values
      const tempEntity = new pc.Entity();
      tempEntity.setPosition(chosenCameraX, chosenCameraY, chosenCameraZ);
      tempEntity.lookAt(modelPos);
      
      // 2. Calculate the distance between camera and model
      const distance = new pc.Vec3().sub2(
        new pc.Vec3(chosenCameraX, chosenCameraY, chosenCameraZ),
        modelPos
      ).length();
      
      // 3. Set camera position
      cameraEntity.setPosition(chosenCameraX, chosenCameraY, chosenCameraZ);
      cameraEntity.lookAt(modelPos);
      
      // 4. Update the orbit camera's pivot point
      orbitCam.pivotPoint = new pc.Vec3(modelPos.x, modelPos.y, modelPos.z);
      
      // 5. Set the distance
      orbitCam._targetDistance = distance;
      orbitCam._distance = distance;
      
      // 6. Calculate and set yaw and pitch from the camera's rotation
      const rotation = tempEntity.getRotation();
      const tempForward = new pc.Vec3();
      rotation.transformVector(pc.Vec3.FORWARD, tempForward);
      
      const yaw = Math.atan2(-tempForward.x, -tempForward.z) * pc.math.RAD_TO_DEG;
      
      const yawQuat = new pc.Quat().setFromEulerAngles(0, -yaw, 0);
      const rotWithoutYaw = new pc.Quat().mul2(yawQuat, rotation);
      const forwardWithoutYaw = new pc.Vec3();
      rotWithoutYaw.transformVector(pc.Vec3.FORWARD, forwardWithoutYaw);
      const pitch = Math.atan2(forwardWithoutYaw.y, -forwardWithoutYaw.z) * pc.math.RAD_TO_DEG;
      
      // Set yaw and pitch directly on internal variables
      orbitCam._targetYaw = yaw;
      orbitCam._yaw = yaw;
      orbitCam._targetPitch = pitch;
      orbitCam._pitch = pitch;
      
      // Force update
      if (typeof orbitCam._updatePosition === 'function') {
        orbitCam._updatePosition();
      }
      
      // Clean up
      tempEntity.destroy();
      
    } catch (error) {
      console.error("Error resetting camera:", error);
    }
  }
  
  resetCameraBtn.addEventListener('click', function() {
    console.log("Reset camera button clicked.");
    resetCamera();
  });
  
  document.addEventListener('keydown', function(e) {
    if (e.key === 'Escape' || e.key === 'Esc') {
      let wasFullscreen = false;
      if (document.fullscreenElement === widgetContainer) {
        wasFullscreen = true;
        if (document.exitFullscreen) {
          document.exitFullscreen();
        }
      }
      if (widgetContainer.classList.contains('fake-fullscreen')) {
        wasFullscreen = true;
        widgetContainer.classList.remove('fake-fullscreen');
        fullscreenToggle.textContent = '⇱';
      }
      if (wasFullscreen) {
        resetCamera();
      }
    }
  });
  
  // --- Prevent app from hijacking all wheel events ---
  const handleWheel = function(event) {
    // Check if mouse is over the viewer
    if (!isMouseOverViewer) {
      // Allow normal page scrolling
      return true;
    }
    
    // Otherwise apply zooming, but prevent default only for viewer area
    event.stopPropagation();
    
    if (cameraEntity && cameraEntity.script && cameraEntity.script.orbitCamera) {
      const camera = cameraEntity.camera;
      const orbitCamera = cameraEntity.script.orbitCamera;
      const sensitivity = cameraEntity.script.orbitCameraInputMouse ? 
                          cameraEntity.script.orbitCameraInputMouse.distanceSensitivity || 0.4 : 0.4;
      
      if (camera.projection === pc.PROJECTION_PERSPECTIVE) {
        orbitCamera.distance -= event.deltaY * 0.01 * sensitivity * (orbitCamera.distance * 0.1);
      } else {
        orbitCamera.orthoHeight -= event.deltaY * 0.01 * sensitivity * (orbitCamera.orthoHeight * 0.1);
      }
      
      event.preventDefault();
    }
  };
  
  // --- Listen for wheel events at the viewer level, not document level ---
  viewerContainer.addEventListener('wheel', handleWheel, { passive: false });
  canvas.addEventListener('wheel', handleWheel, { passive: false });
  
  // --- Initialize the 3D PLY Viewer using PlayCanvas ---
  async function initializeViewer() {
    progressDialog.style.display = 'block';
    
    // Initialize PlayCanvas
    const deviceType = "webgl2";
    const gfxOptions = {
      deviceTypes: [deviceType],
      glslangUrl: `https://playcanvas.vercel.app/static/lib/glslang/glslang.js`,
      twgslUrl: `https://playcanvas.vercel.app/static/lib/twgsl/twgsl.js`,
      antialias: false
    };
    
    try {
      // Create graphics device
      const device = await pc.createGraphicsDevice(canvas, gfxOptions);
      device.maxPixelRatio = Math.min(window.devicePixelRatio, 2);
      
      // Create app
      const createOptions = new pc.AppOptions();
      createOptions.graphicsDevice = device;
      createOptions.mouse = new pc.Mouse(canvas); // Use canvas, not document.body
      createOptions.touch = new pc.TouchDevice(canvas); // Use canvas, not document.body
      createOptions.componentSystems = [
        pc.RenderComponentSystem,
        pc.CameraComponentSystem,
        pc.LightComponentSystem,
        pc.ScriptComponentSystem,
        pc.GSplatComponentSystem
      ];
      createOptions.resourceHandlers = [
        pc.TextureHandler,
        pc.ContainerHandler,
        pc.ScriptHandler,
        pc.GSplatHandler
      ];
      
      app = new pc.AppBase(canvas);
      app.init(createOptions);
      
      // Set canvas fill mode to match the container
      app.setCanvasFillMode(pc.FILLMODE_NONE);
      app.setCanvasResolution(pc.RESOLUTION_AUTO);
      
      // Set scene options
      app.scene.exposure = 0.8;
      app.scene.toneMapping = pc.TONEMAP_ACES;
      
      // Handle window resizing
      const resize = () => {
        if (app) {
          app.resizeCanvas(canvas.clientWidth, canvas.clientHeight);
        }
      };
      window.addEventListener('resize', resize);
      app.on('destroy', () => window.removeEventListener('resize', resize));
      
      // Load required assets
      const assets = {
        model: new pc.Asset('gsplat', 'gsplat', { url: plyUrl }),
        orbit: new pc.Asset('script', 'script', { url: `https://bilca-visionneur-play-canva-2.static.hf.space/orbit-camera.js` })
      };
      
      // Create asset loader with progress tracking
      const assetListLoader = new pc.AssetListLoader(Object.values(assets), app.assets);
      
      // Handle asset loading progress
      let lastProgress = 0;
      assets.model.on('load', (asset) => {
        progressDialog.style.display = 'none';
      });
      
      assets.model.on('error', (err) => {
        console.error("Error loading PLY file:", err);
        progressDialog.innerHTML = `<p style="color: red">Error loading model: ${err}</p>`;
      });
      
      // Set up progress monitoring
      const checkProgress = () => {
        if (app && assets.model.resource) {
          progressIndicator.value = 100;
          clearInterval(progressChecker);
          progressDialog.style.display = 'none';
        } else if (assets.model.loading) {
          // Increment progress for visual feedback
          lastProgress += 2;
          if (lastProgress > 90) lastProgress = 90; // Cap at 90% until fully loaded
          progressIndicator.value = lastProgress;
        }
      };
      
      const progressChecker = setInterval(checkProgress, 100);
      
      // Load assets and set up scene
      assetListLoader.load(() => {
        app.start();
        
        // Create model entity
        modelEntity = new pc.Entity('model');
        modelEntity.addComponent('gsplat', {
          asset: assets.model
        });
        
        // Position the model using JSON parameters
        modelEntity.setLocalPosition(modelX, modelY, modelZ);
        modelEntity.setLocalEulerAngles(modelRotationX, modelRotationY, modelRotationZ);
        modelEntity.setLocalScale(modelScale, modelScale, modelScale);
        
        app.root.addChild(modelEntity);
        
        // Create camera entity
        cameraEntity = new pc.Entity('camera');
        cameraEntity.addComponent('camera', {
          clearColor: new pc.Color(
            config.canvas_background ? parseInt(config.canvas_background.substr(1, 2), 16) / 255 : 0,
            config.canvas_background ? parseInt(config.canvas_background.substr(3, 2), 16) / 255 : 0,
            config.canvas_background ? parseInt(config.canvas_background.substr(5, 2), 16) / 255 : 0
          ),
          toneMapping: pc.TONEMAP_ACES
        });
        
        // Set camera position directly using X, Y, Z coordinates from config
        cameraEntity.setPosition(chosenCameraX, chosenCameraY, chosenCameraZ);
        cameraEntity.lookAt(modelEntity.getPosition());
        
        // Add orbit camera script for interactive navigation
        cameraEntity.addComponent('script');
        cameraEntity.script.create('orbitCamera', {
          attributes: {
            inertiaFactor: 0.2,
            focusEntity: modelEntity,
            distanceMax: maxZoom,
            distanceMin: minZoom,
            pitchAngleMax: maxAngle,
            pitchAngleMin: minAngle,
            yawAngleMax: maxAzimuth,
            yawAngleMin: minAzimuth,
            frameOnStart: false // Don't auto-frame since we're setting position directly
          }
        });
        
        // Create input controllers but don't add mouse wheel handling - we handle that separately
        cameraEntity.script.create('orbitCameraInputMouse', {
          attributes: {
            orbitSensitivity: isMobile ? 0.6 : 0.3,
            distanceSensitivity: isMobile ? 0.5 : 0.4
          }
        });
        
        // Disable wheel event in the orbit camera input
        if (cameraEntity.script.orbitCameraInputMouse) {
          // Override mouse wheel to do nothing - we handle wheel events separately
          cameraEntity.script.orbitCameraInputMouse.onMouseWheel = function() {};
        }
        
        // Add touch input controller
        cameraEntity.script.create('orbitCameraInputTouch', {
          attributes: {
            orbitSensitivity: 0.6,
            distanceSensitivity: 0.5
          }
        });
        
        // Initialize the orbit controller
        setTimeout(() => {
          if (cameraEntity.script.orbitCamera) {
            // Calculate distance from camera to model
            const modelPos = modelEntity.getPosition();
            const camPos = cameraEntity.getPosition();
            const distanceVec = new pc.Vec3();
            distanceVec.sub2(camPos, modelPos);
            const distance = distanceVec.length();
            
            // Set up the orbit controller
            cameraEntity.script.orbitCamera.pivotPoint.copy(modelPos);
            cameraEntity.script.orbitCamera.distance = distance;
            cameraEntity.script.orbitCamera._removeInertia();
          }
        }, 100);
        
        app.root.addChild(cameraEntity);
        
        // Initial resize to match container
        resize();
        
        // Hide progress dialog when everything is set up
        progressDialog.style.display = 'none';
      });
      
    } catch (error) {
      console.error("Error initializing PlayCanvas viewer:", error);
      progressDialog.innerHTML = `<p style="color: red">Error loading viewer: ${error.message}</p>`;
    }
  }
})();