/*
 * Copyright 2021 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * ---------------------------------------------------------------------
 *
 * Portions of this file were modified and ported to Vala by Victor Vintorez in 2026.
 * These modifications are licensed under the European Union Public License (EUPL),
 * Version 1.2 or later. You may obtain a copy of the License at:
 * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
 */

using GLib;
using MaterialColorUtilities;

public class MathUtilsTest : McuTestCase {
	public MathUtilsTest () {
		base ("math_utils");
		add_test ("signum", test_signum);
		add_test ("lerp", test_lerp);
		add_test ("clamp_int", test_clamp_int);
		add_test ("clamp_double", test_clamp_double);
		add_test ("sanitize_degrees_int", test_sanitize_degrees_int);
		add_test ("sanitize_degrees_double", test_sanitize_degrees_double);
		add_test ("rotation_direction", test_rotation_direction);
		add_test ("difference_degrees", test_difference_degrees);
		add_test ("matrix_multiply", test_matrix_multiply);
	}

	public override void set_up () {}

	public void test_signum () {
		var result_1 = MathUtils.signum (-2.0);
		var result_2 = MathUtils.signum (0.0);
		var result_3 = MathUtils.signum (2.0);

		McuAssert.doubles_equal (result_1, -1.0);
		McuAssert.doubles_equal (result_2, 0.0);
		McuAssert.doubles_equal (result_3, 1.0);
	}

	public void test_lerp () {
		var result_1 = MathUtils.lerp (0, 1, 0.5);
		var result_2 = MathUtils.lerp (0, 1, 0.2);
		var result_3 = MathUtils.lerp (0, 1, 0.8);
		var result_4 = MathUtils.lerp (0, 100, 0.5);

		McuAssert.doubles_equal (result_1, 0.5);
		McuAssert.doubles_equal (result_2, 0.2);
		McuAssert.doubles_equal (result_3, 0.8);
		McuAssert.doubles_equal (result_4, 50.0);
	}

	public void test_clamp_int () {
		var result_1 = MathUtils.clamp_int (0, 2, -1);
		var result_2 = MathUtils.clamp_int (0, 2, 1);
		var result_3 = MathUtils.clamp_int (0, 2, 3);

		McuAssert.ints_equal (result_1, 0);
		McuAssert.ints_equal (result_2, 1);
		McuAssert.ints_equal (result_3, 2);
	}

	public void test_clamp_double () {
		var result_1 = MathUtils.clamp_double (0.0, 2.0, -1.0);
		var result_2 = MathUtils.clamp_double (0.0, 2.0, 1.0);
		var result_3 = MathUtils.clamp_double (0.0, 2.0, 3.0);

		McuAssert.doubles_equal (result_1, 0.0);
		McuAssert.doubles_equal (result_2, 1.0);
		McuAssert.doubles_equal (result_3, 2.0);
	}

	public void test_sanitize_degrees_int () {
		var result_1 = MathUtils.sanitize_degrees_int (20);
		var result_2 = MathUtils.sanitize_degrees_int (360);
		var result_3 = MathUtils.sanitize_degrees_int (420);

		McuAssert.ints_equal (result_1, 20);
		McuAssert.ints_equal (result_2, 0);
		McuAssert.ints_equal (result_3, 60);
	}

	public void test_sanitize_degrees_double () {
		var result_1 = MathUtils.sanitize_degrees_double (20.0);
		var result_2 = MathUtils.sanitize_degrees_double (360.0);
		var result_3 = MathUtils.sanitize_degrees_double (420.0);

		McuAssert.doubles_equal (result_1, 20.0);
		McuAssert.doubles_equal (result_2, 0.0);
		McuAssert.doubles_equal (result_3, 60.0);
	}

	public void test_rotation_direction () {
		var from = 0.0;
		while (from < 360.0) {
			var to = 7.5;
			while (to < 360.0) {
				var expected = rotation_direction (from, to);
				var answer = MathUtils.rotation_direction (from, to);

				McuAssert.doubles_equal (answer, expected);
				McuAssert.doubles_equal (Math.fabs (answer), 1);

				to += 15;
			}
			from += 15;
		}
	}

	public void test_difference_degrees () {
		var result_0 = MathUtils.difference_degrees (274.6219605304299, 77.72547409861208);
		var result_1 = MathUtils.difference_degrees (34.300891775453685, 345.71661212068005);
		var result_2 = MathUtils.difference_degrees (133.40137028102632, 242.91571888782715);
		var result_3 = MathUtils.difference_degrees (190.0917641906071, 212.50303144714914);
		var result_4 = MathUtils.difference_degrees (74.32650812140363, 8.216795031646305);
		var result_5 = MathUtils.difference_degrees (297.86357401167163, 40.68646508824719);
		var result_6 = MathUtils.difference_degrees (267.81615364209966, 278.2059089562928);
		var result_7 = MathUtils.difference_degrees (174.5904980548994, 23.563620558167703);
		var result_8 = MathUtils.difference_degrees (157.88312470562747, 136.9620280065816);
		var result_9 = MathUtils.difference_degrees (125.57678704737181, 232.55663167025088);
		var result_10 = MathUtils.difference_degrees (29.516420072682976, 282.618422595264);

		McuAssert.doubles_equal (result_0, 163.10351356818222);
		McuAssert.doubles_equal (result_1, 48.58427965477364);
		McuAssert.doubles_equal (result_2, 109.51434860680084);
		McuAssert.doubles_equal (result_3, 22.411267256542033);
		McuAssert.doubles_equal (result_4, 66.10971308975734);
		McuAssert.doubles_equal (result_5, 102.82289107657556);
		McuAssert.doubles_equal (result_6, 10.38975531419311);
		McuAssert.doubles_equal (result_7, 151.0268774967317);
		McuAssert.doubles_equal (result_8, 20.921096699045876);
		McuAssert.doubles_equal (result_9, 106.97984462287907);
		McuAssert.doubles_equal (result_10, 106.89799747741898);
	}

	public void test_matrix_multiply () {
		var result_1 = MathUtils.matrix_multiply (
		                                          new double[] { 0.0, 1.0, 2.0 },
		                                          new double[, ] {
			{ 1.0, 2.0, 3.0 },
			{ 2.0, 3.0, 4.0 },
			{ 4.0, 5.0, 6.0 }
		});
		var result_2 = MathUtils.matrix_multiply (
		                                          new double[] { 3.0, 4.0, 5.0 },
		                                          new double[, ] {
			{ 3.0, 7.0, 1.0 },
			{ 5.0, 8.0, 2.0 },
			{ 6.0, 9.0, 3.0 }
		});

		McuAssert.doubles_equal (result_1[0], 8.0);
		McuAssert.doubles_equal (result_1[1], 11.0);
		McuAssert.doubles_equal (result_1[2], 17.0);

		McuAssert.doubles_equal (result_2[0], 42.0);
		McuAssert.doubles_equal (result_2[1], 57.0);
		McuAssert.doubles_equal (result_2[2], 69.0);
	}

	public override void tear_down () {}

	private double rotation_direction (double from, double to) {
		var a = to - from;
		var b = to - from + 360;
		var c = to - from - 360;
		var a_abs = Math.fabs (a);
		var b_abs = Math.fabs (b);
		var c_abs = Math.fabs (c);
		if (a_abs <= b_abs && a_abs <= c_abs) {
			return a >= 0 ? 1 : -1;
		} else if (b_abs <= a_abs && b_abs <= c_abs) {
			return b >= 0 ? 1 : -1;
		} else {
			return c >= 0 ? 1 : -1;
		}
	}
}
