// 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 'package:meta/meta.dart';

import '../../../util/number.dart';
import '../../color.dart';
import '../conversions.dart';
import 'utils.dart';
import 'xyz_d50.dart';

/// The Lab color space.
///
/// https://www.w3.org/TR/css-color-4/#specifying-lab-lch
///
/// @nodoc
@internal
final class LabColorSpace extends ColorSpace {
  bool get isBoundedInternal => false;

  const LabColorSpace()
      : super('lab', const [
          LinearChannel(
            'lightness',
            0,
            100,
            lowerClamped: true,
            upperClamped: true,
          ),
          LinearChannel('a', -125, 125),
          LinearChannel('b', -125, 125),
        ]);

  SassColor convert(
    ColorSpace dest,
    double? lightness,
    double? a,
    double? b,
    double? alpha, {
    bool missingChroma = false,
    bool missingHue = false,
  }) {
    switch (dest) {
      case ColorSpace.lab:
        var powerlessAB = lightness == null || fuzzyEquals(lightness, 0);
        return SassColor.lab(
          lightness,
          a == null || powerlessAB ? null : a,
          b == null || powerlessAB ? null : b,
          alpha,
        );

      case ColorSpace.lch:
        return labToLch(dest, lightness, a, b, alpha);

      default:
        var missingLightness = lightness == null;
        lightness ??= 0;
        // 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 f1 = (lightness + 16) / 116;

        return const XyzD50ColorSpace().convert(
          dest,
          _convertFToXorZ((a ?? 0) / 500 + f1) * d50[0],
          (lightness > labKappa * labEpsilon
                  ? math.pow((lightness + 16) / 116, 3) * 1.0
                  : lightness / labKappa) *
              d50[1],
          _convertFToXorZ(f1 - (b ?? 0) / 200) * d50[2],
          alpha,
          missingLightness: missingLightness,
          missingChroma: missingChroma,
          missingHue: missingHue,
          missingA: a == null,
          missingB: b == null,
        );
    }
  }

  /// Converts an f-format component to the X or Z channel of an XYZ color.
  double _convertFToXorZ(double component) {
    var cubed = math.pow(component, 3) + 0.0;
    return cubed > labEpsilon ? cubed : (116 * component - 16) / labKappa;
  }
}
