/*
 * 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 DynamicColorTest : McuTestCase {
	private Hct[] seed_colors = {
		Hct.from_int((int) 0xffff0000u),
		Hct.from_int((int) 0xffffff00u),
		Hct.from_int((int) 0xff00ff00u),
		Hct.from_int((int) 0xff0000ffu)
	};

	private const double[] contrast_levels = { -1.0, -0.5, 0.0, 0.5, 1.0 };

	private HashTable<string, DynamicColor> colors;
	construct {
		colors = new GLib.HashTable<string, DynamicColor> (str_hash, str_equal);

		colors["background"] = MaterialDynamicColors.background();
		colors["on_background"] = MaterialDynamicColors.on_background();
		colors["surface"] = MaterialDynamicColors.surface();
		colors["surface_dim"] = MaterialDynamicColors.surface_dim();
		colors["surface_bright"] = MaterialDynamicColors.surface_bright();
		colors["surface_container_lowest"] = MaterialDynamicColors.surface_container_lowest();
		colors["surface_container_low"] = MaterialDynamicColors.surface_container_low();
		colors["surface_container"] = MaterialDynamicColors.surface_container();
		colors["surface_container_high"] = MaterialDynamicColors.surface_container_high();
		colors["surface_container_highest"] = MaterialDynamicColors.surface_container_highest();
		colors["on_surface"] = MaterialDynamicColors.on_surface();
		colors["surface_variant"] = MaterialDynamicColors.surface_variant();
		colors["on_surface_variant"] = MaterialDynamicColors.on_surface_variant();
		colors["inverse_surface"] = MaterialDynamicColors.inverse_surface();
		colors["inverse_on_surface"] = MaterialDynamicColors.inverse_on_surface();
		colors["outline"] = MaterialDynamicColors.outline();
		colors["outline_variant"] = MaterialDynamicColors.outline_variant();
		colors["shadow"] = MaterialDynamicColors.shadow();
		colors["scrim"] = MaterialDynamicColors.scrim();
		colors["surface_tint"] = MaterialDynamicColors.surface_tint();
		colors["primary"] = MaterialDynamicColors.primary();
		colors["on_primary"] = MaterialDynamicColors.on_primary();
		colors["primary_container"] = MaterialDynamicColors.primary_container();
		colors["on_primary_container"] = MaterialDynamicColors.on_primary_container();
		colors["inverse_primary"] = MaterialDynamicColors.inverse_primary();
		colors["secondary"] = MaterialDynamicColors.secondary();
		colors["on_secondary"] = MaterialDynamicColors.on_secondary();
		colors["secondary_container"] = MaterialDynamicColors.secondary_container();
		colors["on_secondary_container"] = MaterialDynamicColors.on_secondary_container();
		colors["tertiary"] = MaterialDynamicColors.tertiary();
		colors["on_tertiary"] = MaterialDynamicColors.on_tertiary();
		colors["tertiary_container"] = MaterialDynamicColors.tertiary_container();
		colors["on_tertiary_container"] = MaterialDynamicColors.on_tertiary_container();
		colors["error"] = MaterialDynamicColors.error();
		colors["on_error"] = MaterialDynamicColors.on_error();
		colors["error_container"] = MaterialDynamicColors.error_container();
		colors["on_error_container"] = MaterialDynamicColors.on_error_container();
	}

	private const Pair[] text_surface_pairs = {
		{ "on_primary", "primary" },
		{ "on_primary_container", "primary_container" },
		{ "on_secondary", "secondary" },
		{ "on_secondary_container", "secondary_container" },
		{ "on_tertiary", "tertiary" },
		{ "on_tertiary_container", "tertiary_container" },
		{ "on_error", "error" },
		{ "on_error_container", "error_container" },
		{ "on_background", "background" },
		{ "on_surface_variant", "surface_bright" },
		{ "on_surface_variant", "surface_dim" }
	};

	public DynamicColorTest() {
		base("dynamic_color");
		add_test("contrast_pairs", test_contrast_pairs);
		add_test("fixed_colors_in_non_monochrome_schemes", test_fixed_colors_in_non_monochrome_schemes);
		add_test("fixed_colors_in_light_monochrome_schemes", test_fixed_colors_in_light_monochrome_schemes);
		add_test("fixed_colors_in_dark_monochrome_schemes", test_fixed_colors_in_dark_monochrome_schemes);
	}

	public override void set_up() {}

	public void test_contrast_pairs() {
		foreach (var color in seed_colors) {
			foreach (var contrast_level in contrast_levels) {
				foreach (var is_dark in new bool[] { true, false }) {
					foreach (var scheme in new DynamicScheme[] {
						new SchemeContent(color, is_dark, contrast_level),
						new SchemeMonochrome(color, is_dark, contrast_level),
						new SchemeTonalSpot(color, is_dark, contrast_level),
						new SchemeFidelity(color, is_dark, contrast_level)
					}) {
						foreach (var pair in text_surface_pairs) {
							var fg_name = pair.text;
							var bg_name = pair.surface;
							var fg_tone = colors[fg_name].get_hct(scheme).tone;
							var bg_tone = colors[bg_name].get_hct(scheme).tone;
							var contrast = Contrast.ratio_of_tones(fg_tone, bg_tone);

							var minimum_requirement = contrast_level >= 0 ? 4.5 : 3;

							McuAssert.double_greater_than_double(contrast, minimum_requirement, true);
						}
					}
				}
			}
		}
	}

	public void test_fixed_colors_in_non_monochrome_schemes() {
		var scheme = new SchemeTonalSpot(
		                                 Hct.from_int((int) 0xffff0000u),
		                                 true,
		                                 0.0
		);

		McuAssert.doubles_equal(MaterialDynamicColors.primary_fixed().get_hct(scheme).tone, 90, 1.0);
		McuAssert.doubles_equal(MaterialDynamicColors.primary_fixed_dim().get_hct(scheme).tone, 80, 1.0);
		McuAssert.doubles_equal(MaterialDynamicColors.on_primary_fixed().get_hct(scheme).tone, 10, 1.0);
		McuAssert.doubles_equal(MaterialDynamicColors.on_primary_fixed_variant().get_hct(scheme).tone, 30, 1.0);
		McuAssert.doubles_equal(MaterialDynamicColors.secondary_fixed().get_hct(scheme).tone, 90, 1.0);
		McuAssert.doubles_equal(MaterialDynamicColors.secondary_fixed_dim().get_hct(scheme).tone, 80, 1.0);
		McuAssert.doubles_equal(MaterialDynamicColors.on_secondary_fixed().get_hct(scheme).tone, 10, 1.0);
		McuAssert.doubles_equal(MaterialDynamicColors.on_secondary_fixed_variant().get_hct(scheme).tone, 30, 1.0);
		McuAssert.doubles_equal(MaterialDynamicColors.tertiary_fixed().get_hct(scheme).tone, 90, 1.0);
		McuAssert.doubles_equal(MaterialDynamicColors.tertiary_fixed_dim().get_hct(scheme).tone, 80, 1.0);
		McuAssert.doubles_equal(MaterialDynamicColors.on_tertiary_fixed().get_hct(scheme).tone, 10, 1.0);
		McuAssert.doubles_equal(MaterialDynamicColors.on_tertiary_fixed_variant().get_hct(scheme).tone, 30, 1.0);
	}

	public void test_fixed_colors_in_light_monochrome_schemes() {
		var scheme = new SchemeMonochrome(
		                                  Hct.from_int((int) 0xffff0000u),
		                                  false,
		                                  0.0
		);

		McuAssert.doubles_equal(MaterialDynamicColors.primary_fixed().get_hct(scheme).tone, 40, 1.0);
		McuAssert.doubles_equal(MaterialDynamicColors.primary_fixed_dim().get_hct(scheme).tone, 30, 1.0);
		McuAssert.doubles_equal(MaterialDynamicColors.on_primary_fixed().get_hct(scheme).tone, 100, 1.0);
		McuAssert.doubles_equal(MaterialDynamicColors.on_primary_fixed_variant().get_hct(scheme).tone, 90, 1.0);
		McuAssert.doubles_equal(MaterialDynamicColors.secondary_fixed().get_hct(scheme).tone, 80, 1.0);
		McuAssert.doubles_equal(MaterialDynamicColors.secondary_fixed_dim().get_hct(scheme).tone, 70, 1.0);
		McuAssert.doubles_equal(MaterialDynamicColors.on_secondary_fixed().get_hct(scheme).tone, 10, 1.0);
		McuAssert.doubles_equal(MaterialDynamicColors.on_secondary_fixed_variant().get_hct(scheme).tone, 25, 1.0);
		McuAssert.doubles_equal(MaterialDynamicColors.tertiary_fixed().get_hct(scheme).tone, 40, 1.0);
		McuAssert.doubles_equal(MaterialDynamicColors.tertiary_fixed_dim().get_hct(scheme).tone, 30, 1.0);
		McuAssert.doubles_equal(MaterialDynamicColors.on_tertiary_fixed().get_hct(scheme).tone, 100, 1.0);
		McuAssert.doubles_equal(MaterialDynamicColors.on_tertiary_fixed_variant().get_hct(scheme).tone, 90, 1.0);
	}

	public void test_fixed_colors_in_dark_monochrome_schemes() {
		var scheme = new SchemeMonochrome(
		                                  Hct.from_int((int) 0xffff0000u),
		                                  true,
		                                  0.0
		);

		McuAssert.doubles_equal(MaterialDynamicColors.primary_fixed().get_hct(scheme).tone, 40, 1.0);
		McuAssert.doubles_equal(MaterialDynamicColors.primary_fixed_dim().get_hct(scheme).tone, 30, 1.0);
		McuAssert.doubles_equal(MaterialDynamicColors.on_primary_fixed().get_hct(scheme).tone, 100, 1.0);
		McuAssert.doubles_equal(MaterialDynamicColors.on_primary_fixed_variant().get_hct(scheme).tone, 90, 1.0);
		McuAssert.doubles_equal(MaterialDynamicColors.secondary_fixed().get_hct(scheme).tone, 80, 1.0);
		McuAssert.doubles_equal(MaterialDynamicColors.secondary_fixed_dim().get_hct(scheme).tone, 70, 1.0);
		McuAssert.doubles_equal(MaterialDynamicColors.on_secondary_fixed().get_hct(scheme).tone, 10, 1.0);
		McuAssert.doubles_equal(MaterialDynamicColors.on_secondary_fixed_variant().get_hct(scheme).tone, 25, 1.0);
		McuAssert.doubles_equal(MaterialDynamicColors.tertiary_fixed().get_hct(scheme).tone, 40, 1.0);
		McuAssert.doubles_equal(MaterialDynamicColors.tertiary_fixed_dim().get_hct(scheme).tone, 30, 1.0);
		McuAssert.doubles_equal(MaterialDynamicColors.on_tertiary_fixed().get_hct(scheme).tone, 100, 1.0);
		McuAssert.doubles_equal(MaterialDynamicColors.on_tertiary_fixed_variant().get_hct(scheme).tone, 90, 1.0);
	}

	public override void tear_down() {}
}

private struct Pair {
	public string text;
	public string surface;
}
