import { defined } from "../../../Source/Cesium.js";
import { getTimestamp } from "../../../Source/Cesium.js";
import { FrameRateMonitor } from "../../../Source/Cesium.js";
import createScene from "../../createScene.js";
import { PerformanceWatchdogViewModel } from "../../../Source/Cesium.js";

describe(
  "Widgets/PerformanceWatchdog/PerformanceWatchdogViewModel",
  function () {
    var scene;
    beforeAll(function () {
      scene = createScene();
    });

    afterAll(function () {
      scene.destroyForSpecs();
    });

    var viewModel;
    afterEach(function () {
      if (defined(viewModel)) {
        viewModel.destroy();
        viewModel = undefined;
      }

      FrameRateMonitor.fromScene(scene).destroy();
    });

    function spinWait(milliseconds) {
      /*eslint-disable no-empty*/
      var endTime = getTimestamp() + milliseconds;
      while (getTimestamp() < endTime) {}
      /*eslint-enable no-empty*/
    }

    it("throws when constructed without a scene", function () {
      expect(function () {
        viewModel = new PerformanceWatchdogViewModel();
      }).toThrowDeveloperError();

      expect(function () {
        viewModel = new PerformanceWatchdogViewModel({});
      }).toThrowDeveloperError();
    });

    it("can be constructed with just a scene", function () {
      viewModel = new PerformanceWatchdogViewModel({
        scene: scene,
      });

      expect(viewModel.lowFrameRateMessage).toBeDefined();
      expect(viewModel.lowFrameRateMessageDismissed).toBe(false);
      expect(viewModel.showingLowFrameRateMessage).toBe(false);
      expect(viewModel.scene).toBe(scene);
    });

    it("honors parameters to the constructor", function () {
      var options = {
        scene: scene,
        lowFrameRateMessage: "why so slow?",
      };

      viewModel = new PerformanceWatchdogViewModel(options);

      expect(viewModel.lowFrameRateMessage).toBe("why so slow?");
      expect(viewModel.scene).toBe(scene);
    });

    it("shows a message on low frame rate", function () {
      var monitor = FrameRateMonitor.fromScene(scene);
      monitor.quietPeriod = 0.001;
      monitor.warmupPeriod = 0.001;
      monitor.samplingWindow = 0.001;
      monitor.minimumFrameRateDuringWarmup = 1000;
      monitor.minimumFrameRateAfterWarmup = 1000;

      viewModel = new PerformanceWatchdogViewModel({
        scene: scene,
      });

      expect(viewModel.showingLowFrameRateMessage).toBe(false);

      // Rendering once starts the quiet period
      scene.render();

      // Wait until we're well past the end of the quiet period.
      spinWait(2);

      // Rendering again records our first sample.
      scene.render();

      // Wait well over a millisecond, which is the maximum frame time allowed by this instance.
      spinWait(2);

      // Record our second sample.  The watchdog should notice that our frame rate is too low.
      scene.render();
      expect(viewModel.showingLowFrameRateMessage).toBe(true);
    });

    it("does not report a low frame rate during the queit period", function () {
      var monitor = FrameRateMonitor.fromScene(scene);
      monitor.quietPeriod = 1.0;
      monitor.warmupPeriod = 0.001;
      monitor.samplingWindow = 0.001;
      monitor.minimumFrameRateDuringWarmup = 1000;
      monitor.minimumFrameRateAfterWarmup = 1000;

      viewModel = new PerformanceWatchdogViewModel({
        scene: scene,
      });

      // Rendering once starts the quiet period
      scene.render();

      // Wait well over a millisecond, which is the maximum frame time allowed by this instance.
      spinWait(2);

      // Render again.  Even though our frame rate is too low, the watchdog shouldn't bark because we're in the quiet period.
      scene.render();
      expect(viewModel.showingLowFrameRateMessage).toBe(false);
    });

    it("the low frame rate message goes away after the warmup period if the frame rate returns to nominal", function () {
      var monitor = FrameRateMonitor.fromScene(scene);
      monitor.quietPeriod = 0.001;
      monitor.warmupPeriod = 0.001;
      monitor.samplingWindow = 0.001;
      monitor.minimumFrameRateDuringWarmup = 10;
      monitor.minimumFrameRateAfterWarmup = 10;

      viewModel = new PerformanceWatchdogViewModel({
        scene: scene,
      });

      expect(viewModel.showingLowFrameRateMessage).toBe(false);

      // Rendering once starts the quiet period
      scene.render();

      // Wait until we're well past the end of the quiet period.
      spinWait(2);

      // Rendering again records our first sample.
      scene.render();

      // Wait 120 millseconds, which is over the maximum frame time allowed by this instance.
      spinWait(120);

      // Record our second sample.  The watchdog should notice that our frame rate is too low.
      scene.render();
      expect(viewModel.showingLowFrameRateMessage).toBe(true);

      // Render as fast as possible for a samplingWindow, quietPeriod, and warmupPeriod.
      var endTime = getTimestamp() + 50;
      while (getTimestamp() < endTime) {
        scene.render();
      }

      // The low frame rate message should have gone away.
      expect(viewModel.showingLowFrameRateMessage).toBe(false);
    });

    it("does not show the low frame rate message again once it is dismissed", function () {
      var monitor = FrameRateMonitor.fromScene(scene);
      monitor.quietPeriod = 0.001;
      monitor.warmupPeriod = 0.001;
      monitor.samplingWindow = 0.001;
      monitor.minimumFrameRateDuringWarmup = 1000;
      monitor.minimumFrameRateAfterWarmup = 1000;

      viewModel = new PerformanceWatchdogViewModel({
        scene: scene,
      });

      expect(viewModel.showingLowFrameRateMessage).toBe(false);

      // Rendering once starts the quiet period
      scene.render();

      // Wait until we're well past the end of the quiet period.
      spinWait(2);

      // Rendering again records our first sample.
      scene.render();

      // Wait well over a millisecond, which is the maximum frame time allowed by this instance.
      spinWait(2);

      // Record our second sample.  The watchdog should notice that our frame rate is too low.
      scene.render();
      expect(viewModel.showingLowFrameRateMessage).toBe(true);

      viewModel.dismissMessage();

      // Render several slow frames.  The message should not re-appear.
      scene.render();
      spinWait(2);
      scene.render();
      expect(viewModel.showingLowFrameRateMessage).toBe(false);
    });
  },
  "WebGL"
);
