/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @format
 * @emails oncall+react_native
 */

'use strict';

const buildStyleInterpolator = require('../buildStyleInterpolator');

const validateEmpty = function(interpolator, value, validator) {
  const emptyObject = {};
  let changed = interpolator(emptyObject, value);
  validator(emptyObject);
  expect(changed).toBe(true);
  changed = interpolator(emptyObject, value);
  expect(changed).toBe(false);
};
describe('buildStyleInterpolator', function() {
  it('should linearly interpolate without extrapolating', function() {
    const testAnim = {
      opacity: {
        from: 100,
        to: 200,
        min: 0,
        max: 1,
        type: 'linear',
        extrapolate: false,
      },
      left: {
        from: 200,
        to: 300,
        min: 0,
        max: 1,
        type: 'linear',
        extrapolate: false,
      },
      top: {
        type: 'constant',
        value: 23.5,
      },
    };
    const interpolator = buildStyleInterpolator(testAnim);
    validateEmpty(interpolator, 0, function(res) {
      expect(res).toEqual({
        opacity: 100,
        left: 200,
        top: 23.5,
      });
    });
    validateEmpty(interpolator, 1, function(res) {
      expect(res).toEqual({
        opacity: 200,
        left: 300,
        top: 23.5,
      });
    });
    validateEmpty(interpolator, -0.1, function(res) {
      expect(res).toEqual({
        opacity: 100,
        left: 200,
        top: 23.5,
      });
    });
    validateEmpty(interpolator, 1.1, function(res) {
      expect(res).toEqual({
        opacity: 200,
        left: 300,
        top: 23.5,
      });
    });
    validateEmpty(interpolator, 0.5, function(res) {
      expect(res).toEqual({
        opacity: 150,
        left: 250,
        top: 23.5,
      });
    });
  });
  it('should linearly interpolate with extrapolating', function() {
    const testAnim = {
      opacity: {
        from: 100,
        to: 200,
        min: 0,
        max: 1,
        type: 'linear',
        round: 1, // To make testing easier
        extrapolate: true,
      },
      left: {
        from: 200,
        to: 300,
        min: 0,
        max: 1,
        type: 'linear',
        round: 1, // To make testing easier
        extrapolate: true,
      },
      top: {
        type: 'constant',
        value: 23.5,
      },
    };
    const interpolator = buildStyleInterpolator(testAnim);
    validateEmpty(interpolator, 0, function(res) {
      expect(res).toEqual({
        opacity: 100,
        left: 200,
        top: 23.5,
      });
    });
    validateEmpty(interpolator, 1, function(res) {
      expect(res).toEqual({
        opacity: 200,
        left: 300,
        top: 23.5,
      });
    });
    validateEmpty(interpolator, -0.1, function(res) {
      expect(res).toEqual({
        opacity: 90,
        left: 190,
        top: 23.5,
      });
    });
    validateEmpty(interpolator, 1.1, function(res) {
      expect(res).toEqual({
        opacity: 210,
        left: 310,
        top: 23.5,
      });
    });
    validateEmpty(interpolator, 0.5, function(res) {
      expect(res).toEqual({
        opacity: 150,
        left: 250,
        top: 23.5,
      });
    });
  });
  it('should round accordingly', function() {
    const testAnim = {
      opacity: {
        from: 0,
        to: 1,
        min: 0,
        max: 1,
        type: 'linear',
        round: 2, // As in one over two
        extrapolate: true,
      },
    };
    const interpolator = buildStyleInterpolator(testAnim);
    validateEmpty(interpolator, 0, function(res) {
      expect(res).toEqual({
        opacity: 0,
      });
    });
    validateEmpty(interpolator, 0.5, function(res) {
      expect(res).toEqual({
        opacity: 0.5,
      });
    });
    validateEmpty(interpolator, 0.4, function(res) {
      expect(res).toEqual({
        opacity: 0.5,
      });
    });
    validateEmpty(interpolator, 0.26, function(res) {
      expect(res).toEqual({
        opacity: 0.5,
      });
    });
    validateEmpty(interpolator, 0.74, function(res) {
      expect(res).toEqual({
        opacity: 0.5,
      });
    });
    validateEmpty(interpolator, 0.76, function(res) {
      expect(res).toEqual({
        opacity: 1.0,
      });
    });
  });
  it('should detect changes correctly', function() {
    const testAnim = {
      opacity: {
        from: 0,
        to: 1,
        min: 0,
        max: 1,
        type: 'linear',
        round: 2, // As in one over two
        extrapolate: false,
      },
    };
    const interpolator = buildStyleInterpolator(testAnim);
    const obj = {};
    let res = interpolator(obj, 0);
    expect(obj).toEqual({
      opacity: 0,
    });
    expect(res).toBe(true);

    res = interpolator(obj, 0);
    // No change detected
    expect(obj).toEqual({
      opacity: 0,
    });
    expect(res).toBe(false);

    // No change detected
    res = interpolator(obj, 1);
    expect(obj).toEqual({
      opacity: 1,
    });
    expect(res).toBe(true);

    // Still no change detected even when clipping
    res = interpolator(obj, 1);
    expect(obj).toEqual({
      opacity: 1,
    });
    expect(res).toBe(false);
  });
  it('should handle identity', function() {
    const testAnim = {
      opacity: {
        type: 'identity',
      },
    };
    const interpolator = buildStyleInterpolator(testAnim);
    const obj = {};
    let res = interpolator(obj, 0.5);
    expect(obj).toEqual({
      opacity: 0.5,
    });
    expect(res).toBe(true);

    res = interpolator(obj, 0.5);
    // No change detected
    expect(obj).toEqual({
      opacity: 0.5,
    });
    expect(res).toBe(false);
  });
  it('should translate', function() {
    const testAnim = {
      transformTranslate: {
        from: {x: 1, y: 10, z: 100},
        to: {x: 5, y: 50, z: 500},
        min: 0,
        max: 4,
        type: 'linear',
      },
    };
    const interpolator = buildStyleInterpolator(testAnim);
    const obj = {};
    const res = interpolator(obj, 1);
    expect(obj).toEqual({
      transform: [
        {
          matrix: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 2, 20, 200, 1],
        },
      ],
    });
    expect(res).toBe(true);
  });
  it('should scale', function() {
    const testAnim = {
      transformScale: {
        from: {x: 1, y: 10, z: 100},
        to: {x: 5, y: 50, z: 500},
        min: 0,
        max: 4,
        type: 'linear',
      },
    };
    const interpolator = buildStyleInterpolator(testAnim);
    const obj = {};
    const res = interpolator(obj, 1);
    expect(obj).toEqual({
      transform: [
        {
          matrix: [2, 0, 0, 0, 0, 20, 0, 0, 0, 0, 200, 0, 0, 0, 0, 1],
        },
      ],
    });
    expect(res).toBe(true);
  });
  it('should combine scale and translate', function() {
    const testAnim = {
      transformScale: {
        from: {x: 1, y: 10, z: 100},
        to: {x: 5, y: 50, z: 500},
        min: 0,
        max: 4,
        type: 'linear',
      },
      transformTranslate: {
        from: {x: 1, y: 10, z: 100},
        to: {x: 5, y: 50, z: 500},
        min: 0,
        max: 4,
        type: 'linear',
      },
    };
    const interpolator = buildStyleInterpolator(testAnim);
    const obj = {};
    const res = interpolator(obj, 1);
    expect(obj).toEqual({
      transform: [
        {
          matrix: [2, 0, 0, 0, 0, 20, 0, 0, 0, 0, 200, 0, 4, 400, 40000, 1],
        },
      ],
    });
    expect(res).toBe(true);
  });
  it('should step', function() {
    const testAnim = {
      opacity: {
        threshold: 13,
        from: 10,
        to: 20,
        type: 'step',
      },
    };
    const interpolator = buildStyleInterpolator(testAnim);
    const obj = {};
    let res = interpolator(obj, 0);
    expect(obj).toEqual({
      opacity: 10,
    });
    expect(res).toBe(true);

    res = interpolator(obj, 0);
    // No change detected
    expect(obj).toEqual({
      opacity: 10,
    });
    expect(res).toBe(false);

    // No change detected
    res = interpolator(obj, 10);
    expect(obj).toEqual({
      opacity: 10,
    });
    expect(res).toBe(false);

    // No change detected
    res = interpolator(obj, 12);
    expect(obj).toEqual({
      opacity: 10,
    });
    expect(res).toBe(false);

    // No change detected
    res = interpolator(obj, 13);
    expect(obj).toEqual({
      opacity: 20,
    });
    expect(res).toBe(true);

    // No change detected
    res = interpolator(obj, 13.1);
    expect(obj).toEqual({
      opacity: 20,
    });
    expect(res).toBe(false);

    // No change detected
    res = interpolator(obj, 25);
    expect(obj).toEqual({
      opacity: 20,
    });
    expect(res).toBe(false);
  });
});
