// Copyright 2018 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:js/js.dart';

import '../../../util/nullable.dart';
import '../../../util/number.dart';
import '../../../value.dart';
import '../../reflection.dart';

@JS()
class _NodeSassColor {
  external SassColor get dartValue;
  external set dartValue(SassColor dartValue);
}

/// Creates a new `sass.types.Color` object wrapping [value].
Object newNodeSassColor(SassColor value) =>
    legacyColorClass.construct([null, null, null, null, value]);

/// The JS `sass.types.Color` class.
final JSClass legacyColorClass = createJSClass('sass.types.Color', (
  _NodeSassColor thisArg,
  num? redOrArgb, [
  num? green,
  num? blue,
  num? alpha,
  SassColor? dartValue,
]) {
  if (dartValue != null) {
    thisArg.dartValue = dartValue;
    return;
  }

  // This has two signatures:
  //
  // * `new sass.types.Color(red, green, blue, [alpha])`
  // * `new sass.types.Color(argb)`
  //
  // The latter takes an integer that's interpreted as the hex value 0xAARRGGBB.
  num red;
  if (green == null || blue == null) {
    var argb = redOrArgb as int;
    alpha = (argb >> 24) / 0xff;
    red = (argb >> 16) % 0x100;
    green = (argb >> 8) % 0x100;
    blue = argb % 0x100;
  } else {
    // Either [dartValue] or [redOrArgb] must be passed.
    red = redOrArgb!;
  }

  thisArg.dartValue = SassColor.rgb(
    _clamp(red),
    _clamp(green),
    _clamp(blue),
    alpha.andThen((alpha) => clampLikeCss(alpha.toDouble(), 0, 1)) ?? 1,
  );
})
  ..defineMethods({
    'getR': (_NodeSassColor thisArg) => thisArg.dartValue.red,
    'getG': (_NodeSassColor thisArg) => thisArg.dartValue.green,
    'getB': (_NodeSassColor thisArg) => thisArg.dartValue.blue,
    'getA': (_NodeSassColor thisArg) => thisArg.dartValue.alpha,
    'setR': (_NodeSassColor thisArg, num value) {
      thisArg.dartValue = thisArg.dartValue.changeRgb(red: _clamp(value));
    },
    'setG': (_NodeSassColor thisArg, num value) {
      thisArg.dartValue = thisArg.dartValue.changeRgb(green: _clamp(value));
    },
    'setB': (_NodeSassColor thisArg, num value) {
      thisArg.dartValue = thisArg.dartValue.changeRgb(blue: _clamp(value));
    },
    'setA': (_NodeSassColor thisArg, num value) {
      thisArg.dartValue = thisArg.dartValue.changeRgb(
        alpha: clampLikeCss(value.toDouble(), 0, 1),
      );
    },
  });

/// Clamps [channel] within the range 0, 255 and rounds it to the nearest
/// integer.
int _clamp(num channel) => fuzzyRound(clampLikeCss(channel.toDouble(), 0, 255));
