import { ClockRange } from "../../../Source/Cesium.js";
import { ClockStep } from "../../../Source/Cesium.js";
import { JulianDate } from "../../../Source/Cesium.js";
import { AnimationViewModel } from "../../../Source/Cesium.js";
import { ClockViewModel } from "../../../Source/Cesium.js";

describe("Widgets/Animation/AnimationViewModel", function () {
  var clockViewModel;
  beforeEach(function () {
    clockViewModel = new ClockViewModel();
  });

  function verifyPausedState(viewModel) {
    expect(viewModel.pauseViewModel.toggled).toEqual(true);
    expect(viewModel.playReverseViewModel.toggled).toEqual(false);
    expect(viewModel.playForwardViewModel.toggled).toEqual(false);
    expect(viewModel.playRealtimeViewModel.toggled).toEqual(false);
  }

  function verifyForwardState(viewModel) {
    expect(viewModel.pauseViewModel.toggled).toEqual(false);
    expect(viewModel.playReverseViewModel.toggled).toEqual(false);
    expect(viewModel.playForwardViewModel.toggled).toEqual(true);
    expect(viewModel.playRealtimeViewModel.toggled).toEqual(false);
  }

  function verifyReverseState(viewModel) {
    expect(viewModel.pauseViewModel.toggled).toEqual(false);
    expect(viewModel.playReverseViewModel.toggled).toEqual(true);
    expect(viewModel.playForwardViewModel.toggled).toEqual(false);
    expect(viewModel.playRealtimeViewModel.toggled).toEqual(false);
  }

  function verifyRealtimeState(viewModel) {
    expect(viewModel.pauseViewModel.toggled).toEqual(false);
    expect(viewModel.playReverseViewModel.toggled).toEqual(false);
    expect(viewModel.playForwardViewModel.toggled).toEqual(false);
    expect(viewModel.playRealtimeViewModel.toggled).toEqual(true);
    expect(viewModel.shuttleRingAngle).toEqual(
      AnimationViewModel._realtimeShuttleRingAngle
    );
  }

  it("constructor sets expected properties", function () {
    var animationViewModel = new AnimationViewModel(clockViewModel);
    expect(animationViewModel.clockViewModel).toBe(clockViewModel);
  });

  it("setTimeFormatter overrides the default formatter", function () {
    var animationViewModel = new AnimationViewModel(clockViewModel);

    var expectedString = "My Time";
    var myCustomFormatter = function (date) {
      expect(date).toEqual(clockViewModel.currentTime);
      return expectedString;
    };
    animationViewModel.timeFormatter = myCustomFormatter;

    expect(animationViewModel.timeLabel).toEqual(expectedString);
    expect(animationViewModel.timeFormatter).toEqual(myCustomFormatter);
  });

  it("defaultTimeFormatter produces expected result", function () {
    var animationViewModel = new AnimationViewModel(clockViewModel);

    var date = JulianDate.fromIso8601("2012-03-05T06:07:08.89Z");

    clockViewModel.multiplier = 1;
    var expectedResult = "06:07:08 UTC";
    var result = animationViewModel.timeFormatter(date, animationViewModel);
    expect(result).toEqual(expectedResult);

    clockViewModel.multiplier = -1;
    expectedResult = "06:07:08 UTC";
    result = animationViewModel.timeFormatter(date, animationViewModel);
    expect(result).toEqual(expectedResult);

    clockViewModel.multiplier = -0.5;
    expectedResult = "06:07:08.890";
    result = animationViewModel.timeFormatter(date, animationViewModel);
    expect(result).toEqual(expectedResult);

    clockViewModel.multiplier = 0.5;
    expectedResult = "06:07:08.890";
    result = animationViewModel.timeFormatter(date, animationViewModel);
    expect(result).toEqual(expectedResult);
  });

  it("setDateFormatter overrides the default formatter", function () {
    var animationViewModel = new AnimationViewModel(clockViewModel);

    var expectedString = "My Date";
    var myCustomFormatter = function (date) {
      expect(date).toEqual(clockViewModel.currentTime);
      return expectedString;
    };
    animationViewModel.dateFormatter = myCustomFormatter;

    expect(animationViewModel.dateLabel).toEqual(expectedString);
    expect(animationViewModel.dateFormatter).toEqual(myCustomFormatter);
  });

  it("defaultDateFormatter produces expected result", function () {
    var animationViewModel = new AnimationViewModel(new ClockViewModel());

    var date = JulianDate.fromIso8601("2012-01-05T06:07:08.89Z");
    var expectedResult = "Jan 5 2012";
    var result = animationViewModel.dateFormatter(date, animationViewModel);
    expect(result).toEqual(expectedResult);

    date = JulianDate.fromIso8601("2012-02-05T06:07:08.89Z");
    expectedResult = "Feb 5 2012";
    result = animationViewModel.dateFormatter(date, animationViewModel);
    expect(result).toEqual(expectedResult);

    date = JulianDate.fromIso8601("2012-03-05T06:07:08.89Z");
    expectedResult = "Mar 5 2012";
    result = animationViewModel.dateFormatter(date, animationViewModel);
    expect(result).toEqual(expectedResult);

    date = JulianDate.fromIso8601("2012-04-05T06:07:08.89Z");
    expectedResult = "Apr 5 2012";
    result = animationViewModel.dateFormatter(date, animationViewModel);
    expect(result).toEqual(expectedResult);

    date = JulianDate.fromIso8601("2012-05-05T06:07:08.89Z");
    expectedResult = "May 5 2012";
    result = animationViewModel.dateFormatter(date, animationViewModel);
    expect(result).toEqual(expectedResult);

    date = JulianDate.fromIso8601("2012-06-05T06:07:08.89Z");
    expectedResult = "Jun 5 2012";
    result = animationViewModel.dateFormatter(date, animationViewModel);
    expect(result).toEqual(expectedResult);

    date = JulianDate.fromIso8601("2012-07-05T06:07:08.89Z");
    expectedResult = "Jul 5 2012";
    result = animationViewModel.dateFormatter(date, animationViewModel);
    expect(result).toEqual(expectedResult);

    date = JulianDate.fromIso8601("2012-08-05T06:07:08.89Z");
    expectedResult = "Aug 5 2012";
    result = animationViewModel.dateFormatter(date, animationViewModel);
    expect(result).toEqual(expectedResult);

    date = JulianDate.fromIso8601("2012-09-05T06:07:08.89Z");
    expectedResult = "Sep 5 2012";
    result = animationViewModel.dateFormatter(date, animationViewModel);
    expect(result).toEqual(expectedResult);

    date = JulianDate.fromIso8601("2012-10-05T06:07:08.89Z");
    expectedResult = "Oct 5 2012";
    result = animationViewModel.dateFormatter(date, animationViewModel);
    expect(result).toEqual(expectedResult);

    date = JulianDate.fromIso8601("2012-11-05T06:07:08.89Z");
    expectedResult = "Nov 5 2012";
    result = animationViewModel.dateFormatter(date, animationViewModel);
    expect(result).toEqual(expectedResult);

    date = JulianDate.fromIso8601("2012-12-05T06:07:08.89Z");
    expectedResult = "Dec 5 2012";
    result = animationViewModel.dateFormatter(date, animationViewModel);
    expect(result).toEqual(expectedResult);
  });

  it("correctly formats speed label", function () {
    var animationViewModel = new AnimationViewModel(clockViewModel);
    var expectedString;

    clockViewModel.clockStep = ClockStep.TICK_DEPENDENT;
    clockViewModel.multiplier = 123.1;
    expectedString = "123.1x";
    expect(animationViewModel.multiplierLabel).toEqual(expectedString);

    clockViewModel.clockStep = ClockStep.TICK_DEPENDENT;
    clockViewModel.multiplier = 123.12;
    expectedString = "123.12x";
    expect(animationViewModel.multiplierLabel).toEqual(expectedString);

    clockViewModel.clockStep = ClockStep.TICK_DEPENDENT;
    clockViewModel.multiplier = 123.123;
    expectedString = "123.123x";
    expect(animationViewModel.multiplierLabel).toEqual(expectedString);

    clockViewModel.clockStep = ClockStep.TICK_DEPENDENT;
    clockViewModel.multiplier = 123.1236;
    expectedString = "123.124x";
    expect(animationViewModel.multiplierLabel).toEqual(expectedString);

    clockViewModel.clockStep = ClockStep.SYSTEM_CLOCK;
    expectedString = "Today";
    expect(animationViewModel.multiplierLabel).toEqual(expectedString);

    clockViewModel.clockStep = ClockStep.TICK_DEPENDENT;
    clockViewModel.multiplier = 15;
    expectedString = "15x";
    expect(animationViewModel.multiplierLabel).toEqual(expectedString);
  });

  it("pause button restores current state", function () {
    clockViewModel.startTime = JulianDate.fromIso8601("2012-01-01T00:00:00");
    clockViewModel.stopTime = JulianDate.fromIso8601("2012-01-02T00:00:00");
    clockViewModel.currentTime = JulianDate.fromIso8601("2012-01-01T12:00:00");
    clockViewModel.multiplier = 1;
    clockViewModel.clockStep = ClockStep.TICK_DEPENDENT;
    clockViewModel.clockRange = ClockRange.UNBOUNDED;
    clockViewModel.shouldAnimate = false;

    var viewModel = new AnimationViewModel(clockViewModel);

    //Starts out paused
    verifyPausedState(viewModel);

    //Toggling paused restores state when animating forward
    viewModel.pauseViewModel.command();

    verifyForwardState(viewModel);

    //Executing paused command restores paused state
    viewModel.pauseViewModel.command();

    verifyPausedState(viewModel);

    //Setting the multiplier to negative and unpausing animates backward
    clockViewModel.multiplier = -1;
    viewModel.pauseViewModel.command();

    verifyReverseState(viewModel);
  });

  it("animating forwards negates the multiplier if it is negative", function () {
    var viewModel = new AnimationViewModel(clockViewModel);
    var multiplier = -100;
    clockViewModel.multiplier = multiplier;
    viewModel.playForwardViewModel.command();
    expect(clockViewModel.multiplier).toEqual(-multiplier);
  });

  it("animating backwards negates the multiplier if it is positive", function () {
    var viewModel = new AnimationViewModel(clockViewModel);
    var multiplier = 100;
    clockViewModel.multiplier = multiplier;
    viewModel.playReverseViewModel.command();
    expect(clockViewModel.multiplier).toEqual(-multiplier);
  });

  it("animating backwards pauses with a bounded startTime", function () {
    var centerTime = JulianDate.fromIso8601("2012-01-01T12:00:00");

    clockViewModel.startTime = JulianDate.fromIso8601("2012-01-01T00:00:00");
    clockViewModel.stopTime = JulianDate.fromIso8601("2012-01-02T00:00:00");
    clockViewModel.clockStep = ClockStep.TICK_DEPENDENT;
    clockViewModel.currentTime = centerTime;
    clockViewModel.shouldAnimate = false;

    var viewModel = new AnimationViewModel(clockViewModel);
    verifyPausedState(viewModel);

    //Play in reverse while clamped
    clockViewModel.multiplier = -1;
    clockViewModel.clockRange = ClockRange.CLAMPED;
    viewModel.playReverseViewModel.command();

    verifyReverseState(viewModel);

    //Set current time to start time
    clockViewModel.currentTime = clockViewModel.startTime;

    //Should now be paused
    verifyPausedState(viewModel);

    //Animate in reverse again.
    clockViewModel.currentTime = centerTime;
    clockViewModel.clockRange = ClockRange.LOOP_STOP;
    viewModel.playReverseViewModel.command();

    verifyReverseState(viewModel);

    //Set current time to start time
    clockViewModel.currentTime = clockViewModel.startTime;

    //Should now be paused
    verifyPausedState(viewModel);

    //Reversing in start state while bounded should have no effect
    viewModel.playReverseViewModel.command();
    verifyPausedState(viewModel);

    //Set to unbounded and reversing should be okay
    clockViewModel.clockRange = ClockRange.UNBOUNDED;
    viewModel.playReverseViewModel.command();
    verifyReverseState(viewModel);
  });

  it("dragging shuttle ring does not pause with bounded start or stop Time", function () {
    var centerTime = JulianDate.fromIso8601("2012-01-01T12:00:00");

    clockViewModel.startTime = JulianDate.fromIso8601("2012-01-01T00:00:00");
    clockViewModel.stopTime = JulianDate.fromIso8601("2012-01-02T00:00:00");
    clockViewModel.clockStep = ClockStep.TICK_DEPENDENT;
    clockViewModel.clockRange = ClockRange.CLAMPED;
    clockViewModel.multiplier = 1;

    var viewModel = new AnimationViewModel(clockViewModel);
    verifyPausedState(viewModel);

    //Play forward while clamped
    clockViewModel.currentTime = centerTime;
    viewModel.playForwardViewModel.command();
    verifyForwardState(viewModel);

    //Set current time to stop time, which won't stop while dragging
    viewModel.shuttleRingDragging = true;
    clockViewModel.currentTime = clockViewModel.stopTime;
    verifyForwardState(viewModel);

    //Drag complete stops.
    viewModel.shuttleRingDragging = false;
    verifyPausedState(viewModel);

    //Do the same thing with start time
    clockViewModel.currentTime = centerTime;
    viewModel.playReverseViewModel.command();
    verifyReverseState(viewModel);

    viewModel.shuttleRingDragging = true;
    clockViewModel.currentTime = clockViewModel.startTime;
    verifyReverseState(viewModel);

    //Drag complete stops.
    viewModel.shuttleRingDragging = false;
    verifyPausedState(viewModel);
  });

  it("animating forward pauses with a bounded stopTime", function () {
    var centerTime = JulianDate.fromIso8601("2012-01-01T12:00:00");

    clockViewModel.startTime = JulianDate.fromIso8601("2012-01-01T00:00:00");
    clockViewModel.stopTime = JulianDate.fromIso8601("2012-01-02T00:00:00");
    clockViewModel.clockStep = ClockStep.TICK_DEPENDENT;
    clockViewModel.currentTime = centerTime;
    clockViewModel.shouldAnimate = false;

    var viewModel = new AnimationViewModel(clockViewModel);
    verifyPausedState(viewModel);

    //Play forward while clamped
    clockViewModel.multiplier = 1;
    clockViewModel.clockRange = ClockRange.CLAMPED;
    viewModel.playForwardViewModel.command();

    verifyForwardState(viewModel);

    //Set current time to stop time
    clockViewModel.currentTime = clockViewModel.stopTime;

    //Should now be paused
    verifyPausedState(viewModel);

    //Playing in stop state while bounded should have no effect
    viewModel.playForwardViewModel.command();
    verifyPausedState(viewModel);

    //Set to unbounded and playing should be okay
    clockViewModel.clockRange = ClockRange.UNBOUNDED;
    viewModel.playForwardViewModel.command();
    verifyForwardState(viewModel);
  });

  it("slower has no effect if at the slowest speed", function () {
    var viewModel = new AnimationViewModel(clockViewModel);
    viewModel.setShuttleRingTicks([0.0, 1.0, 2.0]);
    var slowestMultiplier = -2;
    clockViewModel.multiplier = slowestMultiplier;
    viewModel.slower();
    expect(clockViewModel.multiplier).toEqual(slowestMultiplier);
  });

  it("faster has no effect if at the faster speed", function () {
    var viewModel = new AnimationViewModel(clockViewModel);
    viewModel.setShuttleRingTicks([0.0, 1.0, 2.0]);
    var fastestMultiplier = 2;
    clockViewModel.multiplier = fastestMultiplier;
    viewModel.faster();
    expect(clockViewModel.multiplier).toEqual(fastestMultiplier);
  });

  it("slower and faster cycle through defined multipliers", function () {
    var viewModel = new AnimationViewModel(clockViewModel);

    var i = 0;
    var multipliers = viewModel.getShuttleRingTicks();
    var length = multipliers.length;

    //Start at slowest speed
    clockViewModel.multiplier = multipliers[0];

    //Cycle through them all with faster
    for (i = 1; i < length; i++) {
      viewModel.faster();
      expect(clockViewModel.multiplier).toEqual(multipliers[i]);
    }

    //We should be at the fastest time now.
    expect(clockViewModel.multiplier).toEqual(multipliers[length - 1]);

    //Cycle through them all with slower
    for (i = length - 2; i >= 0; i--) {
      viewModel.slower();
      expect(clockViewModel.multiplier).toEqual(multipliers[i]);
    }

    //We should be at the slowest time now.
    expect(clockViewModel.multiplier).toEqual(multipliers[0]);
  });

  it("Realtime canExecute and tooltip depends on clock settings", function () {
    var viewModel = new AnimationViewModel(clockViewModel);

    //UNBOUNDED but available when start/stop time does not include realtime
    clockViewModel.systemTime = JulianDate.now();
    clockViewModel.clockRange = ClockRange.UNBOUNDED;
    clockViewModel.startTime = JulianDate.addSeconds(
      clockViewModel.systemTime,
      -60,
      new JulianDate()
    );
    clockViewModel.stopTime = JulianDate.addSeconds(
      clockViewModel.systemTime,
      -30,
      new JulianDate()
    );
    expect(viewModel.playRealtimeViewModel.command.canExecute).toEqual(true);
    expect(viewModel.playRealtimeViewModel.tooltip).toEqual(
      "Today (real-time)"
    );

    //CLAMPED but unavailable when start/stop time does not include realtime
    clockViewModel.clockRange = ClockRange.CLAMPED;
    clockViewModel.startTime = JulianDate.addSeconds(
      clockViewModel.systemTime,
      -60,
      new JulianDate()
    );
    clockViewModel.stopTime = JulianDate.addSeconds(
      clockViewModel.systemTime,
      -30,
      new JulianDate()
    );
    expect(viewModel.playRealtimeViewModel.command.canExecute).toEqual(false);
    expect(viewModel.playRealtimeViewModel.tooltip).toEqual(
      "Current time not in range"
    );

    //CLAMPED but available when start/stop time includes realtime
    clockViewModel.clockRange = ClockRange.CLAMPED;
    clockViewModel.startTime = JulianDate.addSeconds(
      clockViewModel.systemTime,
      -60,
      new JulianDate()
    );
    clockViewModel.stopTime = JulianDate.addSeconds(
      clockViewModel.systemTime,
      60,
      new JulianDate()
    );
    expect(viewModel.playRealtimeViewModel.command.canExecute).toEqual(true);
    expect(viewModel.playRealtimeViewModel.tooltip).toEqual(
      "Today (real-time)"
    );

    //LOOP_STOP but unavailable when start/stop time does not include realtime
    clockViewModel.clockRange = ClockRange.LOOP_STOP;
    clockViewModel.startTime = JulianDate.addSeconds(
      clockViewModel.systemTime,
      -60,
      new JulianDate()
    );
    clockViewModel.stopTime = JulianDate.addSeconds(
      clockViewModel.systemTime,
      -30,
      new JulianDate()
    );
    expect(viewModel.playRealtimeViewModel.command.canExecute).toEqual(false);
    expect(viewModel.playRealtimeViewModel.tooltip).toEqual(
      "Current time not in range"
    );

    //LOOP_STOP but available when start/stop time includes realtime
    clockViewModel.clockRange = ClockRange.LOOP_STOP;
    clockViewModel.startTime = JulianDate.addSeconds(
      clockViewModel.systemTime,
      -60,
      new JulianDate()
    );
    clockViewModel.stopTime = JulianDate.addSeconds(
      clockViewModel.systemTime,
      60,
      new JulianDate()
    );
    expect(viewModel.playRealtimeViewModel.command.canExecute).toEqual(true);
    expect(viewModel.playRealtimeViewModel.tooltip).toEqual(
      "Today (real-time)"
    );
  });

  it("User action breaks out of realtime mode", function () {
    var viewModel = new AnimationViewModel(clockViewModel);
    clockViewModel.clockStep = ClockStep.TICK_DEPENDENT;
    clockViewModel.clockRange = ClockRange.UNBOUNDED;

    viewModel.playRealtimeViewModel.command();
    verifyRealtimeState(viewModel);
    expect(clockViewModel.multiplier).toEqual(1);

    //Pausing breaks realtime state
    viewModel.pauseViewModel.command();
    verifyPausedState(viewModel);
    expect(clockViewModel.multiplier).toEqual(1);

    viewModel.playRealtimeViewModel.command();
    verifyRealtimeState(viewModel);

    //Reverse breaks realtime state
    viewModel.playReverseViewModel.command();
    verifyReverseState(viewModel);
    expect(clockViewModel.multiplier).toEqual(-1);

    viewModel.playRealtimeViewModel.command();
    verifyRealtimeState(viewModel);

    //Play does not break realtime state
    viewModel.playForwardViewModel.command();
    verifyRealtimeState(viewModel);
    expect(clockViewModel.multiplier).toEqual(1);

    viewModel.playRealtimeViewModel.command();
    verifyRealtimeState(viewModel);

    //Shuttle ring change breaks realtime state
    viewModel.shuttleRingAngle = viewModel.shuttleRingAngle + 1;
    verifyForwardState(viewModel);
  });

  it("real time mode toggles off but not back on when shouldAnimate changes", function () {
    var viewModel = new AnimationViewModel(clockViewModel);

    viewModel.playRealtimeViewModel.command();
    verifyRealtimeState(viewModel);

    clockViewModel.shouldAnimate = false;
    expect(viewModel.playRealtimeViewModel.toggled).toEqual(false);

    clockViewModel.shouldAnimate = true;
    expect(viewModel.playRealtimeViewModel.toggled).toEqual(false);
  });

  it("Shuttle ring angles set expected multipliers", function () {
    var viewModel = new AnimationViewModel(clockViewModel);

    var shuttleRingTicks = viewModel.getShuttleRingTicks();
    var maxMultiplier = shuttleRingTicks[shuttleRingTicks.length - 1];
    var minMultiplier = -maxMultiplier;

    //Max angle should produce max speed
    viewModel.shuttleRingAngle = AnimationViewModel._maxShuttleRingAngle;
    expect(clockViewModel.multiplier).toEqual(maxMultiplier);

    //Min angle should produce min speed
    viewModel.shuttleRingAngle = -AnimationViewModel._maxShuttleRingAngle;
    expect(clockViewModel.multiplier).toEqual(minMultiplier);

    //AnimationViewModel._realtimeShuttleRingAngle degrees is always 1x
    viewModel.shuttleRingAngle = AnimationViewModel._realtimeShuttleRingAngle;
    expect(clockViewModel.multiplier).toEqual(1);

    viewModel.shuttleRingAngle = -AnimationViewModel._realtimeShuttleRingAngle;
    expect(clockViewModel.multiplier).toEqual(-1);

    //For large values, the shuttleRingAngle should always round to the first two digits.
    viewModel.shuttleRingAngle = 45.0;
    expect(clockViewModel.multiplier).toEqual(85.0);

    viewModel.shuttleRingAngle = -90.0;
    expect(clockViewModel.multiplier).toEqual(-66000.0);

    viewModel.shuttleRingAngle = 0.0;
    expect(clockViewModel.multiplier).toEqual(0.0);
  });

  it("Shuttle ring angles set expected multipliers when snapping to ticks", function () {
    var viewModel = new AnimationViewModel(clockViewModel);
    viewModel.snapToTicks = true;

    var shuttleRingTicks = viewModel.getShuttleRingTicks();
    var maxMultiplier = shuttleRingTicks[shuttleRingTicks.length - 1];
    var minMultiplier = -maxMultiplier;

    //Max angle should produce max speed
    viewModel.shuttleRingAngle = AnimationViewModel._maxShuttleRingAngle;
    expect(clockViewModel.multiplier).toEqual(maxMultiplier);

    //Min angle should produce min speed
    viewModel.shuttleRingAngle = -AnimationViewModel._maxShuttleRingAngle;
    expect(clockViewModel.multiplier).toEqual(minMultiplier);

    //AnimationViewModel._realtimeShuttleRingAngle degrees is always 1x
    viewModel.shuttleRingAngle = AnimationViewModel._realtimeShuttleRingAngle;
    expect(clockViewModel.multiplier).toEqual(1);

    viewModel.shuttleRingAngle = -AnimationViewModel._realtimeShuttleRingAngle;
    expect(clockViewModel.multiplier).toEqual(-1);

    //For large values, the shuttleRingAngle should always round to the first two digits.
    viewModel.shuttleRingAngle = 45.0;
    expect(clockViewModel.multiplier).toEqual(120.0);

    viewModel.shuttleRingAngle = -90.0;
    expect(clockViewModel.multiplier).toEqual(-43200.0);

    viewModel.shuttleRingAngle = 0.0;
    expect(clockViewModel.multiplier).toEqual(
      AnimationViewModel.defaultTicks[0]
    );
  });

  it("throws when constructed without arguments", function () {
    expect(function () {
      return new AnimationViewModel();
    }).toThrowDeveloperError();
  });

  it("setting timeFormatter throws with non-function", function () {
    var animationViewModel = new AnimationViewModel(clockViewModel);
    expect(function () {
      animationViewModel.timeFormatter = {};
    }).toThrowDeveloperError();
  });

  it("setting dateFormatter throws with non-function", function () {
    var animationViewModel = new AnimationViewModel(clockViewModel);
    expect(function () {
      animationViewModel.dateFormatter = {};
    }).toThrowDeveloperError();
  });

  it("setting shuttleRingTicks throws with undefined", function () {
    var animationViewModel = new AnimationViewModel(clockViewModel);
    expect(function () {
      animationViewModel.setShuttleRingTicks(undefined);
    }).toThrowDeveloperError();
  });

  it("returns a copy of shuttleRingTicks when getting", function () {
    var animationViewModel = new AnimationViewModel(clockViewModel);
    var originalTicks = [0.0, 1.0, 2.0];
    animationViewModel.setShuttleRingTicks(originalTicks);

    var ticks = animationViewModel.getShuttleRingTicks();
    ticks.push(99);
    ticks[0] = -99;
    expect(animationViewModel.getShuttleRingTicks()).toEqual(originalTicks);
  });

  it("sorts shuttleRingTicks when setting", function () {
    var animationViewModel = new AnimationViewModel(clockViewModel);
    var ticks = [4.0, 0.0, 8.0, 2.0];

    animationViewModel.setShuttleRingTicks(ticks);
    expect(animationViewModel.getShuttleRingTicks()).toEqual([
      0.0,
      2.0,
      4.0,
      8.0,
    ]);
  });
});
