import {expectToMatchColor} from '../../test/lib/util';
import interpolate, {isSupportedInterpolationColorSpace} from './interpolate';
import Color from './color';
import Padding from './padding';
import VariableAnchorOffsetCollection from './variable_anchor_offset_collection';

describe('interpolate', () => {

    test('interpolate number', () => {
        expect(interpolate.number(-5, 5, 0.00)).toBe(-5.0);
        expect(interpolate.number(-5, 5, 0.25)).toBe(-2.5);
        expect(interpolate.number(-5, 5, 0.50)).toBe(0);
        expect(interpolate.number(-5, 5, 0.75)).toBe(2.5);
        expect(interpolate.number(-5, 5, 1.00)).toBe(5.0);

        expect(interpolate.number(0, 1, 0.5)).toBe(0.5);
        expect(interpolate.number(-10, -5, 0.5)).toBe(-7.5);
        expect(interpolate.number(5, 10, 0.5)).toBe(7.5);
    });

    describe('interpolation color space', () => {

        test('should recognize supported interpolation color spaces', () => {
            expect(isSupportedInterpolationColorSpace('rgb')).toBe(true);
            expect(isSupportedInterpolationColorSpace('hcl')).toBe(true);
            expect(isSupportedInterpolationColorSpace('lab')).toBe(true);
        });

        test('should ignore invalid interpolation color spaces', () => {
            expect(isSupportedInterpolationColorSpace('sRGB')).toBe(false);
            expect(isSupportedInterpolationColorSpace('HCL')).toBe(false);
            expect(isSupportedInterpolationColorSpace('LCH')).toBe(false);
            expect(isSupportedInterpolationColorSpace('LAB')).toBe(false);
            expect(isSupportedInterpolationColorSpace('interpolate')).toBe(false);
            expect(isSupportedInterpolationColorSpace('interpolate-hcl')).toBe(false);
            expect(isSupportedInterpolationColorSpace('interpolate-lab')).toBe(false);
        });

    });

    describe('interpolate color', () => {

        test('should interpolate colors in "rgb" color space', () => {
            const color = Color.parse('rgba(0,0,255,1)');
            const targetColor = Color.parse('rgba(0,255,0,.6)');

            const i11nFn = (t: number) => interpolate.color(color, targetColor, t, 'rgb');
            expectToMatchColor(i11nFn(0.00), 'rgb(0% 0% 100% / 1)');
            expectToMatchColor(i11nFn(0.25), 'rgb(0% 25% 75% / 0.9)');
            expectToMatchColor(i11nFn(0.50), 'rgb(0% 50% 50% / 0.8)');
            expectToMatchColor(i11nFn(0.75), 'rgb(0% 75% 25% / 0.7)');
            expectToMatchColor(i11nFn(1.00), 'rgb(0% 100% 0% / 0.6)');
        });

        test('should interpolate colors in "hcl" color space', () => {
            const color = Color.parse('rgba(0,0,255,1)');
            const targetColor = Color.parse('rgba(0,255,0,.6)');

            const i11nFn = (t: number) => interpolate.color(color, targetColor, t, 'hcl');
            expectToMatchColor(i11nFn(0.00), 'rgb(0% 0% 100% / 1)');
            expectToMatchColor(i11nFn(0.25), 'rgb(0% 49.37% 100% / 0.9)', 4);
            expectToMatchColor(i11nFn(0.50), 'rgb(0% 70.44% 100% / 0.8)', 4);
            expectToMatchColor(i11nFn(0.75), 'rgb(0% 87.54% 63.18% / 0.7)', 4);
            expectToMatchColor(i11nFn(1.00), 'rgb(0% 100% 0% / 0.6)');
        });

        test('should interpolate colors in "lab" color space', () => {
            const color = Color.parse('rgba(0,0,255,1)');
            const targetColor = Color.parse('rgba(0,255,0,.6)');

            const i11nFn = (t: number) => interpolate.color(color, targetColor, t, 'lab');
            expectToMatchColor(i11nFn(0.00), 'rgb(0% 0% 100% / 1)');
            expectToMatchColor(i11nFn(0.25), 'rgb(39.64% 34.55% 83.36% / 0.9)', 4);
            expectToMatchColor(i11nFn(0.50), 'rgb(46.42% 56.82% 65.91% / 0.8)', 4);
            expectToMatchColor(i11nFn(0.75), 'rgb(41.45% 78.34% 45.62% / 0.7)', 4);
            expectToMatchColor(i11nFn(1.00), 'rgb(0% 100% 0% / 0.6)');
        });

        test('should correctly interpolate colors with alpha=0', () => {
            const color = Color.parse('rgba(0,0,255,0)');
            const targetColor = Color.parse('rgba(0,255,0,1)');

            const i11nFn = (t: number) => interpolate.color(color, targetColor, t, 'rgb');
            expectToMatchColor(i11nFn(0.00), 'rgb(0% 0% 0% / 0)');
            expectToMatchColor(i11nFn(0.25), 'rgb(0% 25% 75% / 0.25)');
            expectToMatchColor(i11nFn(0.50), 'rgb(0% 50% 50% / 0.5)');
            expectToMatchColor(i11nFn(0.75), 'rgb(0% 75% 25% / 0.75)');
            expectToMatchColor(i11nFn(1.00), 'rgb(0% 100% 0% / 1)');
        });

        test('should limit interpolation results to sRGB gamut', () => {
            const color = Color.parse('royalblue');
            const targetColor = Color.parse('cyan');

            for (const space of ['rgb', 'hcl', 'lab'] as const) {
                const i11nFn = (t: number) => interpolate.color(color, targetColor, t, space);
                const colorInBetween = i11nFn(0.5);
                for (const key of ['r', 'g', 'b', 'a'] as const) {
                    expect(colorInBetween[ key ]).toBeGreaterThanOrEqual(0);
                    expect(colorInBetween[ key ]).toBeLessThanOrEqual(1);
                }
            }
        });

    });

    test('interpolate array', () => {
        expect(interpolate.array([0, 0, 0, 0], [1, 2, 3, 4], 0.5)).toEqual([0.5, 1, 3 / 2, 2]);
    });

    test('interpolate padding', () => {
        const padding = new Padding([0, 0, 0, 0]);
        const targetPadding = new Padding([1, 2, 6, 4]);

        const i11nFn = (t: number) => interpolate.padding(padding, targetPadding, t);
        expect(i11nFn(0.5)).toBeInstanceOf(Padding);
        expect(i11nFn(0.5)).toEqual(new Padding([0.5, 1, 3, 2]));
    });

    describe('interpolate variableAnchorOffsetCollection', () => {
        const i11nFn = interpolate.variableAnchorOffsetCollection;
        const parseFn = VariableAnchorOffsetCollection.parse;

        test('should throw with mismatched endpoints', () => {
            expect(() => i11nFn(parseFn(['top', [0, 0]]), parseFn(['bottom', [1, 1]]), 0.5)).toThrow('Cannot interpolate values containing mismatched anchors. from[0]: top, to[0]: bottom');
            expect(() => i11nFn(parseFn(['top', [0, 0]]), parseFn(['top', [1, 1], 'bottom', [2, 2]]), 0.5)).toThrow('Cannot interpolate values of different length. from: ["top",[0,0]], to: ["top",[1,1],"bottom",[2,2]]');
        });

        test('should interpolate offsets', () => {
            expect(i11nFn(parseFn(['top', [0, 0], 'bottom', [2, 2]]), parseFn(['top', [1, 1], 'bottom', [4, 4]]), 0.5).values).toEqual(['top', [0.5, 0.5], 'bottom', [3, 3]]);
        });
    });

});
