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

let rectangle;
let worldRectangle;
let polyline;

function applyAlphaMapMaterial(primitive, scene) {
  Sandcastle.declare(applyAlphaMapMaterial); // For highlighting in Sandcastle.
  primitive.appearance.material = new Cesium.Material({
    fabric: {
      materials: {
        alphaMaterial: {
          type: "AlphaMap",
          uniforms: {
            image: "../images/Cesium_Logo_Color.jpg",
            channel: "r",
          },
        },
      },
      components: {
        diffuse: "vec3(1.0)",
        alpha: "alphaMaterial.alpha",
      },
    },
  });
}

function applyBumpMapMaterial(primitive, scene) {
  Sandcastle.declare(applyBumpMapMaterial); // For highlighting in Sandcastle.
  primitive.appearance.material = new Cesium.Material({
    fabric: {
      materials: {
        diffuseMaterial: {
          type: "DiffuseMap",
          uniforms: {
            image: "../images/bumpmap.png",
          },
        },
        bumpMaterial: {
          type: "BumpMap",
          uniforms: {
            image: "../images/bumpmap.png",
            strength: 0.8,
          },
        },
      },
      components: {
        diffuse: "diffuseMaterial.diffuse",
        specular: 0.01,
        normal: "bumpMaterial.normal",
      },
    },
  });
}

function applyCheckerboardMaterial(primitive, scene) {
  Sandcastle.declare(applyCheckerboardMaterial); // For highlighting in Sandcastle.
  primitive.appearance.material = Cesium.Material.fromType("Checkerboard");
}

function applyColorMaterial(primitive, scene) {
  Sandcastle.declare(applyColorMaterial); // For highlighting in Sandcastle.
  primitive.appearance.material = Cesium.Material.fromType("Color");
}

function applyCompositeMaterial(primitive, scene) {
  Sandcastle.declare(applyCompositeMaterial); // For highlighting in Sandcastle.
  primitive.appearance.material = new Cesium.Material({
    fabric: {
      uniforms: {
        image: "../images/earthspec1k.jpg",
        heightField: "../images/earthbump1k.jpg",
      },
      materials: {
        bumpMap: {
          type: "BumpMap",
          uniforms: {
            image: "../images/earthbump1k.jpg",
          },
        },
      },
      source: `
                czm_material czm_getMaterial(czm_materialInput materialInput) {
                    czm_material material = czm_getDefaultMaterial(materialInput);
                    vec4 color;
                    float heightValue = texture(heightField, materialInput.st).r;
                    color.rgb = mix(vec3(0.2, 0.6, 0.2), vec3(1.0, 0.5, 0.2), heightValue);
                    color.a = (1.0 - texture(image, materialInput.st).r) * 0.7;
                    color = czm_gammaCorrect(color);
                    material.diffuse = color.rgb;
                    material.alpha = color.a;
                    material.normal = bumpMap.normal;
                    material.specular = step(0.1, heightValue);  // Specular mountain tops
                    material.shininess = 8.0;  // Sharpen highlight
                    return material;
                }
                `,
    },
  });
}

function applyDotMaterial(primitive, scene) {
  Sandcastle.declare(applyDotMaterial); // For highlighting in Sandcastle.
  primitive.appearance.material = Cesium.Material.fromType("Dot");
}

function applyDiffuseMaterial(primitive, scene) {
  Sandcastle.declare(applyDiffuseMaterial); // For highlighting in Sandcastle.
  primitive.appearance.material = new Cesium.Material({
    fabric: {
      type: "DiffuseMap",
      uniforms: {
        image: "../images/Cesium_Logo_Color.jpg",
      },
    },
  });
}

function applyEmissionMapMaterial(primitive, scene) {
  Sandcastle.declare(applyEmissionMapMaterial); // For highlighting in Sandcastle.
  primitive.appearance.material = new Cesium.Material({
    fabric: {
      materials: {
        diffuseMaterial: {
          type: "DiffuseMap",
          uniforms: {
            image: "../images/Cesium_Logo_Color.jpg",
          },
        },
        emissionMaterial: {
          type: "EmissionMap",
          uniforms: {
            image: "../images/checkerboard.png",
            repeat: {
              x: 1,
              y: 0.5,
            },
          },
        },
      },
      components: {
        diffuse: "diffuseMaterial.diffuse",
        emission: "emissionMaterial.emission * 0.2",
      },
    },
  });
}

function applyWaterMaterial(primitive, scene) {
  Sandcastle.declare(applyWaterMaterial); // For highlighting in Sandcastle.

  primitive.appearance.material = new Cesium.Material({
    fabric: {
      type: "Water",
      uniforms: {
        specularMap: "../images/earthspec1k.jpg",
        normalMap: Cesium.buildModuleUrl("Assets/Textures/waterNormals.jpg"),
        frequency: 10000.0,
        animationSpeed: 0.01,
        amplitude: 1.0,
      },
    },
  });
}

function applyGridMaterial(primitive, scene) {
  Sandcastle.declare(applyGridMaterial); // For highlighting in Sandcastle.
  primitive.appearance.material = Cesium.Material.fromType("Grid");
}

function applyImageMaterial(primitive, scene) {
  Sandcastle.declare(applyImageMaterial); // For highlighting in Sandcastle.
  primitive.appearance.material = new Cesium.Material({
    fabric: {
      type: "Image",
      uniforms: {
        image: "../images/Cesium_Logo_Color.jpg",
      },
    },
  });
}

function applyETC1SCompressedTextureMaterial(primitive, scene) {
  Sandcastle.declare(applyETC1SCompressedTextureMaterial); // For highlighting in Sandcastle.

  const compressedImageUrl = "../images/Cesium_Logo_ETC1S.ktx2";

  primitive.appearance.material = new Cesium.Material({
    fabric: {
      type: "Image",
      uniforms: {
        image: compressedImageUrl,
      },
    },
  });
}

function applyUASTCCompressedTextureMaterial(primitive, scene) {
  Sandcastle.declare(applyUASTCCompressedTextureMaterial); // For highlighting in Sandcastle.

  const compressedImageUrl = "../images/Cesium_Logo_UASTC.ktx2";

  primitive.appearance.material = new Cesium.Material({
    fabric: {
      type: "Image",
      uniforms: {
        image: compressedImageUrl,
      },
    },
  });
}

function applyNormalMapMaterial(primitive, scene) {
  Sandcastle.declare(applyNormalMapMaterial); // For highlighting in Sandcastle.
  primitive.appearance.material = new Cesium.Material({
    fabric: {
      materials: {
        diffuseMaterial: {
          type: "DiffuseMap",
          uniforms: {
            image: "../images/bumpmap.png",
          },
        },
        normalMap: {
          type: "NormalMap",
          uniforms: {
            image: "../images/normalmap.png",
            strength: 0.6,
          },
        },
      },
      components: {
        diffuse: "diffuseMaterial.diffuse",
        specular: 0.01,
        normal: "normalMap.normal",
      },
    },
  });
}

function applySpecularMapMaterial(primitive, scene) {
  Sandcastle.declare(applySpecularMapMaterial); // For highlighting in Sandcastle.
  primitive.appearance.material = new Cesium.Material({
    fabric: {
      type: "SpecularMap",
      uniforms: {
        image: "../images/Cesium_Logo_Color.jpg",
        channel: "r",
      },
    },
  });
}

function applyStripeMaterial(primitive, scene) {
  Sandcastle.declare(applyStripeMaterial); // For highlighting in Sandcastle.
  primitive.appearance.material = Cesium.Material.fromType("Stripe");
}

function applyRimLightingMaterial(primitive, scene) {
  Sandcastle.declare(applyRimLightingMaterial); // For highlighting in Sandcastle.
  primitive.appearance.material = Cesium.Material.fromType("RimLighting");
}

function applyPolylineArrowMaterial(primitive, scene) {
  Sandcastle.declare(applyPolylineArrowMaterial); // For highlighting in Sandcastle.
  const material = Cesium.Material.fromType("PolylineArrow");
  primitive.material = material;
}

function applyPolylineGlowMaterial(primitive, scene) {
  Sandcastle.declare(applyPolylineGlowMaterial); // For highlighting in Sandcastle.
  const material = Cesium.Material.fromType("PolylineGlow", {
    color: Cesium.Color.CRIMSON,
    glowPower: 0.2,
    taperPower: 0.4,
  });
  primitive.material = material;
}

function applyPolylineOutlineMaterial(primitive, scene) {
  Sandcastle.declare(applyPolylineOutlineMaterial); // For highlighting in Sandcastle.
  const material = Cesium.Material.fromType("PolylineOutline", {
    outlineWidth: primitive.width / 2.0,
  });
  primitive.material = material;
}

function createButtons(scene) {
  function toggleRectangleVisibility() {
    rectangle.show = true;
    worldRectangle.show = false;
    polyline.show = false;
  }

  function toggleWorldRectangleVisibility() {
    worldRectangle.show = true;
    rectangle.show = false;
    polyline.show = false;
  }

  function togglePolylineVisibility() {
    polyline.show = true;
    worldRectangle.show = false;
    rectangle.show = false;
  }

  Sandcastle.addToolbarMenu([
    {
      text: "Common materials",
    },
    {
      text: "Color",
      onselect: function () {
        toggleRectangleVisibility();
        applyColorMaterial(rectangle, scene);
        Sandcastle.highlight(applyColorMaterial);
      },
    },
    {
      text: "Image",
      onselect: function () {
        toggleRectangleVisibility();
        applyImageMaterial(rectangle, scene);
        Sandcastle.highlight(applyImageMaterial);
      },
    },
    {
      text: "ETC1S Compressed Image",
      onselect: function () {
        if (!Cesium.FeatureDetection.supportsBasis(scene)) {
          window.alert(
            "This browser does not support Basis Universal compressed textures",
          );
        }
        toggleRectangleVisibility();
        applyETC1SCompressedTextureMaterial(rectangle, scene);
        Sandcastle.highlight(applyETC1SCompressedTextureMaterial);
      },
    },
    {
      text: "UASTC Compressed Image",
      onselect: function () {
        if (!Cesium.FeatureDetection.supportsBasis(scene)) {
          window.alert(
            "This browser does not support Basis Universal compressed textures",
          );
        }
        toggleRectangleVisibility();
        applyUASTCCompressedTextureMaterial(rectangle, scene);
        Sandcastle.highlight(applyUASTCCompressedTextureMaterial);
      },
    },
  ]);

  Sandcastle.addToolbarMenu([
    {
      text: "Procedural textures",
    },
    {
      text: "Checkerboard",
      onselect: function () {
        toggleRectangleVisibility();
        applyCheckerboardMaterial(rectangle, scene);
        Sandcastle.highlight(applyCheckerboardMaterial);
      },
    },
    {
      text: "Dot",
      onselect: function () {
        toggleRectangleVisibility();
        applyDotMaterial(rectangle, scene);
        Sandcastle.highlight(applyDotMaterial);
      },
    },
    {
      text: "Grid",
      onselect: function () {
        toggleRectangleVisibility(rectangle, worldRectangle);
        applyGridMaterial(rectangle, scene);
        Sandcastle.highlight(applyGridMaterial);
      },
    },
    {
      text: "Stripe",
      onselect: function () {
        toggleRectangleVisibility();
        applyStripeMaterial(rectangle, scene);
        Sandcastle.highlight(applyStripeMaterial);
      },
    },
  ]);

  Sandcastle.addToolbarMenu([
    {
      text: "Base materials",
    },
    {
      text: "Alpha Map",
      onselect: function () {
        toggleRectangleVisibility();
        applyAlphaMapMaterial(rectangle, scene);
        Sandcastle.highlight(applyAlphaMapMaterial);
      },
    },
    {
      text: "Bump Map",
      onselect: function () {
        toggleRectangleVisibility();
        applyBumpMapMaterial(rectangle, scene);
        Sandcastle.highlight(applyBumpMapMaterial);
      },
    },
    {
      text: "Diffuse",
      onselect: function () {
        toggleRectangleVisibility();
        applyDiffuseMaterial(rectangle, scene);
        Sandcastle.highlight(applyDiffuseMaterial);
      },
    },
    {
      text: "Emission Map",
      onselect: function () {
        toggleRectangleVisibility();
        applyEmissionMapMaterial(rectangle, scene);
        Sandcastle.highlight(applyEmissionMapMaterial);
      },
    },
    {
      text: "Normal Map",
      onselect: function () {
        toggleRectangleVisibility();
        applyNormalMapMaterial(rectangle, scene);
        Sandcastle.highlight(applyNormalMapMaterial);
      },
    },
    {
      text: "Specular Map",
      onselect: function () {
        toggleRectangleVisibility();
        applySpecularMapMaterial(rectangle, scene);
        Sandcastle.highlight(applySpecularMapMaterial);
      },
    },
  ]);

  Sandcastle.addToolbarMenu([
    {
      text: "Misc materials",
    },
    {
      text: "Rim Lighting",
      onselect: function () {
        toggleWorldRectangleVisibility();
        applyRimLightingMaterial(worldRectangle, scene);
        Sandcastle.highlight(applyRimLightingMaterial);
      },
    },
    {
      text: "Water",
      onselect: function () {
        toggleWorldRectangleVisibility();
        applyWaterMaterial(worldRectangle, scene);
        Sandcastle.highlight(applyWaterMaterial);
      },
    },
  ]);

  Sandcastle.addToolbarMenu([
    {
      text: "Example composite materials",
    },
    {
      text: "Composite Example",
      onselect: function () {
        toggleWorldRectangleVisibility();
        applyCompositeMaterial(worldRectangle, scene);
        Sandcastle.highlight(applyCompositeMaterial);
      },
    },
  ]);

  Sandcastle.addToolbarMenu([
    {
      text: "Polyline materials",
    },
    {
      text: "Polyline Arrow",
      onselect: function () {
        togglePolylineVisibility();
        applyPolylineArrowMaterial(polyline, scene);
        Sandcastle.highlight(applyPolylineArrowMaterial);
      },
    },
    {
      text: "Polyline Glow",
      onselect: function () {
        togglePolylineVisibility();
        applyPolylineGlowMaterial(polyline, scene);
        Sandcastle.highlight(applyPolylineGlowMaterial);
      },
    },
    {
      text: "Polyline Outline",
      onselect: function () {
        togglePolylineVisibility();
        applyPolylineOutlineMaterial(polyline, scene);
        Sandcastle.highlight(applyPolylineOutlineMaterial);
      },
    },
  ]);
}

function createPrimitives(scene) {
  rectangle = scene.primitives.add(
    new Cesium.Primitive({
      geometryInstances: new Cesium.GeometryInstance({
        geometry: new Cesium.RectangleGeometry({
          rectangle: Cesium.Rectangle.fromDegrees(-120.0, 20.0, -60.0, 40.0),
          vertexFormat: Cesium.EllipsoidSurfaceAppearance.VERTEX_FORMAT,
        }),
      }),
      appearance: new Cesium.EllipsoidSurfaceAppearance({
        aboveGround: false,
      }),
    }),
  );

  worldRectangle = scene.primitives.add(
    new Cesium.Primitive({
      geometryInstances: new Cesium.GeometryInstance({
        geometry: new Cesium.RectangleGeometry({
          rectangle: Cesium.Rectangle.fromDegrees(-180.0, -90.0, 180.0, 90.0),
          vertexFormat: Cesium.EllipsoidSurfaceAppearance.VERTEX_FORMAT,
        }),
      }),
      appearance: new Cesium.EllipsoidSurfaceAppearance({
        aboveGround: false,
      }),
      show: false,
    }),
  );

  const polylines = scene.primitives.add(new Cesium.PolylineCollection());
  polyline = polylines.add({
    positions: Cesium.PolylinePipeline.generateCartesianArc({
      positions: Cesium.Cartesian3.fromDegreesArray([
        -110.0, 42.0, -85.0, 36.0, -100.0, 25.0, -77.0, 12.0,
      ]),
    }),
    width: 10.0,
    show: false,
  });
}

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

createPrimitives(scene);
createButtons(scene);
