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

// A demo of an multi-layer Architectural Design with site clipping,
// metadata picking and layer visibility toggling.
// Snowdon Towers sample data courtesy of Autodesk Revit.
const viewer = new Cesium.Viewer("cesiumContainer", {
  globe: false,
});

// Enable rendering the sky
viewer.scene.skyAtmosphere.show = true;

// Configure Ambient Occlusion
if (Cesium.PostProcessStageLibrary.isAmbientOcclusionSupported(viewer.scene)) {
  const ambientOcclusion = viewer.scene.postProcessStages.ambientOcclusion;
  ambientOcclusion.enabled = true;
  ambientOcclusion.uniforms.intensity = 2.0;
  ambientOcclusion.uniforms.bias = 0.1;
  ambientOcclusion.uniforms.lengthCap = 0.5;
  ambientOcclusion.uniforms.directionCount = 16;
  ambientOcclusion.uniforms.stepCount = 32;
}

// Set to 1 PM Philadelphia time in UTC
viewer.clock.currentTime = Cesium.JulianDate.fromIso8601(
  "2024-11-22T18:00:00Z",
);

// Set the initial camera view
viewer.camera.setView({
  destination: Cesium.Cartesian3.fromDegrees(-79.886626, 40.021649, 235.65),
  orientation: {
    heading: 0,
    pitch: Cesium.Math.toRadians(-20),
    roll: 0,
  },
});

// Add Photorealistic 3D tiles
let googleTileset;
try {
  googleTileset = await Cesium.Cesium3DTileset.fromIonAssetId(2275207);
  viewer.scene.primitives.add(googleTileset);
} catch (error) {
  console.log(`Error loading tileset: ${error}`);
}

// Add clipping for the site
const positions = Cesium.Cartesian3.fromDegreesArray([
  -79.887735, 40.022564, -79.886341, 40.023087, -79.886161, 40.023087,
  -79.885493, 40.022032, -79.88703, 40.021456, -79.887735, 40.022564,
]);

const polygon = new Cesium.ClippingPolygon({
  positions: positions,
});

const polygons = new Cesium.ClippingPolygonCollection({
  polygons: [polygon],
});

googleTileset.clippingPolygons = polygons;

// The Architectural Design is comprised of multiple tilesets
const tilesetData = [
  { title: "Architecture", assetId: 2887123, visible: true },
  { title: "Facade", assetId: 2887125, visible: true },
  { title: "Structural", assetId: 2887130, visible: false },
  { title: "Electrical", assetId: 2887124, visible: true },
  { title: "HVAC", assetId: 2887126, visible: true },
  { title: "Plumbing", assetId: 2887127, visible: true },
  { title: "Site", assetId: 2887129, visible: true },
];

// Load each tileset and create a corresponding visibility toggle button
for (const { title, assetId, visible } of tilesetData) {
  try {
    const tileset = await Cesium.Cesium3DTileset.fromIonAssetId(assetId);
    viewer.scene.primitives.add(tileset);
    tileset.show = visible;

    Sandcastle.addToggleButton(title, visible, function (checked) {
      tileset.show = checked;
    });
  } catch (error) {
    console.log(`Error loading tileset (${title}): ${error}`);
  }
}

// Get default left click handler for when a feature is not picked on left click
const clickHandler = viewer.screenSpaceEventHandler.getInputAction(
  Cesium.ScreenSpaceEventType.LEFT_CLICK,
);

// Create the HTML that will be put into the info box that shows
// information about the currently selected feature
function createPickedFeatureDescription(pickedFeature) {
  let description = `${'<table class="cesium-infoBox-defaultTable"><tbody>'}`;

  const propertyIds = pickedFeature.getPropertyIds();

  // Sort properties alphabetically
  propertyIds.sort((a, b) => a.localeCompare(b));

  const length = propertyIds.length;
  for (let i = 0; i < length; ++i) {
    const propertyId = propertyIds[i];
    const propertyValue = pickedFeature.getProperty(propertyId);

    // Reject properties with default values
    if (Cesium.defined(propertyValue) && propertyValue !== "") {
      description += `<tr><th>${propertyId}</th><td>${propertyValue}</td></tr>`;
    }
  }

  description += `</tbody></table>`;

  return description;
}

// An entity object which will hold info about the currently selected feature for infobox display
const selectedEntity = new Cesium.Entity();

// Information about the currently selected feature
const selected = {
  feature: undefined,
  originalColor: new Cesium.Color(),
};

// Information about the currently highlighted feature
const highlighted = {
  feature: undefined,
  originalColor: new Cesium.Color(),
};

// Color a feature yellow on hover.
viewer.screenSpaceEventHandler.setInputAction(function onMouseMove(movement) {
  // If a feature was previously highlighted, undo the highlight
  if (Cesium.defined(highlighted.feature)) {
    highlighted.feature.color = highlighted.originalColor;
    highlighted.feature = undefined;
  }
  // Pick a new feature
  const pickedFeature = viewer.scene.pick(movement.endPosition);

  if (
    !Cesium.defined(pickedFeature) ||
    !(pickedFeature instanceof Cesium.Cesium3DTileFeature)
  ) {
    return;
  }

  // Highlight the feature if it's not already selected.
  if (pickedFeature !== selected.feature) {
    highlighted.feature = pickedFeature;
    Cesium.Color.clone(pickedFeature.color, highlighted.originalColor);
    pickedFeature.color = Cesium.Color.YELLOW;
  }
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

// Color a feature on selection and show metadata in the InfoBox.
viewer.screenSpaceEventHandler.setInputAction(function onLeftClick(movement) {
  // If a feature was previously selected, undo the highlight
  if (Cesium.defined(selected.feature)) {
    selected.feature.color = selected.originalColor;
    selected.feature = undefined;
  }
  // Pick a new feature
  const pickedFeature = viewer.scene.pick(movement.position);
  if (
    !Cesium.defined(pickedFeature) ||
    !(pickedFeature instanceof Cesium.Cesium3DTileFeature)
  ) {
    clickHandler(movement);
    return;
  }
  // Select the feature if it's not already selected
  if (selected.feature === pickedFeature) {
    return;
  }
  selected.feature = pickedFeature;
  // Save the selected feature's original color
  if (pickedFeature === highlighted.feature) {
    Cesium.Color.clone(highlighted.originalColor, selected.originalColor);
    highlighted.feature = undefined;
  } else {
    Cesium.Color.clone(pickedFeature.color, selected.originalColor);
  }
  // Highlight newly selected feature
  pickedFeature.color = Cesium.Color.LIME;

  // Set feature infobox description
  viewer.selectedEntity = selectedEntity;
  selectedEntity.description = createPickedFeatureDescription(pickedFeature);
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
