@use "sass:meta";

// Adapted from https://css-tricks.com/snippets/sass/advanced-type-checking/

/// A wrapper around the `call()` function.
/// Calls the function `$function` with the arguments `$args`.
/// @param {Function} $function - The function to call.
/// @param {List} $args - The arguments to pass to `$function`.
/// @return {Any} - The result of calling `$function` with `$args`.
///
/// @example scss - Usage
///   @debug k-meta-call( k-meta-get-function( "k-string-replace" ), "foo bar", "bar", "baz" ); // => "foo baz"
@function k-meta-call( $function, $args... ) {
    @return meta.call( $function, $args... );
}

/// A wrapper around the `function-exists()` function.
/// Returns whether a function with the name `$name` exists.
/// @param {String} $name - The name of the function to check.
/// @return {Boolean} - Whether a function with the name `$name` exists.
///
/// @example scss - Usage
///   @debug k-meta-function-exists( "k-string-replace" ); // => true
@function k-meta-function-exists( $name ) {
    @if $name == "" {
        @return false;
    }

    @return meta.function-exists( $name );
}

/// A wrapper around the `get-function()` function.
/// Returns the function with the name `$name`.
/// @param {String} $name - The name of the function to get.
/// @param {Boolean} $css - Whether to return the CSS representation of the function.
/// @param {Module} $module - The module to get the function from.
/// @return {Function} - The function with the name `$name`.
///
/// @example scss - Usage
///   @debug k-meta-get-function( "k-string-replace" ); // => Function
@function k-meta-get-function( $name, $args... ) {
    @return meta.get-function( $name, $args... );
}

/// A wrapper around the `inspect()` function.
/// Returns a string representation of `$value`.
/// @param {Any} $value - The value to inspect.
/// @return {String} - A string representation of `$value`.
///
/// @example scss - Usage
///   @debug k-meta-inspect( "foo bar" ); // => "foo bar"
@function k-meta-inspect( $value ) {
    @return meta.inspect( $value );
}

/// A wrapper around the `keywords()` function.
/// Returns a map of the keywords in `$args`.
/// @param {List} $args - The arguments to process.
/// @return {Map} - A map of the keywords in `$args`.
///
/// @example scss - Usage
///   @debug k-meta-keywords( ( "foo" "bar" "baz" "qux" ) ); // => ( "foo": "bar", "baz": "qux" )
@function k-meta-keywords( $args ) {
    @return meta.keywords( $args );
}

/// A wrapper around the `type-of()` function.
/// Returns the type of `$value`.
/// @param {Any} $value - The value to get the type of.
/// @return {String} - The type of `$value`.
///
/// @example scss - Usage
///   @debug k-meta-type-of( "foo bar" ); // => "string"
@function k-meta-type-of( $value ) {
    @return meta.type-of( $value );
}

/// A wrapper around the `variable-exists()` function.
/// Returns whether a variable with the name `$name` exists.
/// @param {String} $name - The name of the variable to check.
/// @return {Boolean} - Whether a variable with the name `$name` exists.
///
/// @example scss - Usage
///   @debug k-meta-variable-exists( "foo" ); // => true
@function k-meta-variable-exists( $name ) {
    @return meta.variable-exists( $name );
}

/// Checks whether `$value` is a <number> CSS data type.
/// @param {Any} $value - The value to check.
/// @return {Boolean} - Whether `$value` is a number.
///
/// @link https://developer.mozilla.org/en-US/docs/Web/CSS/number
///
/// @example scss - Usage
///   @debug k-meta-is-number( 1 ); // => true
///   @debug k-meta-is-number( "foo" ); // => false
@function k-meta-is-number( $value ) {
    @return k-meta-type-of( $value ) == "number";
}

/// Checks whether `$value` is a <integer> CSS data type.
/// @param {Any} $value - The value to check.
/// @return {Boolean} - Whether `$value` is a integer.
///
/// @link https://developer.mozilla.org/en-US/docs/Web/CSS/integer
///
/// @example scss - Usage
///   @debug k-meta-is-integer( 1 ); // => true
///   @debug k-meta-is-integer( 1.5 ); // => false
@function k-meta-is-integer( $value ) {
    @return k-meta-is-number( $value ) and k-math-round( $value ) == $value;
}

/// Checks whether `$value` is a <time> CSS data type.
/// @param {Any} $value - The value to check.
/// @return {Boolean} - Whether `$value` is a time.
///
/// @link https://developer.mozilla.org/en-US/docs/Web/CSS/time
///
/// @example scss - Usage
///   @debug k-meta-is-time( 1s ); // => true
///   @debug k-meta-is-time( 1 ); // => false
@function k-meta-is-time( $value ) {
    @return k-meta-is-number( $value ) and k-string-index( "ms" "s", k-math-unit( $value ) ) != null;
}

/// Checks whether `$value` is a valid duration period.
/// @param {Any} $value - The value to check.
/// @return {Boolean} - Whether `$value` is a duration.
///
/// @link https://developer.mozilla.org/en-US/docs/Web/CSS/time
///
/// @example scss - Usage
///   @debug k-meta-is-duration( 1s ); // => true
///   @debug k-meta-is-duration( 1 ); // => false
@function k-meta-is-duration( $value ) {
    @return k-meta-is-time( $value );
}

/// Checks whether `$value` is a <angle> CSS data type.
/// @param {Any} $value - The value to check.
/// @return {Boolean} - Whether `$value` is a angle.
///
/// @link https://developer.mozilla.org/en-US/docs/Web/CSS/angle
///
/// @example scss - Usage
///   @debug k-meta-is-angle( 1deg ); // => true
///   @debug k-meta-is-angle( 1 ); // => false
@function k-meta-is-angle( $value ) {
    @return k-meta-is-number( $value ) and k-string-index( "deg" "rad" "grad" "turn", k-math-unit( $value ) ) != null;
}

/// Checks whether `$value` is a <frequency> CSS data type.
/// @param {Any} $value - The value to check.
/// @return {Boolean} - Whether `$value` is a frequency.
///
/// @link https://developer.mozilla.org/en-US/docs/Web/CSS/frequency
///
/// @example scss - Usage
///   @debug k-meta-is-frequency( 1Hz ); // => true
///   @debug k-meta-is-frequency( 1 ); // => false
@function k-meta-is-frequency( $value ) {
    @return k-meta-is-number( $value ) and k-string-index( "Hz" "kHz", k-math-unit( $value ) ) != null;
}

/// Checks whether `$value` is a relative <length> CSS data type.
/// @param {Any} $value - The value to check.
/// @return {Boolean} - Whether `$value` is a relative length.
///
/// @link https://developer.mozilla.org/en-US/docs/Web/CSS/length#relative_length_units_based_on_font
/// @link https://developer.mozilla.org/en-US/docs/Web/CSS/length#relative_length_units_based_on_viewport
///
/// @example scss - Usage
///   @debug k-meta-is-relative-length( 1em ); // => true
///   @debug k-meta-is-relative-length( 1ch ); // => true
///   @debug k-meta-is-relative-length( 1 ); // => false
@function k-meta-is-relative-length( $value ) {
    @return k-meta-is-number( $value ) and k-string-index( "em" "ex" "ch" "rem" "vw" "vh" "vmin" "vmax", k-math-unit( $value ) ) != null;
}

/// Checks whether `$value` is an absolute <length> CSS data type.
/// @param {Any} $value - The value to check.
/// @return {Boolean} - Whether `$value` is an absolute length.
///
/// @link https://developer.mozilla.org/en-US/docs/Web/CSS/length#absolute_length_units
///
/// @example scss - Usage
///   @debug k-meta-is-absolute-length( 1cm ); // => true
///   @debug k-meta-is-absolute-length( 1 ); // => false
@function k-meta-is-absolute-length( $value ) {
    @return k-meta-is-number( $value ) and k-string-index( "cm" "mm" "in" "px" "pt" "pc", k-math-unit( $value ) ) != null;
}

/// Checks whether `$value` is a <percentage> CSS data type.
/// @param {Any} $value - The value to check.
/// @return {Boolean} - Whether `$value` is a percentage.
///
/// @link https://developer.mozilla.org/en-US/docs/Web/CSS/percentage
///
/// @example scss - Usage
///   @debug k-meta-is-percentage( 1% ); // => true
///   @debug k-meta-is-percentage( 1 ); // => false
@function k-meta-is-percentage( $value ) {
    @return k-meta-is-number( $value ) and k-math-unit( $value ) == "%";
}

/// Checks whether `$value` is a <length> CSS data type.
/// @param {Any} $value - The value to check.
/// @return {Boolean} - Whether `$value` is a length.
///
/// @link https://developer.mozilla.org/en-US/docs/Web/CSS/length
///
/// @example scss - Usage
///   @debug k-meta-is-length( 1em ); // => true
///   @debug k-meta-is-length( 1cm ); // => true
///   @debug k-meta-is-length( 1 ); // => false
@function k-meta-is-length( $value ) {
    @return k-meta-is-relative-length( $value ) or k-meta-is-absolute-length( $value );
}

/// Checks whether `$value` is a <resolution> CSS data type.
/// @param {Any} $value - The value to check.
/// @return {Boolean} - Whether `$value` is a resolution.
///
/// @link https://developer.mozilla.org/en-US/docs/Web/CSS/resolution
///
/// @example scss - Usage
///   @debug k-meta-is-resolution( 1dpi ); // => true
///   @debug k-meta-is-resolution( 1 ); // => false
@function k-meta-is-resolution( $value ) {
    @return k-meta-is-number( $value ) and k-string-index( "dpi" "dpcm" "dppx", k-math-unit( $value ) ) != null;
}

/// Checks whether `$value` is a <position> CSS data type.
/// @param {Any} $value - The value to check.
/// @return {Boolean} - Whether `$value` is a position.
///
/// @link https://developer.mozilla.org/en-US/docs/Web/CSS/position
///
/// @example scss - Usage
///   @debug k-meta-is-position( center ); // => true
@function k-meta-is-position( $value ) {
    @return k-meta-is-length( $value ) or k-meta-is-percentage( $value ) or k-string-index( "top" "right" "bottom" "left" "center", $value ) != null;
}
