import {
  zoomViewportIn,
  zoomViewportOut,
  canZoomViewportIn,
  canZoomViewportOut,
} from './zoom';

// zf is the zoom factor.
const zf = 2;
const emptySelection = { start: 0, end: 0 };

describe('zoomViewportIn', () => {
  describe('when viewport start and end is equal', () => {
    it('returns the same viewport', () => {
      const newViewport = zoomViewportIn(
        { start: 100, end: 100 },
        500,
        emptySelection,
        0,
        zf
      );

      expect(newViewport).toEqual({ start: 100, end: 100 });
    });
  });

  describe('with nothing selected', () => {
    it('centres the zoom on the playback position if possible', () => {
      const newViewport = zoomViewportIn(
        { start: 100_000, end: 200_000 },
        500_000,
        emptySelection,
        50_000,
        zf
      );
      expect(newViewport).toEqual({ start: 25_000, end: 75_000 });
    });

    it('offsets the new viewport if it overlaps the viewport minimum', () => {
      const newViewport = zoomViewportIn(
        { start: 100_000, end: 200_000 },
        500_000,
        emptySelection,
        0,
        zf
      );
      expect(newViewport).toEqual({ start: 0, end: 50_000 });
    });

    it('offsets the new viewport if it overlaps the viewport maximum', () => {
      const newViewport = zoomViewportIn(
        { start: 100_000, end: 200_000 },
        500_000,
        emptySelection,
        490_000,
        zf
      );
      expect(newViewport).toEqual({ start: 450_000, end: 500_000 });
    });
  });

  describe('with an active selection', () => {
    it('centres the new viewport on the selection if possible', () => {
      const newViewport = zoomViewportIn(
        { start: 100_000, end: 200_000 },
        500_000,
        { start: 120_000, end: 140_000 },
        0,
        zf
      );

      expect(newViewport).toEqual({ start: 105_000, end: 155_000 });
    });

    it('offsets the new viewport if it overlaps the viewport minimum', () => {
      const newViewport = zoomViewportIn(
        { start: 100_000, end: 200_000 },
        500_000,
        { start: 10_000, end: 20_000 },
        0,
        zf
      );

      expect(newViewport).toEqual({ start: 0, end: 50_000 });
    });

    it('offsets the new viewport if it overlaps the viewport maximum', () => {
      const newViewport = zoomViewportIn(
        { start: 100_000, end: 200_000 },
        500_000,
        { start: 480_000, end: 490_000 },
        0,
        zf
      );

      expect(newViewport).toEqual({ start: 450_000, end: 500_000 });
    });

    describe('when zooming beyond the selection', () => {
      it('disallows the zoom', () => {
        const newViewport = zoomViewportIn(
          { start: 100_000, end: 200_000 },
          500_000,
          { start: 110_000, end: 190_000 },
          0,
          zf
        );

        expect(newViewport).toEqual({ start: 100_000, end: 200_000 });
      });
    });
  });
});

describe('zoomViewportOut', () => {
  describe('when viewport start and end is equal', () => {
    it('returns the same viewport', () => {
      const newViewport = zoomViewportOut(
        { start: 100, end: 100 },
        500,
        emptySelection,
        0,
        zf
      );

      expect(newViewport).toEqual({ start: 100, end: 100 });
    });
  });

  describe('with nothing selected', () => {
    it('centres the zoom on the playback position if possible', () => {
      const newViewport = zoomViewportOut(
        { start: 190_000, end: 210_000 },
        500_000,
        emptySelection,
        170_000,
        zf
      );
      expect(newViewport).toEqual({ start: 150_000, end: 190_000 });
    });

    it('offsets the new viewport if it overlaps the viewport minimum', () => {
      const newViewport = zoomViewportOut(
        { start: 190_000, end: 210_000 },
        500_000,
        emptySelection,
        10_000,
        zf
      );
      expect(newViewport).toEqual({ start: 0, end: 40_000 });
    });

    it('offsets the new viewport if it overlaps the viewport maximum', () => {
      const newViewport = zoomViewportOut(
        { start: 190_000, end: 210_000 },
        500_000,
        emptySelection,
        485_000,
        zf
      );

      expect(newViewport).toEqual({ start: 460_000, end: 500_000 });
    });

    it('refuses to zoom out beyond the available limits', () => {
      const newViewport = zoomViewportOut(
        { start: 10_000, end: 490_000 },
        500_000,
        emptySelection,
        200_000,
        zf
      );

      expect(newViewport).toEqual({ start: 0, end: 500_000 });
    });
  });

  describe('with an active selection', () => {
    it('centres the new viewport on the selection if possible', () => {
      const newViewport = zoomViewportOut(
        { start: 150_000, end: 170_000 },
        500_000,
        { start: 120_000, end: 140_000 },
        0,
        zf
      );

      expect(newViewport).toEqual({ start: 110_000, end: 150_000 });
    });

    it('offsets the new viewport if it overlaps the viewport minimum', () => {
      const newViewport = zoomViewportOut(
        { start: 190_000, end: 210_000 },
        500_000,
        { start: 10_000, end: 20_000 },
        10_000,
        zf
      );

      expect(newViewport).toEqual({ start: 0, end: 40_000 });
    });

    it('offsets the new viewport if it overlaps the viewport minimum', () => {
      const newViewport = zoomViewportOut(
        { start: 190_000, end: 210_000 },
        500_000,
        { start: 495_000, end: 500_000 },
        0,
        zf
      );

      expect(newViewport).toEqual({ start: 460_000, end: 500_000 });
    });

    it('refuses to zoom out beyond the available limits', () => {
      const newViewport = zoomViewportOut(
        { start: 10_000, end: 490_000 },
        500_000,
        { start: 20_000, end: 480_000 },
        0,
        zf
      );

      expect(newViewport).toEqual({ start: 0, end: 500_000 });
    });
  });
});

describe('canZoomViewportIn', () => {
  it('does now allow zooming when the viewport is inactive', () => {
    const result = canZoomViewportIn(
      { start: 0, end: 0 },
      { start: 0, end: 0 },
      zf
    );
    expect(result).toBeFalsy();
  });

  it('does not allow zooming past the selection', () => {
    const result = canZoomViewportIn(
      { start: 1000, end: 2000 },
      { start: 1100, end: 1900 },
      zf
    );
    expect(result).toBeFalsy();
  });

  it('allows zooming', () => {
    const result = canZoomViewportIn(
      { start: 1000, end: 2000 },
      { start: 0, end: 100 },
      zf
    );
    expect(result).toBeTruthy();
  });
});

describe('canZoomViewportOut', () => {
  it('does now allow zooming when the viewport is inactive', () => {
    const result = canZoomViewportOut({ start: 0, end: 0 }, 1000);
    expect(result).toBeFalsy();
  });

  it('does now allow zooming when already at maximum zoom', () => {
    const result = canZoomViewportOut({ start: 0, end: 1000 }, 1000);
    expect(result).toBeFalsy();
  });

  it('allows zooming', () => {
    const result = canZoomViewportOut({ start: 1000, end: 2000 }, 5000);
    expect(result).toBeTruthy();
  });
});
