// Copyright 2016 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 '../callable.dart';
import '../exception.dart';
import '../visitor/interface/value.dart';
import '../value.dart';

/// A SassScript function reference.
///
/// A function reference captures a function from the local environment so that
/// it may be passed between modules.
///
/// {@category Value}
@sealed
class SassFunction extends Value {
  /// The callable that this function invokes.
  ///
  /// Note that this is typed as an [AsyncCallable] so that it will work with
  /// both synchronous and asynchronous evaluate visitors, but in practice the
  /// synchronous evaluate visitor will crash if this isn't a [Callable].
  final AsyncCallable callable;

  /// The unique compile context for tracking if this [SassFunction] belongs to
  /// the current compilation or not.
  ///
  /// This is `null` for functions defined in plugins' Dart code.
  final Object? _compileContext;

  SassFunction(this.callable) : _compileContext = null;

  @internal
  SassFunction.withCompileContext(this.callable, this._compileContext);

  /// @nodoc
  @internal
  T accept<T>(ValueVisitor<T> visitor) => visitor.visitFunction(this);

  SassFunction assertFunction([String? name]) => this;

  /// Asserts that this SassFunction belongs to [compileContext] and returns it.
  ///
  /// It's checked before evaluating a SassFunction to prevent execution of
  /// SassFunction across different compilations.
  @internal
  SassFunction assertCompileContext(Object compileContext) {
    if (_compileContext != null && _compileContext != compileContext) {
      throw SassScriptException(
          "$this does not belong to current compilation.");
    }

    return this;
  }

  bool operator ==(Object other) =>
      other is SassFunction && callable == other.callable;

  int get hashCode => callable.hashCode;
}
