import * as Cesium from "cesium";
import Sandcastle from "Sandcastle";

const viewer = new Cesium.Viewer("cesiumContainer");
const scene = viewer.scene;
const clock = viewer.clock;
let referenceFramePrimitive;

function flyToSanDiego() {
  Sandcastle.declare(flyToSanDiego);
  viewer.camera.flyTo({
    destination: Cesium.Cartesian3.fromDegrees(-117.16, 32.71, 15000.0),
  });
}

function flyToHeadingPitchRoll() {
  Sandcastle.declare(flyToHeadingPitchRoll);
  viewer.camera.flyTo({
    destination: Cesium.Cartesian3.fromDegrees(-122.22, 46.12, 5000.0),
    orientation: {
      heading: Cesium.Math.toRadians(20.0),
      pitch: Cesium.Math.toRadians(-35.0),
      roll: 0.0,
    },
  });
}

function flyToLocation() {
  Sandcastle.declare(flyToLocation);

  // Create callback for browser's geolocation
  function fly(position) {
    viewer.camera.flyTo({
      destination: Cesium.Cartesian3.fromDegrees(
        position.coords.longitude,
        position.coords.latitude,
        1000.0,
      ),
    });
  }

  // Ask browser for location, and fly there.
  navigator.geolocation.getCurrentPosition(fly);
}

function viewRectangle() {
  Sandcastle.declare(viewRectangle);

  const west = -77.0;
  const south = 38.0;
  const east = -72.0;
  const north = 42.0;

  const rectangle = Cesium.Rectangle.fromDegrees(west, south, east, north);
  viewer.camera.setView({
    destination: rectangle,
  });

  // Show the rectangle.  Not required; just for show.
  viewer.entities.add({
    rectangle: {
      coordinates: rectangle,
      fill: false,
      outline: true,
      outlineColor: Cesium.Color.WHITE,
    },
  });
}

function flyToRectangle() {
  Sandcastle.declare(flyToRectangle);

  const west = -90.0;
  const south = 38.0;
  const east = -87.0;
  const north = 40.0;
  const rectangle = Cesium.Rectangle.fromDegrees(west, south, east, north);

  viewer.camera.flyTo({
    destination: rectangle,
  });

  // Show the rectangle.  Not required; just for show.
  viewer.entities.add({
    rectangle: {
      coordinates: rectangle,
      fill: false,
      outline: true,
      outlineColor: Cesium.Color.WHITE,
    },
  });
}

function setReferenceFrame() {
  Sandcastle.declare(setReferenceFrame);

  const center = Cesium.Cartesian3.fromDegrees(-75.59777, 40.03883);
  const transform = Cesium.Transforms.eastNorthUpToFixedFrame(center);

  // View in east-north-up frame
  const camera = viewer.camera;
  camera.constrainedAxis = Cesium.Cartesian3.UNIT_Z;
  camera.lookAtTransform(
    transform,
    new Cesium.Cartesian3(-120000.0, -120000.0, 120000.0),
  );

  // Show reference frame.  Not required.
  referenceFramePrimitive = scene.primitives.add(
    new Cesium.DebugModelMatrixPrimitive({
      modelMatrix: transform,
      length: 100000.0,
    }),
  );
}

function setHeadingPitchRoll() {
  Sandcastle.declare(setHeadingPitchRoll);

  const camera = viewer.camera;
  camera.setView({
    destination: Cesium.Cartesian3.fromDegrees(-75.5847, 40.0397, 1000.0),
    orientation: {
      heading: -Cesium.Math.PI_OVER_TWO,
      pitch: -Cesium.Math.PI_OVER_FOUR,
      roll: 0.0,
    },
  });
}

function icrf(scene, time) {
  if (scene.mode !== Cesium.SceneMode.SCENE3D) {
    return;
  }

  const icrfToFixed = Cesium.Transforms.computeIcrfToFixedMatrix(time);
  if (Cesium.defined(icrfToFixed)) {
    const camera = viewer.camera;
    const offset = Cesium.Cartesian3.clone(camera.position);
    const transform = Cesium.Matrix4.fromRotationTranslation(icrfToFixed);
    camera.lookAtTransform(transform, offset);
  }
}

function viewInICRF() {
  Sandcastle.declare(viewInICRF);

  viewer.camera.flyHome(0);

  clock.multiplier = 3 * 60 * 60;
  scene.postUpdate.addEventListener(icrf);
  scene.globe.enableLighting = true;
}

const viewChanged = document.getElementById("viewChanged");

let removeStart;
let removeEnd;

function cameraEvents() {
  Sandcastle.declare(cameraEvents);

  const camera = viewer.camera;
  removeStart = camera.moveStart.addEventListener(function () {
    viewChanged.style.display = "block";
  });
  removeEnd = camera.moveEnd.addEventListener(function () {
    viewChanged.style.display = "none";
  });
}

const cameraChanged = document.getElementById("cameraChanged");

let removeChanged;

function cameraChanges() {
  Sandcastle.declare(cameraChanges);

  let i = 0;
  removeChanged = viewer.camera.changed.addEventListener(function (percentage) {
    ++i;
    cameraChanged.innerText = `Camera Changed: ${i}, ${percentage.toFixed(6)}`;
    cameraChanged.style.display = "block";
  });
}

function flyInACity() {
  Sandcastle.declare(flyInACity);

  const camera = scene.camera;
  camera.flyTo({
    destination: Cesium.Cartesian3.fromDegrees(
      -73.98580932617188,
      40.74843406689482,
      363.34038727246224,
    ),
    complete: function () {
      setTimeout(function () {
        camera.flyTo({
          destination: Cesium.Cartesian3.fromDegrees(
            -73.98585975679403,
            40.75759944127251,
            186.50838555841779,
          ),
          orientation: {
            heading: Cesium.Math.toRadians(200.0),
            pitch: Cesium.Math.toRadians(-50.0),
          },
          easingFunction: Cesium.EasingFunction.LINEAR_NONE,
        });
      }, 1000);
    },
  });
}

function losAngelesToTokyo(adjustPitch) {
  const camera = scene.camera;

  const tokyoOptions = {
    destination: Cesium.Cartesian3.fromDegrees(139.8148, 35.7142, 20000.0),
    orientation: {
      heading: Cesium.Math.toRadians(15.0),
      pitch: Cesium.Math.toRadians(-60),
      roll: 0.0,
    },
    duration: 20,
    flyOverLongitude: Cesium.Math.toRadians(60.0),
  };

  const laOptions = {
    destination: Cesium.Cartesian3.fromDegrees(-117.729, 34.457, 10000.0),
    duration: 5,
    orientation: {
      heading: Cesium.Math.toRadians(-15.0),
      pitch: -Cesium.Math.PI_OVER_FOUR,
      roll: 0.0,
    },
  };

  laOptions.complete = function () {
    setTimeout(function () {
      camera.flyTo(tokyoOptions);
    }, 1000);
  };

  if (adjustPitch) {
    tokyoOptions.pitchAdjustHeight = 1000;
    laOptions.pitchAdjustHeight = 1000;
  }

  camera.flyTo(laOptions);
}

function flyOverLongitude(adjustPitch) {
  Sandcastle.declare(flyOverLongitude);
  losAngelesToTokyo();
}

function flyOverLongitudeWithPitch() {
  Sandcastle.declare(flyOverLongitudeWithPitch);
  losAngelesToTokyo(true);
}

Sandcastle.addToolbarMenu([
  {
    text: "Camera Options",
  },
  {
    text: "Fly in a city",
    onselect: function () {
      reset();
      flyInACity();
      Sandcastle.highlight(flyInACity);
    },
  },
  {
    text: "Fly to San Diego",
    onselect: function () {
      reset();
      flyToSanDiego();
      Sandcastle.highlight(flyToSanDiego);
    },
  },
  {
    text: "Fly to Location with heading, pitch and roll",
    onselect: function () {
      reset();
      flyToHeadingPitchRoll();
      Sandcastle.highlight(flyToHeadingPitchRoll);
    },
  },
  {
    text: "Fly to My Location",
    onselect: function () {
      reset();
      flyToLocation();
      Sandcastle.highlight(flyToLocation);
    },
  },
  {
    text: "Fly to Rectangle",
    onselect: function () {
      reset();
      flyToRectangle();
      Sandcastle.highlight(flyToRectangle);
    },
  },
  {
    text: "View a Rectangle",
    onselect: function () {
      reset();
      viewRectangle();
      Sandcastle.highlight(viewRectangle);
    },
  },
  {
    text: "Set camera reference frame",
    onselect: function () {
      reset();
      setReferenceFrame();
      Sandcastle.highlight(setReferenceFrame);
    },
  },
  {
    text: "Set camera with heading, pitch, and roll",
    onselect: function () {
      reset();
      setHeadingPitchRoll();
      Sandcastle.highlight(setHeadingPitchRoll);
    },
  },
  {
    text: "View in ICRF",
    onselect: function () {
      reset();
      viewInICRF();
      Sandcastle.highlight(viewInICRF);
    },
  },
  {
    text: "Move events",
    onselect: function () {
      reset();
      cameraEvents();
      Sandcastle.highlight(cameraEvents);
    },
  },
  {
    text: "Camera changed event",
    onselect: function () {
      reset();
      cameraChanges();
      Sandcastle.highlight(cameraChanges);
    },
  },
  {
    text: "Fly from Los Angeles to Tokyo via Europe",
    onselect: function () {
      reset();
      flyOverLongitude();
      Sandcastle.highlight(flyOverLongitude);
    },
  },
  {
    text: "Look down during exaggerated flight",
    onselect: function () {
      reset();
      flyOverLongitudeWithPitch();
      Sandcastle.highlight(flyOverLongitudeWithPitch);
    },
  },
]);

Sandcastle.addToolbarButton("Complete Flight", function () {
  scene.camera.completeFlight();
});

Sandcastle.addToolbarButton("Cancel Flight", function () {
  scene.camera.cancelFlight();
});

function reset() {
  scene.completeMorph();
  viewer.entities.removeAll();
  scene.primitives.remove(referenceFramePrimitive);
  scene.tweens.removeAll();

  if (Cesium.defined(removeStart)) {
    removeStart();
    removeEnd();

    viewChanged.style.display = "none";

    removeStart = undefined;
    removeEnd = undefined;
  }

  if (Cesium.defined(removeChanged)) {
    removeChanged();
    removeChanged = undefined;

    cameraChanged.style.display = "none";
  }

  viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY);

  clock.multiplier = 1.0;
  scene.postUpdate.removeEventListener(icrf);
  scene.globe.enableLighting = false;
}

scene.morphComplete.addEventListener(function () {
  reset();
});
