// Copyright 2020 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';

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

/// A specialized subclass of [SassNumber] for numbers that have no units.
///
/// {@category Value}
@sealed
class UnitlessSassNumber extends SassNumber {
  List<String> get numeratorUnits => const [];

  List<String> get denominatorUnits => const [];

  bool get hasUnits => false;
  bool get hasComplexUnits => false;

  UnitlessSassNumber(super.value, [super.asSlash]) : super.protected();

  SassNumber withValue(num value) => UnitlessSassNumber(value.toDouble());

  SassNumber withSlash(SassNumber numerator, SassNumber denominator) =>
      UnitlessSassNumber(value, (numerator, denominator));

  bool hasUnit(String unit) => false;

  bool hasCompatibleUnits(SassNumber other) => other is UnitlessSassNumber;

  @internal
  bool hasPossiblyCompatibleUnits(SassNumber other) =>
      other is UnitlessSassNumber;

  bool compatibleWithUnit(String unit) => true;

  SassNumber coerceToMatch(
    SassNumber other, [
    String? name,
    String? otherName,
  ]) =>
      other.withValue(value);

  double coerceValueToMatch(
    SassNumber other, [
    String? name,
    String? otherName,
  ]) =>
      value;

  SassNumber convertToMatch(
    SassNumber other, [
    String? name,
    String? otherName,
  ]) =>
      other.hasUnits
          // Call this to generate a consistent error message.
          ? super.convertToMatch(other, name, otherName)
          : this;

  double convertValueToMatch(
    SassNumber other, [
    String? name,
    String? otherName,
  ]) =>
      other.hasUnits
          // Call this to generate a consistent error message.
          ? super.convertValueToMatch(other, name, otherName)
          : value;

  SassNumber coerce(
    List<String> newNumerators,
    List<String> newDenominators, [
    String? name,
  ]) =>
      SassNumber.withUnits(
        value,
        numeratorUnits: newNumerators,
        denominatorUnits: newDenominators,
      );

  double coerceValue(
    List<String> newNumerators,
    List<String> newDenominators, [
    String? name,
  ]) =>
      value;

  double coerceValueToUnit(String unit, [String? name]) => value;

  SassBoolean greaterThan(Value other) {
    if (other is SassNumber) {
      return SassBoolean(fuzzyGreaterThan(value, other.value));
    }
    return super.greaterThan(other);
  }

  SassBoolean greaterThanOrEquals(Value other) {
    if (other is SassNumber) {
      return SassBoolean(fuzzyGreaterThanOrEquals(value, other.value));
    }
    return super.greaterThanOrEquals(other);
  }

  SassBoolean lessThan(Value other) {
    if (other is SassNumber) {
      return SassBoolean(fuzzyLessThan(value, other.value));
    }
    return super.lessThan(other);
  }

  SassBoolean lessThanOrEquals(Value other) {
    if (other is SassNumber) {
      return SassBoolean(fuzzyLessThanOrEquals(value, other.value));
    }
    return super.lessThanOrEquals(other);
  }

  SassNumber modulo(Value other) {
    if (other is SassNumber) {
      return other.withValue(moduloLikeSass(value, other.value));
    }
    return super.modulo(other);
  }

  Value plus(Value other) {
    if (other is SassNumber) {
      return other.withValue(value + other.value);
    }
    return super.plus(other);
  }

  Value minus(Value other) {
    if (other is SassNumber) {
      return other.withValue(value - other.value);
    }
    return super.minus(other);
  }

  Value times(Value other) {
    if (other is SassNumber) {
      return other.withValue(value * other.value);
    }
    return super.times(other);
  }

  Value dividedBy(Value other) {
    if (other is SassNumber) {
      return other.hasUnits
          ? SassNumber.withUnits(
              value / other.value,
              numeratorUnits: other.denominatorUnits,
              denominatorUnits: other.numeratorUnits,
            )
          : UnitlessSassNumber(value / other.value);
    }
    return super.dividedBy(other);
  }

  Value unaryMinus() => UnitlessSassNumber(-value);

  bool operator ==(Object other) =>
      other is UnitlessSassNumber && fuzzyEquals(value, other.value);

  int get hashCode => hashCache ??= fuzzyHashCode(value);
}
