// 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.

// ignore_for_file: avoid_renaming_method_parameters

import 'dart:math' as math;
import 'dart:typed_data';

import 'package:meta/meta.dart';

import '../../color.dart';
import '../conversions.dart';
import 'utils.dart';

/// The xyz-d50 color space.
///
/// https://www.w3.org/TR/css-color-4/#predefined-xyz
///
/// @nodoc
@internal
final class XyzD50ColorSpace extends ColorSpace {
  bool get isBoundedInternal => false;

  const XyzD50ColorSpace() : super('xyz-d50', xyzChannels);

  SassColor convert(
    ColorSpace dest,
    double? x,
    double? y,
    double? z,
    double? alpha, {
    bool missingLightness = false,
    bool missingChroma = false,
    bool missingHue = false,
    bool missingA = false,
    bool missingB = false,
  }) {
    switch (dest) {
      case ColorSpace.lab || ColorSpace.lch:
        // Algorithm from https://www.w3.org/TR/css-color-4/#color-conversion-code
        // and http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
        var f0 = _convertComponentToLabF((x ?? 0) / d50[0]);
        var f1 = _convertComponentToLabF((y ?? 0) / d50[1]);
        var f2 = _convertComponentToLabF((z ?? 0) / d50[2]);
        var lightness = missingLightness ? null : (116 * f1) - 16;
        var a = 500 * (f0 - f1);
        var b = 200 * (f1 - f2);

        return dest == ColorSpace.lab
            ? SassColor.lab(
                lightness,
                missingA ? null : a,
                missingB ? null : b,
                alpha,
              )
            : labToLch(
                ColorSpace.lch,
                lightness,
                a,
                b,
                alpha,
                missingChroma: missingChroma,
                missingHue: missingHue,
              );

      default:
        return super.convertLinear(
          dest,
          x,
          y,
          z,
          alpha,
          missingLightness: missingLightness,
          missingChroma: missingChroma,
          missingHue: missingHue,
          missingA: missingA,
          missingB: missingB,
        );
    }
  }

  /// Does a partial conversion of a single XYZ component to Lab.
  double _convertComponentToLabF(double component) => component > labEpsilon
      ? math.pow(component, 1 / 3) + 0.0
      : (labKappa * component + 16) / 116;

  @protected
  double toLinear(double channel) => channel;

  @protected
  double fromLinear(double channel) => channel;

  @protected
  Float64List transformationMatrix(ColorSpace dest) => switch (dest) {
        ColorSpace.srgbLinear ||
        ColorSpace.srgb ||
        ColorSpace.rgb =>
          xyzD50ToLinearSrgb,
        ColorSpace.a98Rgb => xyzD50ToLinearA98Rgb,
        ColorSpace.prophotoRgb => xyzD50ToLinearProphotoRgb,
        ColorSpace.displayP3 ||
        ColorSpace.displayP3Linear =>
          xyzD50ToLinearDisplayP3,
        ColorSpace.rec2020 => xyzD50ToLinearRec2020,
        ColorSpace.xyzD65 => xyzD50ToXyzD65,
        ColorSpace.lms => xyzD50ToLms,
        _ => super.transformationMatrix(dest),
      };
}
