/*
 * 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;

namespace MaterialColorUtilities {
	/** {@link ColorSpec} implementation for the 2025 spec. */
	public class ColorSpec2025 : ColorSpec2021 {

		////////////////////////////////////////////////////////////////
		// Surfaces [S]                                               //
		////////////////////////////////////////////////////////////////

		public override DynamicColor background {
			owned get {
				// Remapped to surface for 2025 spec.
				DynamicColor color_2025 = surface
				     .to_builder()
				     .set_name("background")
				     .build();
				return base.background
				        .to_builder()
				        .extend_spec_version(SpecVersion.SPEC_2025, color_2025)
				        .build();
			}
		}

		public override DynamicColor on_background {
			owned get {
				// Remapped to on_surface for 2025 spec.
				DynamicColor color_2025 = on_surface
				     .to_builder()
				     .set_name("on_background")
				     .set_tone((s) => s.platform == Platform.WATCH ? 100.0 : on_surface.get_tone(s))
				     .build();
				return base.on_background
				        .to_builder()
				        .extend_spec_version(SpecVersion.SPEC_2025, color_2025)
				        .build();
			}
		}

		public override DynamicColor surface {
			owned get {
				DynamicColor color_2025 = new DynamicColorBuilder()
				     .set_name("surface")
				     .set_palette((s) => s.neutral_palette)
				     .set_tone((s) => {
					if (s.platform == Platform.PHONE) {
						if (s.is_dark) {
							return 4.0;
						} else {
							if (Hct.is_yellow(s.neutral_palette.hue)) {
								return 99.0;
							} else if (s.variant == VIBRANT) {
								return 97.0;
							} else {
								return 98.0;
							}
						}
					} else {
						return 0.0;
					}
				})
				     .set_is_background(true)
				     .build();
				return base.surface
				        .to_builder()
				        .extend_spec_version(SpecVersion.SPEC_2025, color_2025)
				        .build();
			}
		}

		public override DynamicColor surface_dim {
			owned get {
				DynamicColor color_2025 = new DynamicColorBuilder()
				     .set_name("surface_dim")
				     .set_palette((s) => s.neutral_palette)
				     .set_tone((s) => {
					if (s.is_dark) {
						return 4.0;
					} else {
						if (Hct.is_yellow(s.neutral_palette.hue)) {
							return 90.0;
						} else if (s.variant == VIBRANT) {
							return 85.0;
						} else {
							return 87.0;
						}
					}
				})
				     .set_is_background(true)
				     .set_chroma_multiplier((s) => {
					if (!s.is_dark) {
						if (s.variant == NEUTRAL) {
							return 2.5;
						} else if (s.variant == TONAL_SPOT) {
							return 1.7;
						} else if (s.variant == EXPRESSIVE) {
							return Hct.is_yellow(s.neutral_palette.hue) ? 2.7 : 1.75;
						} else if (s.variant == VIBRANT) {
							return 1.36;
						}
					}
					return 1.0;
				})
				     .build();
				return base.surface_dim
				        .to_builder()
				        .extend_spec_version(SpecVersion.SPEC_2025, color_2025)
				        .build();
			}
		}

		public override DynamicColor surface_bright {
			owned get {
				DynamicColor color_2025 = new DynamicColorBuilder()
				     .set_name("surface_bright")
				     .set_palette((s) => s.neutral_palette)
				     .set_tone((s) => {
					if (s.is_dark) {
						return 18.0;
					} else {
						if (Hct.is_yellow(s.neutral_palette.hue)) {
							return 99.0;
						} else if (s.variant == VIBRANT) {
							return 97.0;
						} else {
							return 98.0;
						}
					}
				})
				     .set_is_background(true)
				     .set_chroma_multiplier((s) => {
					if (s.is_dark) {
						if (s.variant == NEUTRAL) {
							return 2.5;
						} else if (s.variant == TONAL_SPOT) {
							return 1.7;
						} else if (s.variant == EXPRESSIVE) {
							return Hct.is_yellow(s.neutral_palette.hue) ? 2.7 : 1.75;
						} else if (s.variant == VIBRANT) {
							return 1.36;
						}
					}
					return 1.0;
				})
				     .build();
				return base.surface_bright
				        .to_builder()
				        .extend_spec_version(SpecVersion.SPEC_2025, color_2025)
				        .build();
			}
		}

		public override DynamicColor surface_container_lowest {
			owned get {
				DynamicColor color_2025 = new DynamicColorBuilder()
				     .set_name("surface_container_lowest")
				     .set_palette((s) => s.neutral_palette)
				     .set_tone((s) => s.is_dark ? 0.0 : 100.0)
				     .set_is_background(true)
				     .build();
				return base.surface_container_lowest
				        .to_builder()
				        .extend_spec_version(SpecVersion.SPEC_2025, color_2025)
				        .build();
			}
		}

		public override DynamicColor surface_container_low {
			owned get {
				DynamicColor color_2025 = new DynamicColorBuilder()
				     .set_name("surface_container_low")
				     .set_palette((s) => s.neutral_palette)
				     .set_tone((s) => {
					if (s.platform == Platform.PHONE) {
						if (s.is_dark) {
							return 6.0;
						} else {
							if (Hct.is_yellow(s.neutral_palette.hue)) {
								return 98.0;
							} else if (s.variant == VIBRANT) {
								return 95.0;
							} else {
								return 96.0;
							}
						}
					} else {
						return 15.0;
					}
				})
				     .set_is_background(true)
				     .set_chroma_multiplier((s) => {
					if (s.platform == Platform.PHONE) {
						if (s.variant == NEUTRAL) {
							return 1.3;
						} else if (s.variant == TONAL_SPOT) {
							return 1.25;
						} else if (s.variant == EXPRESSIVE) {
							return Hct.is_yellow(s.neutral_palette.hue) ? 1.3 : 1.15;
						} else if (s.variant == VIBRANT) {
							return 1.08;
						}
					}
					return 1.0;
				})
				     .build();
				return base.surface_container_low
				        .to_builder()
				        .extend_spec_version(SpecVersion.SPEC_2025, color_2025)
				        .build();
			}
		}

		public override DynamicColor surface_container {
			owned get {
				DynamicColor color_2025 = new DynamicColorBuilder()
				     .set_name("surface_container")
				     .set_palette((s) => s.neutral_palette)
				     .set_tone((s) => {
					if (s.platform == Platform.PHONE) {
						if (s.is_dark) {
							return 9.0;
						} else {
							if (Hct.is_yellow(s.neutral_palette.hue)) {
								return 96.0;
							} else if (s.variant == VIBRANT) {
								return 92.0;
							} else {
								return 94.0;
							}
						}
					} else {
						return 20.0;
					}
				})
				     .set_is_background(true)
				     .set_chroma_multiplier((s) => {
					if (s.platform == Platform.PHONE) {
						if (s.variant == NEUTRAL) {
							return 1.6;
						} else if (s.variant == TONAL_SPOT) {
							return 1.4;
						} else if (s.variant == EXPRESSIVE) {
							return Hct.is_yellow(s.neutral_palette.hue) ? 1.6 : 1.3;
						} else if (s.variant == VIBRANT) {
							return 1.15;
						}
					}
					return 1.0;
				})
				     .build();
				return base.surface_container
				        .to_builder()
				        .extend_spec_version(SpecVersion.SPEC_2025, color_2025)
				        .build();
			}
		}

		public override DynamicColor surface_container_high {
			owned get {
				DynamicColor color_2025 = new DynamicColorBuilder()
				     .set_name("surface_container_high")
				     .set_palette((s) => s.neutral_palette)
				     .set_tone((s) => {
					if (s.platform == Platform.PHONE) {
						if (s.is_dark) {
							return 12.0;
						} else {
							if (Hct.is_yellow(s.neutral_palette.hue)) {
								return 94.0;
							} else if (s.variant == VIBRANT) {
								return 90.0;
							} else {
								return 92.0;
							}
						}
					} else {
						return 25.0;
					}
				})
				     .set_is_background(true)
				     .set_chroma_multiplier((s) => {
					if (s.platform == Platform.PHONE) {
						if (s.variant == NEUTRAL) {
							return 1.9;
						} else if (s.variant == TONAL_SPOT) {
							return 1.5;
						} else if (s.variant == EXPRESSIVE) {
							return Hct.is_yellow(s.neutral_palette.hue) ? 1.95 : 1.45;
						} else if (s.variant == VIBRANT) {
							return 1.22;
						}
					}
					return 1.0;
				})
				     .build();
				return base.surface_container_high
				        .to_builder()
				        .extend_spec_version(SpecVersion.SPEC_2025, color_2025)
				        .build();
			}
		}

		public override DynamicColor surface_container_highest {
			owned get {
				DynamicColor color_2025 = new DynamicColorBuilder()
				     .set_name("surface_container_highest")
				     .set_palette((s) => s.neutral_palette)
				     .set_tone((s) => {
					if (s.is_dark) {
						return 15.0;
					} else {
						if (Hct.is_yellow(s.neutral_palette.hue)) {
							return 92.0;
						} else if (s.variant == VIBRANT) {
							return 88.0;
						} else {
							return 90.0;
						}
					}
				})
				     .set_is_background(true)
				     .set_chroma_multiplier((s) => {
					if (s.variant == NEUTRAL) {
						return 2.2;
					} else if (s.variant == TONAL_SPOT) {
						return 1.7;
					} else if (s.variant == EXPRESSIVE) {
						return Hct.is_yellow(s.neutral_palette.hue) ? 2.3 : 1.6;
					} else if (s.variant == VIBRANT) {
						return 1.29;
					}
					return 1.0;
				})
				     .build();
				return base.surface_container_highest
				        .to_builder()
				        .extend_spec_version(SpecVersion.SPEC_2025, color_2025)
				        .build();
			}
		}

		public override DynamicColor on_surface {
			owned get {
				DynamicColor color_2025 = new DynamicColorBuilder()
				     .set_name("on_surface")
				     .set_palette((s) => s.neutral_palette)
				     .set_tone((s) => {
					if (s.variant == Variant.VIBRANT) {
						return t_max_c(s.neutral_palette, 0, 100, 1.1);
					} else {
						return DynamicColor.get_initial_tone_from_background((scheme) => {
							if (scheme.platform == Platform.PHONE) {
								return scheme.is_dark ? surface_bright : surface_dim;
							} else {
								return surface_container_high;
							}
						}) (s);
					}
				})
				     .set_chroma_multiplier((s) => {
					if (s.platform == Platform.PHONE) {
						if (s.variant == NEUTRAL) {
							return 2.2;
						} else if (s.variant == TONAL_SPOT) {
							return 1.7;
						} else if (s.variant == EXPRESSIVE) {
							return Hct.is_yellow(s.neutral_palette.hue) ? (s.is_dark ? 3.0 : 2.3) : 1.6;
						}
					}
					return 1.0;
				})
				     .set_background((s) => {
					if (s.platform == Platform.PHONE) {
						return s.is_dark ? surface_bright : surface_dim;
					} else {
						return surface_container_high;
					}
				})
				     .set_contrast_curve((s) => s.is_dark && s.platform == Platform.PHONE ? get_contrast_curve(11) : get_contrast_curve(9))
				     .build();
				return base.on_surface
				        .to_builder()
				        .extend_spec_version(SpecVersion.SPEC_2025, color_2025)
				        .build();
			}
		}

		public override DynamicColor surface_variant {
			owned get {
				// Remapped to surface_container_highest for 2025 spec.
				DynamicColor color_2025 = surface_container_highest
				     .to_builder()
				     .set_name("surface_variant")
				     .build();
				return base.surface_variant
				        .to_builder()
				        .extend_spec_version(SpecVersion.SPEC_2025, color_2025)
				        .build();
			}
		}

		public override DynamicColor on_surface_variant {
			owned get {
				DynamicColor color_2025 = new DynamicColorBuilder()
				     .set_name("on_surface_variant")
				     .set_palette((s) => s.neutral_palette)
				     .set_chroma_multiplier((s) => {
					if (s.platform == Platform.PHONE) {
						if (s.variant == NEUTRAL) {
							return 2.2;
						} else if (s.variant == TONAL_SPOT) {
							return 1.7;
						} else if (s.variant == EXPRESSIVE) {
							return Hct.is_yellow(s.neutral_palette.hue) ? (s.is_dark ? 3.0 : 2.3) : 1.6;
						}
					}
					return 1.0;
				})
				     .set_background((s) => {
					if (s.platform == Platform.PHONE) {
						return s.is_dark ? surface_bright : surface_dim;
					} else {
						return surface_container_high;
					}
				})
				     .set_contrast_curve((s) => s.platform == Platform.PHONE ? (s.is_dark ? get_contrast_curve(6) : get_contrast_curve(4.5)) : get_contrast_curve(7))
				     .build();
				return base.on_surface_variant
				        .to_builder()
				        .extend_spec_version(SpecVersion.SPEC_2025, color_2025)
				        .build();
			}
		}

		public override DynamicColor inverse_surface {
			owned get {
				DynamicColor color_2025 = new DynamicColorBuilder()
				     .set_name("inverse_surface")
				     .set_palette((s) => s.neutral_palette)
				     .set_tone((s) => s.is_dark ? 98.0 : 4.0)
				     .set_is_background(true)
				     .build();
				return base.inverse_surface
				        .to_builder()
				        .extend_spec_version(SpecVersion.SPEC_2025, color_2025)
				        .build();
			}
		}

		public override DynamicColor inverse_on_surface {
			owned get {
				DynamicColor color_2025 = new DynamicColorBuilder()
				     .set_name("inverse_on_surface")
				     .set_palette((s) => s.neutral_palette)
				     .set_background((s) => inverse_surface)
				     .set_contrast_curve((s) => get_contrast_curve(7))
				     .build();
				return base.inverse_on_surface
				        .to_builder()
				        .extend_spec_version(SpecVersion.SPEC_2025, color_2025)
				        .build();
			}
		}

		public override DynamicColor outline {
			owned get {
				DynamicColor color_2025 = new DynamicColorBuilder()
				     .set_name("outline")
				     .set_palette((s) => s.neutral_palette)
				     .set_chroma_multiplier((s) => {
					if (s.platform == Platform.PHONE) {
						if (s.variant == NEUTRAL) {
							return 2.2;
						} else if (s.variant == TONAL_SPOT) {
							return 1.7;
						} else if (s.variant == EXPRESSIVE) {
							return Hct.is_yellow(s.neutral_palette.hue) ? (s.is_dark ? 3.0 : 2.3) : 1.6;
						}
					}
					return 1.0;
				})
				     .set_background((s) => {
					if (s.platform == Platform.PHONE) {
						return s.is_dark ? surface_bright : surface_dim;
					} else {
						return surface_container_high;
					}
				})
				     .set_contrast_curve((s) => s.platform == Platform.PHONE ? get_contrast_curve(3) : get_contrast_curve(4.5))
				     .build();
				return base.outline
				        .to_builder()
				        .extend_spec_version(SpecVersion.SPEC_2025, color_2025).build();
			}
		}

		public override DynamicColor outline_variant {
			owned get {
				DynamicColor color_2025 = new DynamicColorBuilder()
				     .set_name("outline_variant")
				     .set_palette((s) => s.neutral_palette)
				     .set_chroma_multiplier((s) => {
					if (s.platform == Platform.PHONE) {
						if (s.variant == NEUTRAL) {
							return 2.2;
						} else if (s.variant == TONAL_SPOT) {
							return 1.7;
						} else if (s.variant == EXPRESSIVE) {
							return Hct.is_yellow(s.neutral_palette.hue) ? (s.is_dark ? 3.0 : 2.3) : 1.6;
						}
					}
					return 1.0;
				})
				     .set_background((s) => {
					if (s.platform == Platform.PHONE) {
						return s.is_dark ? surface_bright : surface_dim;
					} else {
						return surface_container_high;
					}
				})
				     .set_contrast_curve((s) => s.platform == Platform.PHONE ? get_contrast_curve(1.5) : get_contrast_curve(3))
				     .build();
				return base.outline_variant
				        .to_builder()
				        .extend_spec_version(SpecVersion.SPEC_2025, color_2025)
				        .build();
			}
		}

		public override DynamicColor surface_tint {
			owned get {
				// Remapped to primary for 2025 spec.
				DynamicColor color_2025 = primary
				     .to_builder()
				     .set_name("surface_tint")
				     .build();
				return base.surface_tint
				        .to_builder()
				        .extend_spec_version(SpecVersion.SPEC_2025, color_2025)
				        .build();
			}
		}

		////////////////////////////////////////////////////////////////
		// Primaries [P]                                              //
		////////////////////////////////////////////////////////////////

		public override DynamicColor primary {
			owned get {
				DynamicColor color_2025 = new DynamicColorBuilder()
				     .set_name("primary")
				     .set_palette((s) => s.primary_palette)
				     .set_tone((s) => {
					if (s.variant == NEUTRAL) {
						if (s.platform == Platform.PHONE) {
							return s.is_dark ? 80.0 : 40.0;
						} else {
							return 90.0;
						}
					} else if (s.variant == TONAL_SPOT) {
						if (s.platform == Platform.PHONE) {
							if (s.is_dark) {
								return 80.0;
							} else {
								return t_max_c(s.primary_palette);
							}
						} else {
							return t_max_c(s.primary_palette, 0, 90);
						}
					} else if (s.variant == EXPRESSIVE) {
						if (s.platform == Platform.PHONE) {
							return t_max_c(s.primary_palette, 0, Hct.is_yellow(s.primary_palette.hue) ? 25 : Hct.is_cyan(s.primary_palette.hue) ? 88 : 98);
						} else {
							return t_max_c(s.primary_palette);
						}
					} else {
						if (s.platform == Platform.PHONE) {
							return t_max_c(s.primary_palette, 0, Hct.is_cyan(s.primary_palette.hue) ? 88 : 98);
						} else {
							return t_max_c(s.primary_palette);
						}
					}
				})
				     .set_is_background(true)
				     .set_background((s) => {
					if (s.platform == Platform.PHONE) {
						return s.is_dark ? surface_bright : surface_dim;
					} else {
						return surface_container_high;
					}
				})
				     .set_contrast_curve((s) => s.platform == Platform.PHONE ? get_contrast_curve(4.5) : get_contrast_curve(7))
				     .set_tone_delta_pair((s) => s.platform == Platform.PHONE ? new ToneDeltaPair(primary_container, primary, 5.0, TonePolarity.RELATIVE_LIGHTER, true, DeltaConstraint.FARTHER) : null)
				     .build();
				return base.primary
				        .to_builder()
				        .extend_spec_version(SpecVersion.SPEC_2025, color_2025)
				        .build();
			}
		}

		public override DynamicColor? primary_dim {
			owned get {
				return new DynamicColorBuilder()
				        .set_name("primary_dim")
				        .set_palette((s) => s.primary_palette)
				        .set_tone((s) => {
					if (s.variant == NEUTRAL) {
						return 85.0;
					} else if (s.variant == TONAL_SPOT) {
						return t_max_c(s.primary_palette, 0, 90);
					} else {
						return t_max_c(s.primary_palette);
					}
				})
				        .set_is_background(true)
				        .set_background((s) => surface_container_high)
				        .set_contrast_curve((s) => get_contrast_curve(4.5))
				        .set_tone_delta_pair((s) => new ToneDeltaPair(primary_dim, primary, 5.0, TonePolarity.DARKER, true, DeltaConstraint.FARTHER))
				        .build();
			}
		}

		public override DynamicColor on_primary {
			owned get {
				DynamicColor color_2025 = new DynamicColorBuilder()
				     .set_name("on_primary")
				     .set_palette((s) => s.primary_palette)
				     .set_background((s) => s.platform == Platform.PHONE ? primary : primary_dim)
				     .set_contrast_curve((s) => s.platform == Platform.PHONE ? get_contrast_curve(6) : get_contrast_curve(7))
				     .build();
				return base.on_primary
				        .to_builder()
				        .extend_spec_version(SpecVersion.SPEC_2025, color_2025)
				        .build();
			}
		}

		public override DynamicColor primary_container {
			owned get {
				DynamicColor color_2025 = new DynamicColorBuilder()
				     .set_name("primary_container")
				     .set_palette((s) => s.primary_palette)
				     .set_tone((s) => {
					if (s.platform == Platform.WATCH) {
						return 30.0;
					} else if (s.variant == NEUTRAL) {
						return s.is_dark ? 30.0 : 90.0;
					} else if (s.variant == TONAL_SPOT) {
						return s.is_dark ? t_min_c(s.primary_palette, 35, 93) : t_max_c(s.primary_palette, 0, 90);
					} else if (s.variant == EXPRESSIVE) {
						return s.is_dark ? t_max_c(s.primary_palette, 30, 93) : t_max_c(s.primary_palette, 78, Hct.is_cyan(s.primary_palette.hue) ? 88 : 90);
					} else {
						return s.is_dark ? t_min_c(s.primary_palette, 66, 93) : t_max_c(s.primary_palette, 66, Hct.is_cyan(s.primary_palette.hue) ? 88 : 93);
					}
				})
				     .set_is_background(true)
				     .set_background((s) => {
					if (s.platform == Platform.PHONE) {
						return s.is_dark ? surface_bright : surface_dim;
					} else {
						return null;
					}
				})
				     .set_tone_delta_pair((s) => s.platform == Platform.WATCH ? new ToneDeltaPair(primary_container, primary_dim, 10.0, TonePolarity.DARKER, true, DeltaConstraint.FARTHER) : null)
				     .set_contrast_curve((s) => s.platform == Platform.PHONE && s.contrast_level > 0 ? get_contrast_curve(1.5) : null)
				     .build();
				return base.primary_container
				        .to_builder()
				        .extend_spec_version(SpecVersion.SPEC_2025, color_2025)
				        .build();
			}
		}

		public override DynamicColor on_primary_container {
			owned get {
				DynamicColor color_2025 = new DynamicColorBuilder()
				     .set_name("on_primary_container")
				     .set_palette((s) => s.primary_palette)
				     .set_background((s) => primary_container)
				     .set_contrast_curve((s) => s.platform == Platform.PHONE ? get_contrast_curve(6) : get_contrast_curve(7))
				     .build();
				return base.on_primary_container
				        .to_builder()
				        .extend_spec_version(SpecVersion.SPEC_2025, color_2025)
				        .build();
			}
		}

		public override DynamicColor inverse_primary {
			owned get {
				DynamicColor color_2025 = new DynamicColorBuilder()
				     .set_name("inverse_primary")
				     .set_palette((s) => s.primary_palette)
				     .set_tone((s) => t_max_c(s.primary_palette))
				     .set_background((s) => inverse_surface)
				     .set_contrast_curve((s) => s.platform == Platform.PHONE ? get_contrast_curve(6) : get_contrast_curve(7))
				     .build();
				return base.inverse_primary
				        .to_builder()
				        .extend_spec_version(SpecVersion.SPEC_2025, color_2025)
				        .build();
			}
		}

		////////////////////////////////////////////////////////////////
		// Secondaries [Q]                                            //
		////////////////////////////////////////////////////////////////

		public override DynamicColor secondary {
			owned get {
				DynamicColor color_2025 = new DynamicColorBuilder()
				     .set_name("secondary")
				     .set_palette((s) => s.secondary_palette)
				     .set_tone((s) => {
					if (s.platform == Platform.WATCH) {
						return s.variant == NEUTRAL ? 90.0 : t_max_c(s.secondary_palette, 0, 90);
					} else if (s.variant == NEUTRAL) {
						return s.is_dark ? t_min_c(s.secondary_palette, 0, 98) : t_max_c(s.secondary_palette);
					} else if (s.variant == VIBRANT) {
						return t_max_c(s.secondary_palette, 0, s.is_dark ? 90 : 98);
					} else {
						return s.is_dark ? 80.0 : t_max_c(s.secondary_palette);
					}
				})
				     .set_is_background(true)
				     .set_background((s) => {
					if (s.platform == Platform.PHONE) {
						return s.is_dark ? surface_bright : surface_dim;
					} else {
						return surface_container_high;
					}
				})
				     .set_contrast_curve((s) => s.platform == Platform.PHONE ? get_contrast_curve(4.5) : get_contrast_curve(7))
				     .set_tone_delta_pair((s) => s.platform == Platform.PHONE ? new ToneDeltaPair(secondary_container, secondary, 5.0, TonePolarity.RELATIVE_LIGHTER, true, DeltaConstraint.FARTHER) : null)
				     .build();
				return base.secondary
				        .to_builder()
				        .extend_spec_version(SpecVersion.SPEC_2025, color_2025)
				        .build();
			}
		}

		public override DynamicColor? secondary_dim {
			owned get {
				return new DynamicColorBuilder()
				        .set_name("secondary_dim")
				        .set_palette((s) => s.secondary_palette)
				        .set_tone((s) => {
					if (s.variant == NEUTRAL) {
						return 85.0;
					} else {
						return t_max_c(s.secondary_palette, 0, 90);
					}
				})
				        .set_is_background(true)
				        .set_background((s) => surface_container_high)
				        .set_contrast_curve((s) => get_contrast_curve(4.5))
				        .set_tone_delta_pair((s) => new ToneDeltaPair(secondary_dim, secondary, 5.0, TonePolarity.DARKER, true, DeltaConstraint.FARTHER))
				        .build();
			}
		}

		public override DynamicColor on_secondary {
			owned get {
				DynamicColor color_2025 = new DynamicColorBuilder()
				     .set_name("on_secondary")
				     .set_palette((s) => s.secondary_palette)
				     .set_background((s) => s.platform == Platform.PHONE ? secondary : secondary_dim)
				     .set_contrast_curve((s) => s.platform == Platform.PHONE ? get_contrast_curve(6) : get_contrast_curve(7))
				     .build();
				return base.on_secondary
				        .to_builder()
				        .extend_spec_version(SpecVersion.SPEC_2025, color_2025)
				        .build();
			}
		}

		public override DynamicColor secondary_container {
			owned get {
				DynamicColor color_2025 = new DynamicColorBuilder()
				     .set_name("secondary_container")
				     .set_palette((s) => s.secondary_palette)
				     .set_tone((s) => {
					if (s.platform == Platform.WATCH) {
						return 30.0;
					} else if (s.variant == VIBRANT) {
						return s.is_dark ? t_min_c(s.secondary_palette, 30, 40) : t_max_c(s.secondary_palette, 84, 90);
					} else if (s.variant == EXPRESSIVE) {
						return s.is_dark ? 15.0 : t_max_c(s.secondary_palette, 90, 95);
					} else {
						return s.is_dark ? 25.0 : 90.0;
					}
				})
				     .set_is_background(true)
				     .set_background((s) => {
					if (s.platform == Platform.PHONE) {
						return s.is_dark ? surface_bright : surface_dim;
					} else {
						return null;
					}
				})
				     .set_tone_delta_pair((s) => s.platform == Platform.WATCH ? new ToneDeltaPair(secondary_container, secondary_dim, 10.0, TonePolarity.DARKER, true, DeltaConstraint.FARTHER) : null)
				     .set_contrast_curve((s) => s.platform == Platform.PHONE && s.contrast_level > 0 ? get_contrast_curve(1.5) : null)
				     .build();
				return base.secondary_container
				        .to_builder()
				        .extend_spec_version(SpecVersion.SPEC_2025, color_2025)
				        .build();
			}
		}

		public override DynamicColor on_secondary_container {
			owned get {
				DynamicColor color_2025 = new DynamicColorBuilder()
				     .set_name("on_secondary_container")
				     .set_palette((s) => s.secondary_palette)
				     .set_background((s) => secondary_container)
				     .set_contrast_curve((s) => s.platform == Platform.PHONE ? get_contrast_curve(6) : get_contrast_curve(7))
				     .build();
				return base.on_secondary_container
				        .to_builder()
				        .extend_spec_version(SpecVersion.SPEC_2025, color_2025)
				        .build();
			}
		}

		////////////////////////////////////////////////////////////////
		// Tertiaries [T]                                             //
		////////////////////////////////////////////////////////////////

		public override DynamicColor tertiary {
			owned get {
				DynamicColor color_2025 = new DynamicColorBuilder()
				     .set_name("tertiary")
				     .set_palette((s) => s.tertiary_palette)
				     .set_tone((s) => {
					if (s.platform == Platform.WATCH) {
						return s.variant == TONAL_SPOT ? t_max_c(s.tertiary_palette, 0, 90) : t_max_c(s.tertiary_palette);
					} else if (s.variant == EXPRESSIVE || s.variant == VIBRANT) {
						return t_max_c(s.tertiary_palette, 0, Hct.is_cyan(s.tertiary_palette.hue) ? 88 : (s.is_dark ? 98 : 100));
					} else {
						return s.is_dark ? t_max_c(s.tertiary_palette, 0, 98) : t_max_c(s.tertiary_palette);
					}
				})
				     .set_is_background(true)
				     .set_background((s) => {
					if (s.platform == Platform.PHONE) {
						return s.is_dark ? surface_bright : surface_dim;
					} else {
						return surface_container_high;
					}
				})
				     .set_contrast_curve((s) => s.platform == Platform.PHONE ? get_contrast_curve(4.5) : get_contrast_curve(7))
				     .set_tone_delta_pair((s) => s.platform == Platform.PHONE ? new ToneDeltaPair(tertiary_container, tertiary, 5.0, TonePolarity.RELATIVE_LIGHTER, true, DeltaConstraint.FARTHER) : null)
				     .build();
				return base.tertiary
				        .to_builder()
				        .extend_spec_version(SpecVersion.SPEC_2025, color_2025)
				        .build();
			}
		}

		public override DynamicColor? tertiary_dim {
			owned get {
				return new DynamicColorBuilder()
				        .set_name("tertiary_dim")
				        .set_palette((s) => s.tertiary_palette)
				        .set_tone((s) => {
					if (s.variant == TONAL_SPOT) {
						return t_max_c(s.tertiary_palette, 0, 90);
					} else {
						return t_max_c(s.tertiary_palette);
					}
				})
				        .set_is_background(true)
				        .set_background((s) => surface_container_high)
				        .set_contrast_curve((s) => get_contrast_curve(4.5))
				        .set_tone_delta_pair((s) => new ToneDeltaPair(tertiary_dim, tertiary, 5.0, TonePolarity.DARKER, true, DeltaConstraint.FARTHER))
				        .build();
			}
		}

		public override DynamicColor on_tertiary {
			owned get {
				DynamicColor color_2025 = new DynamicColorBuilder()
				     .set_name("on_tertiary")
				     .set_palette((s) => s.tertiary_palette)
				     .set_background((s) => s.platform == Platform.PHONE ? tertiary : tertiary_dim)
				     .set_contrast_curve((s) => s.platform == Platform.PHONE ? get_contrast_curve(6) : get_contrast_curve(7))
				     .build();
				return base.on_tertiary
				        .to_builder()
				        .extend_spec_version(SpecVersion.SPEC_2025, color_2025)
				        .build();
			}
		}

		public override DynamicColor tertiary_container {
			owned get {
				DynamicColor color_2025 = new DynamicColorBuilder()
				     .set_name("tertiary_container")
				     .set_palette((s) => s.tertiary_palette)
				     .set_tone((s) => {
					if (s.platform == Platform.WATCH) {
						return s.variant == TONAL_SPOT ? t_max_c(s.tertiary_palette, 0, 90) : t_max_c(s.tertiary_palette);
					} else {
						if (s.variant == NEUTRAL) {
							return s.is_dark ? t_max_c(s.tertiary_palette, 0, 93) : t_max_c(s.tertiary_palette, 0, 96);
						} else if (s.variant == TONAL_SPOT) {
							return t_max_c(s.tertiary_palette, 0, s.is_dark ? 93 : 100);
						} else if (s.variant == EXPRESSIVE) {
							return t_max_c(s.tertiary_palette, 75, Hct.is_cyan(s.tertiary_palette.hue) ? 88 : (s.is_dark ? 93 : 100));
						} else {
							return s.is_dark ? t_max_c(s.tertiary_palette, 0, 93) : t_max_c(s.tertiary_palette, 72, 100);
						}
					}
				})
				     .set_is_background(true)
				     .set_background((s) => {
					if (s.platform == Platform.PHONE) {
						return s.is_dark ? surface_bright : surface_dim;
					} else {
						return null;
					}
				})
				     .set_tone_delta_pair((s) => s.platform == Platform.WATCH ? new ToneDeltaPair(tertiary_container, tertiary_dim, 10.0, TonePolarity.DARKER, true, DeltaConstraint.FARTHER) : null)
				     .set_contrast_curve((s) => s.platform == Platform.PHONE && s.contrast_level > 0 ? get_contrast_curve(1.5) : null)
				     .build();
				return base.tertiary_container
				        .to_builder()
				        .extend_spec_version(SpecVersion.SPEC_2025, color_2025)
				        .build();
			}
		}

		public override DynamicColor on_tertiary_container {
			owned get {
				DynamicColor color_2025 = new DynamicColorBuilder()
				     .set_name("on_tertiary_container")
				     .set_palette((s) => s.tertiary_palette)
				     .set_background((s) => tertiary_container)
				     .set_contrast_curve((s) => s.platform == Platform.PHONE ? get_contrast_curve(6) : get_contrast_curve(7))
				     .build();
				return base.on_tertiary_container
				        .to_builder()
				        .extend_spec_version(SpecVersion.SPEC_2025, color_2025)
				        .build();
			}
		}

		////////////////////////////////////////////////////////////////
		// Errors [E]                                                 //
		////////////////////////////////////////////////////////////////

		public override DynamicColor error {
			owned get {
				DynamicColor color_2025 = new DynamicColorBuilder()
				     .set_name("error")
				     .set_palette((s) => s.error_palette)
				     .set_tone((s) => {
					if (s.platform == Platform.PHONE) {
						return s.is_dark ? t_min_c(s.error_palette, 0, 98) : t_max_c(s.error_palette);
					} else {
						return t_min_c(s.error_palette);
					}
				})
				     .set_is_background(true)
				     .set_background((s) => {
					if (s.platform == Platform.PHONE) {
						return s.is_dark ? surface_bright : surface_dim;
					} else {
						return surface_container_high;
					}
				})
				     .set_contrast_curve((s) => s.platform == Platform.PHONE ? get_contrast_curve(4.5) : get_contrast_curve(7))
				     .set_tone_delta_pair((s) => s.platform == Platform.PHONE ? new ToneDeltaPair(error_container, error, 5.0, TonePolarity.RELATIVE_LIGHTER, true, DeltaConstraint.FARTHER) : null)
				     .build();
				return base.error
				        .to_builder()
				        .extend_spec_version(SpecVersion.SPEC_2025, color_2025)
				        .build();
			}
		}

		public override DynamicColor? error_dim {
			owned get {
				return new DynamicColorBuilder()
				        .set_name("error_dim")
				        .set_palette((s) => s.error_palette)
				        .set_tone((s) => t_min_c(s.error_palette))
				        .set_is_background(true)
				        .set_background((s) => surface_container_high)
				        .set_contrast_curve((s) => get_contrast_curve(4.5))
				        .set_tone_delta_pair((s) => new ToneDeltaPair(error_dim, error, 5.0, TonePolarity.DARKER, true, DeltaConstraint.FARTHER))
				        .build();
			}
		}

		public override DynamicColor on_error {
			owned get {
				DynamicColor color_2025 = new DynamicColorBuilder()
				     .set_name("on_error")
				     .set_palette((s) => s.error_palette)
				     .set_background((s) => s.platform == Platform.PHONE ? error : error_dim)
				     .set_contrast_curve((s) => s.platform == Platform.PHONE ? get_contrast_curve(6) : get_contrast_curve(7))
				     .build();
				return base.on_error
				        .to_builder()
				        .extend_spec_version(SpecVersion.SPEC_2025, color_2025)
				        .build();
			}
		}

		public override DynamicColor error_container {
			owned get {
				DynamicColor color_2025 = new DynamicColorBuilder()
				     .set_name("error_container")
				     .set_palette((s) => s.error_palette)
				     .set_tone((s) => {
					if (s.platform == Platform.WATCH) {
						return 30.0;
					} else {
						return s.is_dark ? t_min_c(s.error_palette, 30, 93) : t_max_c(s.error_palette, 0, 90);
					}
				})
				     .set_is_background(true)
				     .set_background((s) => {
					if (s.platform == Platform.PHONE) {
						return s.is_dark ? surface_bright : surface_dim;
					} else {
						return null;
					}
				})
				     .set_tone_delta_pair((s) => s.platform == Platform.WATCH ? new ToneDeltaPair(error_container, error_dim, 10.0, TonePolarity.DARKER, true, DeltaConstraint.FARTHER) : null)
				     .set_contrast_curve((s) => s.platform == Platform.PHONE && s.contrast_level > 0 ? get_contrast_curve(1.5) : null)
				     .build();
				return base.error_container
				        .to_builder()
				        .extend_spec_version(SpecVersion.SPEC_2025, color_2025)
				        .build();
			}
		}

		public override DynamicColor on_error_container {
			owned get {
				DynamicColor color_2025 = new DynamicColorBuilder()
				     .set_name("on_error_container")
				     .set_palette((s) => s.error_palette)
				     .set_background((s) => error_container)
				     .set_contrast_curve((s) => s.platform == Platform.PHONE ? get_contrast_curve(4.5) : get_contrast_curve(7))
				     .build();
				return base.on_error_container
				        .to_builder()
				        .extend_spec_version(SpecVersion.SPEC_2025, color_2025)
				        .build();
			}
		}

		////////////////////////////////////////////////////////////////
		// Primary Fixed Colors [PF]                                  //
		////////////////////////////////////////////////////////////////

		public override DynamicColor primary_fixed {
			owned get {
				DynamicColor color_2025 = new DynamicColorBuilder()
				     .set_name("primary_fixed")
				     .set_palette((s) => s.primary_palette)
				     .set_tone((s) => {
					DynamicScheme temp_s = DynamicScheme.from(s, false, 0.0);
					return primary_container.get_tone(temp_s);
				})
				     .set_is_background(true)
				     .set_background((s) => {
					if (s.platform == Platform.PHONE) {
						return s.is_dark ? surface_bright : surface_dim;
					} else {
						return null;
					}
				})
				     .set_contrast_curve((s) => s.platform == Platform.PHONE && s.contrast_level > 0 ? get_contrast_curve(1.5) : null)
				     .build();
				return base.primary_fixed
				        .to_builder()
				        .extend_spec_version(SpecVersion.SPEC_2025, color_2025)
				        .build();
			}
		}

		public override DynamicColor primary_fixed_dim {
			owned get {
				DynamicColor color_2025 = new DynamicColorBuilder()
				     .set_name("primary_fixed_dim")
				     .set_palette((s) => s.primary_palette)
				     .set_tone((s) => primary_fixed.get_tone(s))
				     .set_is_background(true)
				     .set_tone_delta_pair((s) => new ToneDeltaPair(primary_fixed_dim, primary_fixed, 5.0, TonePolarity.DARKER, true, DeltaConstraint.EXACT))
				     .build();
				return base.primary_fixed_dim
				        .to_builder()
				        .extend_spec_version(SpecVersion.SPEC_2025, color_2025)
				        .build();
			}
		}

		public override DynamicColor on_primary_fixed {
			owned get {
				DynamicColor color_2025 = new DynamicColorBuilder()
				     .set_name("on_primary_fixed")
				     .set_palette((s) => s.primary_palette)
				     .set_background((s) => primary_fixed_dim)
				     .set_contrast_curve((s) => get_contrast_curve(7))
				     .build();
				return base.on_primary_fixed
				        .to_builder()
				        .extend_spec_version(SpecVersion.SPEC_2025, color_2025)
				        .build();
			}
		}

		public override DynamicColor on_primary_fixed_variant {
			owned get {
				DynamicColor color_2025 = new DynamicColorBuilder()
				     .set_name("on_primary_fixed_variant")
				     .set_palette((s) => s.primary_palette)
				     .set_background((s) => primary_fixed_dim)
				     .set_contrast_curve((s) => get_contrast_curve(4.5))
				     .build();
				return base.on_primary_fixed_variant
				        .to_builder()
				        .extend_spec_version(SpecVersion.SPEC_2025, color_2025)
				        .build();
			}
		}

		////////////////////////////////////////////////////////////////
		// Secondary Fixed Colors [QF]                                //
		////////////////////////////////////////////////////////////////

		public override DynamicColor secondary_fixed {
			owned get {
				DynamicColor color_2025 = new DynamicColorBuilder()
				     .set_name("secondary_fixed")
				     .set_palette((s) => s.secondary_palette)
				     .set_tone((s) => {
					DynamicScheme temp_s = DynamicScheme.from(s, false, 0.0);
					return secondary_container.get_tone(temp_s);
				})
				     .set_is_background(true)
				     .set_background((s) => {
					if (s.platform == Platform.PHONE) {
						return s.is_dark ? surface_bright : surface_dim;
					} else {
						return null;
					}
				})
				     .set_contrast_curve((s) => s.platform == Platform.PHONE && s.contrast_level > 0 ? get_contrast_curve(1.5) : null)
				     .build();
				return base.secondary_fixed
				        .to_builder()
				        .extend_spec_version(SpecVersion.SPEC_2025, color_2025)
				        .build();
			}
		}

		public override DynamicColor secondary_fixed_dim {
			owned get {
				DynamicColor color_2025 = new DynamicColorBuilder()
				     .set_name("secondary_fixed_dim")
				     .set_palette((s) => s.secondary_palette)
				     .set_tone((s) => secondary_fixed.get_tone(s))
				     .set_is_background(true)
				     .set_tone_delta_pair((s) => new ToneDeltaPair(secondary_fixed_dim, secondary_fixed, 5.0, TonePolarity.DARKER, true, DeltaConstraint.EXACT))
				     .build();
				return base.secondary_fixed_dim
				        .to_builder()
				        .extend_spec_version(SpecVersion.SPEC_2025, color_2025)
				        .build();
			}
		}

		public override DynamicColor on_secondary_fixed {
			owned get {
				DynamicColor color_2025 = new DynamicColorBuilder()
				     .set_name("on_secondary_fixed")
				     .set_palette((s) => s.secondary_palette)
				     .set_background((s) => secondary_fixed_dim)
				     .set_contrast_curve((s) => get_contrast_curve(7))
				     .build();
				return base.on_secondary_fixed
				        .to_builder()
				        .extend_spec_version(SpecVersion.SPEC_2025, color_2025)
				        .build();
			}
		}

		public override DynamicColor on_secondary_fixed_variant {
			owned get {
				DynamicColor color_2025 = new DynamicColorBuilder()
				     .set_name("on_secondary_fixed_variant")
				     .set_palette((s) => s.secondary_palette)
				     .set_background((s) => secondary_fixed_dim)
				     .set_contrast_curve((s) => get_contrast_curve(4.5))
				     .build();
				return base.on_secondary_fixed_variant
				        .to_builder()
				        .extend_spec_version(SpecVersion.SPEC_2025, color_2025)
				        .build();
			}
		}

		////////////////////////////////////////////////////////////////
		// Tertiary Fixed Colors [TF]                                 //
		////////////////////////////////////////////////////////////////

		public override DynamicColor tertiary_fixed {
			owned get {
				DynamicColor color_2025 = new DynamicColorBuilder()
				     .set_name("tertiary_fixed")
				     .set_palette((s) => s.tertiary_palette)
				     .set_tone((s) => {
					DynamicScheme temp_s = DynamicScheme.from(s, false, 0.0);
					return tertiary_container.get_tone(temp_s);
				})
				     .set_is_background(true)
				     .set_background((s) => {
					if (s.platform == Platform.PHONE) {
						return s.is_dark ? surface_bright : surface_dim;
					} else {
						return null;
					}
				})
				     .set_contrast_curve((s) => s.platform == Platform.PHONE && s.contrast_level > 0 ? get_contrast_curve(1.5) : null)
				     .build();
				return base.tertiary_fixed
				        .to_builder()
				        .extend_spec_version(SpecVersion.SPEC_2025, color_2025)
				        .build();
			}
		}

		public override DynamicColor tertiary_fixed_dim {
			owned get {
				DynamicColor color_2025 = new DynamicColorBuilder()
				     .set_name("tertiary_fixed_dim")
				     .set_palette((s) => s.tertiary_palette)
				     .set_tone((s) => tertiary_fixed.get_tone(s))
				     .set_is_background(true)
				     .set_tone_delta_pair((s) => new ToneDeltaPair(tertiary_fixed_dim, tertiary_fixed, 5.0, TonePolarity.DARKER, true, DeltaConstraint.EXACT))
				     .build();
				return base.tertiary_fixed_dim
				        .to_builder()
				        .extend_spec_version(SpecVersion.SPEC_2025, color_2025)
				        .build();
			}
		}

		public override DynamicColor on_tertiary_fixed {
			owned get {
				DynamicColor color_2025 = new DynamicColorBuilder()
				     .set_name("on_tertiary_fixed")
				     .set_palette((s) => s.tertiary_palette)
				     .set_background((s) => tertiary_fixed_dim)
				     .set_contrast_curve((s) => get_contrast_curve(7))
				     .build();
				return base.on_tertiary_fixed
				        .to_builder()
				        .extend_spec_version(SpecVersion.SPEC_2025, color_2025)
				        .build();
			}
		}

		public override DynamicColor on_tertiary_fixed_variant {
			owned get {
				DynamicColor color_2025 = new DynamicColorBuilder()
				     .set_name("on_tertiary_fixed_variant")
				     .set_palette((s) => s.tertiary_palette)
				     .set_background((s) => tertiary_fixed_dim)
				     .set_contrast_curve((s) => get_contrast_curve(4.5))
				     .build();
				return base.on_tertiary_fixed_variant
				        .to_builder()
				        .extend_spec_version(SpecVersion.SPEC_2025, color_2025)
				        .build();
			}
		}

		//////////////////////////////////////////////////////////////////
		// Android-only Colors                                          //
		//////////////////////////////////////////////////////////////////

		public override DynamicColor control_activated {
			owned get {
				// Remapped to primary_container for 2025 spec.
				DynamicColor color_2025 = primary_container
				     .to_builder()
				     .set_name("control_activated")
				     .build();
				return base.control_activated
				        .to_builder()
				        .extend_spec_version(SpecVersion.SPEC_2025, color_2025)
				        .build();
			}
		}

		public override DynamicColor control_normal {
			owned get {
				// Remapped to on_surface_variant for 2025 spec.
				DynamicColor color_2025 = on_surface_variant
				     .to_builder()
				     .set_name("control_normal")
				     .build();
				return base.control_normal
				        .to_builder()
				        .extend_spec_version(SpecVersion.SPEC_2025, color_2025)
				        .build();
			}
		}

		public override DynamicColor text_primary_inverse {
			owned get {
				// Remapped to inverse_on_surface for 2025 spec.
				DynamicColor color_2025 = inverse_on_surface
				     .to_builder()
				     .set_name("text_primary_inverse")
				     .build();
				return base.text_primary_inverse
				        .to_builder()
				        .extend_spec_version(SpecVersion.SPEC_2025, color_2025)
				        .build();
			}
		}

		////////////////////////////////////////////////////////////////
		// Other                                                      //
		////////////////////////////////////////////////////////////////

		private static double find_best_tone_for_chroma(double hue, double chroma, double tone, bool by_decreasing_tone) {
			double answer = tone;
			Hct best_candidate = Hct.from(hue, chroma, answer);
			while (best_candidate.chroma < chroma) {
				if (tone < 0 || tone > 100) {
					break;
				}
				tone += by_decreasing_tone ? -1.0 : 1.0;
				Hct new_candidate = Hct.from(hue, chroma, tone);
				if (best_candidate.chroma < new_candidate.chroma) {
					best_candidate = new_candidate;
					answer = tone;
				}
			}
			return answer;
		}

		private static double t_max_c(TonalPalette palette, double lower_bound = 0, double upper_bound = 100, double chroma_multipler = 1) {
			double answer = find_best_tone_for_chroma(palette.hue, palette.chroma * chroma_multipler, 100, true);
			return MathUtils.clamp_double(lower_bound, upper_bound, answer);
		}

		private static double t_min_c(TonalPalette palette, double lower_bound = 0, double upper_bound = 100) {
			double answer = find_best_tone_for_chroma(palette.hue, palette.chroma, 0, false);
			return MathUtils.clamp_double(lower_bound, upper_bound, answer);
		}

		private static ContrastCurve get_contrast_curve(double default_contrast) {
			if (default_contrast == 1.5) {
				return new ContrastCurve(1.5, 1.5, 3.0, 5.5);
			} else if (default_contrast == 3.0) {
				return new ContrastCurve(3.0, 3.0, 4.5, 7.0);
			} else if (default_contrast == 4.5) {
				return new ContrastCurve(4.5, 4.5, 7.0, 11.0);
			} else if (default_contrast == 6.0) {
				return new ContrastCurve(6.0, 6.0, 7.0, 11.0);
			} else if (default_contrast == 7.0) {
				return new ContrastCurve(7.0, 7.0, 11.0, 21.0);
			} else if (default_contrast == 9.0) {
				return new ContrastCurve(9.0, 9.0, 11.0, 21.0);
			} else if (default_contrast == 11.0) {
				return new ContrastCurve(11.0, 11.0, 21.0, 21.0);
			} else if (default_contrast == 21.0) {
				return new ContrastCurve(21.0, 21.0, 21.0, 21.0);
			} else {
				return new ContrastCurve(default_contrast, default_contrast, 7.0, 21.0);
			}
		}

		/////////////////////////////////////////////////////////////////
		// Color value calculations                                    //
		/////////////////////////////////////////////////////////////////

		public override Hct get_hct(DynamicScheme scheme, DynamicColor color) {
			// This is crucial for aesthetics: we aren't simply the taking the standard color
			// and changing its tone for contrast. Rather, we find the tone for contrast, then
			// use the specified chroma from the palette to construct a new color.
			//
			// For example, this enables colors with standard tone of T90, which has limited chroma, to
			// "recover" intended chroma as contrast increases.
			TonalPalette palette = color.palette(scheme);
			double tone = get_tone(scheme, color);
			double chroma_multiplier = color.chroma_multiplier == null ? 1.0 : color.chroma_multiplier(scheme);

			if (chroma_multiplier == 1.0) {
				return palette.get_hct(tone);
			}

			double chroma = palette.chroma * chroma_multiplier;

			if (tone == 99.0 && Hct.is_yellow(palette.hue)) {
				return TonalPalette.from_hue_and_chroma(palette.hue, chroma).get_hct(tone);
			}

			return Hct.from(palette.hue, chroma, tone);
		}

		public override double get_tone(DynamicScheme scheme, DynamicColor color) {
			ToneDeltaPair tone_delta_pair = color.tone_delta_pair == null ? null : color.tone_delta_pair(scheme);

			// Case 0: tone delta pair.
			if (tone_delta_pair != null) {
				DynamicColor role_a = tone_delta_pair.get_role_a();
				DynamicColor role_b = tone_delta_pair.get_role_b();
				TonePolarity polarity = tone_delta_pair.get_polarity();
				DeltaConstraint constraint = tone_delta_pair.get_constraint();
				double absolute_delta = polarity == TonePolarity.DARKER ||
				    (polarity == TonePolarity.RELATIVE_LIGHTER && scheme.is_dark) ||
				    (polarity == TonePolarity.RELATIVE_DARKER && !scheme.is_dark) ?
				    -tone_delta_pair.get_delta() : tone_delta_pair.get_delta();

				bool is_role_a = color.name == role_a.name;
				DynamicColor self_role = is_role_a ? role_a : role_b;
				DynamicColor reference_role = is_role_a ? role_b : role_a;
				double self_tone = self_role.tone(scheme);
				double reference_tone = reference_role.tone(scheme);
				double relative_delta = absolute_delta * (is_role_a ? 1 : -1);

				switch (constraint) {
				case EXACT :
					self_tone = MathUtils.clamp_double(0, 100, reference_tone + relative_delta);
					break;
				case NEARER :
					if (relative_delta > 0) {
						self_tone = MathUtils.clamp_double(0, 100, MathUtils.clamp_double(reference_tone, reference_tone + relative_delta, self_tone));
					} else {
						self_tone = MathUtils.clamp_double(0, 100, MathUtils.clamp_double(reference_tone + relative_delta, reference_tone, self_tone));
					}
					break;
				case FARTHER :
					if (relative_delta > 0) {
						self_tone = MathUtils.clamp_double(reference_tone + relative_delta, 100, self_tone);
					} else {
						self_tone = MathUtils.clamp_double(0, reference_tone + relative_delta, self_tone);
					}
					break;
				}

				if (color.background != null && color.contrast_curve != null) {
					DynamicColor background = color.background(scheme);
					ContrastCurve contrast_curve = color.contrast_curve(scheme);
					if (background != null && contrast_curve != null) {
						double bg_tone = background.get_tone(scheme);
						double self_contrast = contrast_curve.get_value(scheme.contrast_level);
						self_tone = Contrast.ratio_of_tones(bg_tone, self_tone) >= self_contrast && scheme.contrast_level >= 0 ? self_tone : DynamicColor.foreground_tone(bg_tone, self_contrast);
					}
				}

				// This can avoid the awkward tones for background colors including the access fixed colors.
				// Accent fixed dim colors should not be adjusted.
				if (color.is_background && !color.name.has_suffix("_fixed_dim")) {
					if (self_tone >= 57) {
						self_tone = MathUtils.clamp_double(65, 100, self_tone);
					} else {
						self_tone = MathUtils.clamp_double(0, 49, self_tone);
					}
				}

				return self_tone;
			} else {
				// Case 1: No tone delta pair; just solve for itself.
				double answer = color.tone(scheme);

				if (color.background == null ||
				    color.background(scheme) == null ||
				    color.contrast_curve == null ||
				    color.contrast_curve(scheme) == null) {
					// No adjustment for colors with no background.
					return answer;
				}

				double bg_tone = color.background(scheme).get_tone(scheme);
				double desired_ratio = color.contrast_curve(scheme).get_value(scheme.contrast_level);

				// Recalculate the tone from desired contrast ratio if the current
				// contrast ratio is not enough or desired contrast level is decreasing
				// (<0).
				answer = Contrast.ratio_of_tones(bg_tone, answer) >= desired_ratio && scheme.contrast_level >= 0 ? answer : DynamicColor.foreground_tone(bg_tone, desired_ratio);

				// This can avoid the awkward tones for background colors including the access fixed colors.
				// Accent fixed dim colors should not be adjusted.
				if (color.is_background && !color.name.has_suffix("_fixed_dim")) {
					if (answer >= 57) {
						answer = MathUtils.clamp_double(65, 100, answer);
					} else {
						answer = MathUtils.clamp_double(0, 49, answer);
					}
				}

				if (color.second_background == null || color.second_background(scheme) == null) {
					return answer;
				}

				// Case 2: Adjust for dual backgrounds.
				double bg_tone_1 = color.background(scheme).get_tone(scheme);
				double bg_tone_2 = color.second_background(scheme).get_tone(scheme);
				double upper = Math.fmax(bg_tone_1, bg_tone_2);
				double lower = Math.fmin(bg_tone_1, bg_tone_2);

				if (Contrast.ratio_of_tones(upper, answer) >= desired_ratio && Contrast.ratio_of_tones(lower, answer) >= desired_ratio) {
					return answer;
				}

				// The darkest light tone that satisfies the desired ratio,
				// or -1 if such ratio cannot be reached.
				double light_option = Contrast.lighter(upper, desired_ratio);

				// The lightest dark tone that satisfies the desired ratio,
				// or -1 if such ratio cannot be reached.
				double dark_option = Contrast.darker(lower, desired_ratio);

				// Tones suitable for the foreground.
				double[] availables = {};
				if (light_option != -1) {
					availables += light_option;
				}
				if (dark_option != -1) {
					availables += dark_option;
				}

				bool prefers_light = DynamicColor.tone_prefers_light_foreground(bg_tone_1) || DynamicColor.tone_prefers_light_foreground(bg_tone_2);
				if (prefers_light) {
					return (light_option < 0) ? 100 : light_option;
				}
				if (availables.length == 1) {
					return availables[0];
				}
				return (dark_option < 0) ? 0 : dark_option;
			}
		}

		//////////////////////////////////////////////////////////////////
		// Scheme Palettes                                              //
		//////////////////////////////////////////////////////////////////

		public override TonalPalette get_primary_palette(Variant variant, Hct source_color_hct, bool is_dark, Platform platform, double contrast_level) {
			switch (variant) {
			case NEUTRAL :
				return TonalPalette.from_hue_and_chroma(source_color_hct.hue, platform == Platform.PHONE ? (Hct.is_blue(source_color_hct.hue) ? 12 : 8) : (Hct.is_blue(source_color_hct.hue) ? 16 : 12));
			case TONAL_SPOT:
				return TonalPalette.from_hue_and_chroma(source_color_hct.hue, platform == Platform.PHONE && is_dark ? 26 : 32);
			case EXPRESSIVE:
				return TonalPalette.from_hue_and_chroma(source_color_hct.hue, platform == Platform.PHONE ? (is_dark ? 36 : 48) : 40);
			case VIBRANT:
				return TonalPalette.from_hue_and_chroma(source_color_hct.hue, platform == Platform.PHONE ? 74 : 56);
			default:
				return base.get_primary_palette(variant, source_color_hct, is_dark, platform, contrast_level);
			}
		}

		public override TonalPalette get_secondary_palette(Variant variant, Hct source_color_hct, bool is_dark, Platform platform, double contrast_level) {
			switch (variant) {
			case NEUTRAL:
				return TonalPalette.from_hue_and_chroma(source_color_hct.hue, platform == Platform.PHONE ? (Hct.is_blue(source_color_hct.hue) ? 6 : 4) : (Hct.is_blue(source_color_hct.hue) ? 10 : 6));
			case TONAL_SPOT:
				return TonalPalette.from_hue_and_chroma(source_color_hct.hue, 16);
			case EXPRESSIVE:
				return TonalPalette.from_hue_and_chroma(DynamicScheme.get_rotated_hue(source_color_hct, new double[] { 0, 105, 140, 204, 253, 278, 300, 333, 360 }, new double[] { -160, 155, -100, 96, -96, -156, -165, -160 }), platform == Platform.PHONE ? (is_dark ? 16 : 24) : 24);
			case VIBRANT:
				return TonalPalette.from_hue_and_chroma(DynamicScheme.get_rotated_hue(source_color_hct, new double[] { 0, 38, 105, 140, 333, 360 }, new double[] { -14, 10, -14, 10, -14 }), platform == Platform.PHONE ? 56 : 36);
			default:
				return base.get_secondary_palette(variant, source_color_hct, is_dark, platform, contrast_level);
			}
		}

		public override TonalPalette get_tertiary_palette(Variant variant, Hct source_color_hct, bool is_dark, Platform platform, double contrast_level) {
			switch (variant) {
			case NEUTRAL:
				return TonalPalette.from_hue_and_chroma(DynamicScheme.get_rotated_hue(source_color_hct, new double[] { 0, 38, 105, 161, 204, 278, 333, 360 }, new double[] { -32, 26, 10, -39, 24, -15, -32 }), platform == Platform.PHONE ? 20 : 36);
			case TONAL_SPOT:
				return TonalPalette.from_hue_and_chroma(DynamicScheme.get_rotated_hue(source_color_hct, new double[] { 0, 20, 71, 161, 333, 360 }, new double[] { -40, 48, -32, 40, -32 }), platform == Platform.PHONE ? 28 : 32);
			case EXPRESSIVE:
				return TonalPalette.from_hue_and_chroma(DynamicScheme.get_rotated_hue(source_color_hct, new double[] { 0, 105, 140, 204, 253, 278, 300, 333, 360 }, new double[] { -165, 160, -105, 101, -101, -160, -170, -165 }), 48);
			case VIBRANT:
				return TonalPalette.from_hue_and_chroma(DynamicScheme.get_rotated_hue(source_color_hct, new double[] { 0, 38, 71, 105, 140, 161, 253, 333, 360 }, new double[] { -72, 35, 24, -24, 62, 50, 62, -72 }), 56);
			default:
				return base.get_tertiary_palette(variant, source_color_hct, is_dark, platform, contrast_level);
			}
		}

		public override TonalPalette get_neutral_palette(Variant variant, Hct source_color_hct, bool is_dark, Platform platform, double contrast_level) {
			switch (variant) {
			case NEUTRAL:
				return TonalPalette.from_hue_and_chroma(source_color_hct.hue, platform == Platform.PHONE ? 1.4 : 6);
			case TONAL_SPOT:
				return TonalPalette.from_hue_and_chroma(source_color_hct.hue, platform == Platform.PHONE ? 5 : 10);
			case EXPRESSIVE:
				return TonalPalette.from_hue_and_chroma(get_expressive_neutral_hue(source_color_hct), get_expressive_neutral_chroma(source_color_hct, is_dark, platform));
			case VIBRANT:
				return TonalPalette.from_hue_and_chroma(get_vibrant_neutral_hue(source_color_hct), get_vibrant_neutral_chroma(source_color_hct, platform));
			default:
				return base.get_neutral_palette(variant, source_color_hct, is_dark, platform, contrast_level);
			}
		}

		public override TonalPalette get_neutral_variant_palette(Variant variant, Hct source_color_hct, bool is_dark, Platform platform, double contrast_level) {
			switch (variant) {
			case NEUTRAL:
				return TonalPalette.from_hue_and_chroma(source_color_hct.hue, (platform == Platform.PHONE ? 1.4 : 6) * 2.2);
			case TONAL_SPOT:
				return TonalPalette.from_hue_and_chroma(source_color_hct.hue, (platform == Platform.PHONE ? 5 : 10) * 1.7);
			case EXPRESSIVE:
				double expressive_neutral_hue = get_expressive_neutral_hue(source_color_hct);
				double expressive_neutral_chroma = get_expressive_neutral_chroma(source_color_hct, is_dark, platform);
				return TonalPalette.from_hue_and_chroma(expressive_neutral_hue, expressive_neutral_chroma * (expressive_neutral_hue >= 105 && expressive_neutral_hue < 125 ? 1.6 : 2.3));
			case VIBRANT:
				double vibrant_neutral_hue = get_vibrant_neutral_hue(source_color_hct);
				double vibrant_neutral_chroma = get_vibrant_neutral_chroma(source_color_hct, platform);
				return TonalPalette.from_hue_and_chroma(vibrant_neutral_hue, vibrant_neutral_chroma * 1.29);
			default:
				return base.get_neutral_variant_palette(variant, source_color_hct, is_dark, platform, contrast_level);
			}
		}

		public override TonalPalette ? get_error_palette(Variant variant, Hct source_color_hct, bool is_dark, Platform platform, double contrast_level) {
			double error_hue = DynamicScheme.get_piecewise_value(source_color_hct, new double[] { 0, 3, 13, 23, 33, 43, 153, 273, 360 }, new double[] { 12, 22, 32, 12, 22, 32, 22, 12 });
			switch (variant) {
			case NEUTRAL :
				return TonalPalette.from_hue_and_chroma(error_hue, platform == Platform.PHONE ? 50 : 40);
			case TONAL_SPOT:
				return TonalPalette.from_hue_and_chroma(error_hue, platform == Platform.PHONE ? 60 : 48);
			case EXPRESSIVE:
				return TonalPalette.from_hue_and_chroma(error_hue, platform == Platform.PHONE ? 64 : 48);
			case VIBRANT:
				return TonalPalette.from_hue_and_chroma(error_hue, platform == Platform.PHONE ? 80 : 60);
			default:
				return base.get_error_palette(variant, source_color_hct, is_dark, platform, contrast_level);
			}
		}

		private static double get_expressive_neutral_hue(Hct source_color_hct) {
			return DynamicScheme.get_rotated_hue(source_color_hct, new double[] { 0, 71, 124, 253, 278, 300, 360 }, new double[] { 10, 0, 10, 0, 10, 0 });
		}

		private static double get_expressive_neutral_chroma(Hct source_color_hct, bool is_dark, Platform platform) {
			double neutral_hue = get_expressive_neutral_hue(source_color_hct);
			return platform == Platform.PHONE ? (is_dark ? (Hct.is_yellow(neutral_hue) ? 6 : 14) : 18) : 12;
		}

		private static double get_vibrant_neutral_hue(Hct source_color_hct) {
			return DynamicScheme.get_rotated_hue(source_color_hct, new double[] { 0, 38, 105, 140, 333, 360 }, new double[] { -14, 10, -14, 10, -14 });
		}

		private static double get_vibrant_neutral_chroma(Hct source_color_hct, Platform platform) {
			double neutral_hue = get_vibrant_neutral_hue(source_color_hct);
			return platform == Platform.PHONE ? 28 : (Hct.is_blue(neutral_hue) ? 28 : 20);
		}
	}
}
