(async function() {
  // Retrieve the current script tag and load the JSON configuration file from the data-config attribute.
  const scriptTag = document.currentScript;
  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 for camera state ---
  let cameraInstance = null;
  let controlsInstance = null;
  let initialCameraPosition = null;
  let initialCameraRotation = null;
  let SPLAT = null; // We'll save the imported SPLAT module here.
  
  // 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;
  const minZoom = parseFloat(config.minZoom || "0");
  const maxZoom = parseFloat(config.maxZoom || "20");
  const minAngle = parseFloat(config.minAngle || "0");
  const maxAngle = parseFloat(config.maxAngle || "360");
  const minAzimuth = config.minAzimuth !== undefined ? parseFloat(config.minAzimuth) : -Infinity;
  const maxAzimuth = config.maxAzimuth !== undefined ? parseFloat(config.maxAzimuth) : Infinity;
  
  // Read initial orbit parameters for desktop.
  const initAlphaDesktop = config.initAlpha !== undefined ? parseFloat(config.initAlpha) : 0.5;
  const initBetaDesktop  = config.initBeta  !== undefined ? parseFloat(config.initBeta)  : 0.5;
  const initRadiusDesktop = config.initRadius !== undefined ? parseFloat(config.initRadius) : 5;
  // Read initial orbit parameters for phone.
  const initAlphaPhone = config.initAlphaPhone !== undefined ? parseFloat(config.initAlphaPhone) : initAlphaDesktop;
  const initBetaPhone  = config.initBetaPhone  !== undefined ? parseFloat(config.initBetaPhone)  : initBetaDesktop;
  const initRadiusPhone = config.initRadiusPhone !== undefined ? parseFloat(config.initRadiusPhone) : initRadiusDesktop;

  // 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 initial orbit values based on device type.
  const chosenInitAlpha = isMobile ? initAlphaPhone : initAlphaDesktop;
  const chosenInitBeta  = isMobile ? initBetaPhone  : initBetaDesktop;
  const chosenInitRadius = isMobile ? initRadiusPhone : initRadiusDesktop;
  
  // 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);
  
  // 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() {
    console.log("Resetting camera to initial position.");
    if (cameraInstance && initialCameraPosition && initialCameraRotation) {
      cameraInstance.position = initialCameraPosition.clone();
      cameraInstance.rotation = initialCameraRotation.clone();
      if (typeof cameraInstance.update === 'function') {
        cameraInstance.update();
      }
      if (controlsInstance && typeof controlsInstance.dispose === 'function') {
        controlsInstance.dispose();
      }
      controlsInstance = new SPLAT.OrbitControls(
        cameraInstance,
        canvas,
        0.5,
        0.5,
        5,
        true,
        new SPLAT.Vector3(),
        chosenInitAlpha,
        chosenInitBeta,
        chosenInitRadius
      );
      controlsInstance.maxZoom = maxZoom;
      controlsInstance.minZoom = minZoom;
      controlsInstance.minAngle = minAngle;
      controlsInstance.maxAngle = maxAngle;
      controlsInstance.minAzimuth = minAzimuth;
      controlsInstance.maxAzimuth = maxAzimuth;
      controlsInstance.panSpeed = isMobile ? 0.5 : 1.2;
      controlsInstance.update();
    }
  }
  
  resetCameraBtn.addEventListener('click', async 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();
      }
    }
  });
  
  // --- Initialize the 3D PLY Viewer ---
  async function initializeViewer() {
    SPLAT = await import("https://bilca-gsplat-library.static.hf.space/dist/index.js");
    progressDialog.style.display = 'block';
    const renderer = new SPLAT.WebGLRenderer(canvas);
    const scene = new SPLAT.Scene();
    
    const camera = new SPLAT.Camera();
    
    controlsInstance = new SPLAT.OrbitControls(
      camera,
      canvas,
      0.5,
      0.5,
      5,
      true,
      new SPLAT.Vector3(),
      chosenInitAlpha,
      chosenInitBeta,
      chosenInitRadius
    );
  
    cameraInstance = camera;
    initialCameraPosition = camera.position.clone();
    initialCameraRotation = camera.rotation.clone();
  
    canvas.style.background = config.canvas_background || "#FEFEFD";
    
    controlsInstance.maxZoom = maxZoom;
    controlsInstance.minZoom = minZoom;
    controlsInstance.minAngle = minAngle;
    controlsInstance.maxAngle = maxAngle;
    controlsInstance.minAzimuth = minAzimuth;
    controlsInstance.maxAzimuth = maxAzimuth;
    controlsInstance.panSpeed = isMobile ? 0.5 : 1.2;
  
    controlsInstance.update();
  
    try {
      await SPLAT.PLYLoader.LoadAsync(
        plyUrl,
        scene,
        (progress) => {
          progressIndicator.value = progress * 100;
        }
      );
      progressDialog.style.display = 'none';
    } catch (error) {
      console.error("Error loading PLY file:", error);
      progressDialog.innerHTML = `<p style="color: red">Error loading model: ${error.message}</p>`;
    }
  
    const frame = () => {
      controlsInstance.update();
      renderer.render(scene, camera);
      requestAnimationFrame(frame);
    };
  
    const handleResize = () => {
      renderer.setSize(canvas.clientWidth, canvas.clientHeight);
    };
  
    handleResize();
    window.addEventListener("resize", handleResize);
    requestAnimationFrame(frame);
  }
})();