import * as BABYLON from '@babylonjs/core/Legacy/legacy';
import '@babylonjs/loaders/glTF';
import { StreamController, loadWasmModule } from '@polygon-streaming/web-player-babylonjs';
import ammoWasmJsUrl from './lib/ammo.wasm.js?url';
import ammoWasmWasmUrl from './lib/ammo.wasm.wasm?url';
import ammoJsUrl from './lib/ammo.js?url';

function toRadians(degrees) {
  return BABYLON.Angle.FromDegrees(degrees).radians();
}

loadWasmModule('Ammo', ammoWasmJsUrl, ammoWasmWasmUrl, ammoJsUrl).then(ammoInstance => {
  // Setup
  const canvas = document.getElementById('application');
  const engine = new BABYLON.Engine(canvas, true);
  const scene = new BABYLON.Scene(engine);

  // Camera
  const cameraTarget = new BABYLON.Vector3(0, 2, 0);
  const camera = new BABYLON.ArcRotateCamera('Camera', toRadians(90), toRadians(80), 10, cameraTarget, scene);
  camera.minZ = 0.1;
  camera.maxZ = 1000;

  camera.attachControl(canvas, true);

  window.addEventListener('resize', () => {
    engine.resize();
  });

  // Ambient light
  const hemisphericLight = new BABYLON.HemisphericLight('Hemispheric light', new BABYLON.Vector3(0, 1, 0), scene);
  hemisphericLight.intensity = .5;

  // Directional light
  const directionalLight = new BABYLON.DirectionalLight('Directional light', new BABYLON.Vector3(-1, -1, -1), scene);
  directionalLight.intensity = 1;
  directionalLight.shadowEnabled = true;

  // Stream controller
  const streamController = new StreamController(camera, engine, scene, cameraTarget, { ammoInstance });

  // Streaming model
  const modelParent = new BABYLON.TransformNode('Model parent', scene);
  modelParent.position.set(0, 1, 0);
  streamController.addModel('https://stream.viverse.com/demos/jet-engine-11m/', modelParent);

  // Ground plane
  const planeSize = new BABYLON.Vector2(10, 10);
  const material = new BABYLON.ShaderMaterial('Grid', scene, {
    vertexSource: /*glsl*/`
      precision highp float;
      attribute vec3 position;
      uniform mat4 worldViewProjection;
      varying vec3 vPosition;

      void main() {
        vPosition = position;
        gl_Position = worldViewProjection * vec4(position, 1.0);
      }`,
    fragmentSource: /*glsl*/`
      precision highp float;
      varying vec3 vPosition;
      uniform float lineBrightness;
      uniform vec2 size;
      uniform float opacity;

      float line(float lineWidth, float numCells) {
        vec2 coord = vPosition.xy / size / vec2(1.0 / numCells);
        vec2 grid = abs(fract(coord - 0.5) - 0.5) / fwidth(coord * lineWidth);
        float line = min(grid.x, grid.y);
        return 1.0 - min(line, 1.0);
      }

      void main() {
        float color = line(1.0, 10.0) + line(0.5, 50.0);
        gl_FragColor = vec4(vec3(color * lineBrightness), opacity);
      }`
  }, {
    attributes: ['position', 'normal', 'uv'],
    uniforms: ['lineBrightness', 'size', 'opacity', 'worldViewProjection'],
    needAlphaBlending: true
  });
  material.setFloat('lineBrightness', 0.5);
  material.setVector2('size', planeSize);
  material.setFloat('opacity', 0.2);
  const plane = BABYLON.CreatePlane('ground plane', { width: planeSize.x, height: planeSize.y, sideOrientation: BABYLON.Mesh.DOUBLESIDE }, scene);
  plane.rotation.x = toRadians(-90);
  plane.material = material;

  // Skybox
  const environmentTexture = BABYLON.CubeTexture.CreateFromPrefilteredData('/assets/env-sky-cube.env', scene);
  scene.createDefaultSkybox(environmentTexture);

  // Render loop
  engine.runRenderLoop(function () {
    scene.render();
    streamController.update();
  });
});

