content: // Copyright 2022 Google Inc. Use of this source code is governed by an
  // MIT-style license that can be found in the LICENSE file or at
  // https://opensource.org/licenses/MIT.
  
  import 'package:meta/meta.dart';
  
  /// Metadata about a single channel in a known color space.
  ///
  /// {@category Value}
  @sealed
  class ColorChannel {
    /// The alpha channel that's shared across all colors.
    static const alpha = LinearChannel('alpha', 0, 1);
  
    /// The channel's name.
    final String name;
  
    /// Whether this is a polar angle channel, which represents (in degrees) the
    /// angle around a circle.
    ///
    /// This is true if and only if this is not a [LinearChannel].
    final bool isPolarAngle;
  
    /// The unit that's associated with this channel.
    ///
    /// Some channels are typically written without units, while others have a
    /// specific unit that is conventionally applied to their values. Although any
    /// compatible unit or unitless value will work for input¹, this unit is used
    /// when the value is serialized or returned from a Sass function.
    ///
    /// 1: Unless [LinearChannel.requiresPercent] is set, in which case unitless
    /// values are not allowed.
    final String? associatedUnit;
  
    /// @nodoc
    @internal
    const ColorChannel(
      this.name, {
      required this.isPolarAngle,
      this.associatedUnit,
    });
  
    /// Returns whether this channel is [analogous] to [other].
    ///
    /// [analogous]: https://www.w3.org/TR/css-color-4/#interpolation-missing
    bool isAnalogous(ColorChannel other) => switch ((name, other.name)) {
          ("red" || "x", "red" || "x") ||
          ("green" || "y", "green" || "y") ||
          ("blue" || "z", "blue" || "z") ||
          ("chroma" || "saturation", "chroma" || "saturation") ||
          ("lightness", "lightness") ||
          ("hue", "hue") =>
            true,
          _ => false,
        };
  }
  
  /// Metadata about a color channel with a linear (as opposed to polar) value.
  ///
  /// {@category Value}
  @sealed
  class LinearChannel extends ColorChannel {
    /// The channel's minimum value.
    ///
    /// Unless this color space is strictly bounded, this channel's values may
    /// still be below this minimum value. It just represents a limit to reference
    /// when specifying channels by percentage, as well as a boundary for what's
    /// considered in-gamut if the color space has a bounded gamut.
    final double min;
  
    /// The channel's maximum value.
    ///
    /// Unless this color space is strictly bounded, this channel's values may
    /// still be above this maximum value. It just represents a limit to reference
    /// when specifying channels by percentage, as well as a boundary for what's
    /// considered in-gamut if the color space has a bounded gamut.
    final double max;
  
    /// Whether this channel requires values to be specified with unit `%` and
    /// forbids unitless values.
    final bool requiresPercent;
  
    /// Whether the lower bound of this channel is clamped when the color is
    /// created using the global function syntax.
    final bool lowerClamped;
  
    /// Whether the upper bound of this channel is clamped when the color is
    /// created using the global function syntax.
    final bool upperClamped;
  
    /// Creates a linear color channel.
    ///
    /// By default, [ColorChannel.associatedUnit] is set to `%` if and only if
    /// [min] is 0 and [max] is 100. However, if [conventionallyPercent] is
    /// true, it's set to `%`, and if it's false, it's set to null.
    ///
    /// @nodoc
    @internal
    const LinearChannel(
      super.name,
      this.min,
      this.max, {
      this.requiresPercent = false,
      this.lowerClamped = false,
      this.upperClamped = false,
      bool? conventionallyPercent,
    }) : super(
            isPolarAngle: false,
            associatedUnit:
                (conventionallyPercent ?? (min == 0 && max == 100)) ? '%' : null,
          );
  }
  