@charset "UTF-8";

// Built-in modules
@use "sass:color";
@use "sass:list";
@use "sass:map";
@use "sass:math";
@use "sass:meta";
@use "sass:selector";
@use "sass:string";

// Mixkit settings and modules
@use "mixkit/settings" as settings;
@use "mixkit/modular/helpers" as *;

/// Generate base and ratio values for modular scale computatiton.
///
/// @name _settings
///
/// @group mixkit/modular
///
/// @param {number (with unit)} $base - [false]
///   A valid CSS length. i.e 1em, 16px
///
/// @param {number} $ratio - [false]
///   Ratio to be used. Ratio variables are stored in "mixkit/modular/helpers"
///
/// @param {map} $thread - [false]
///   Thread to reference.
///
/// @param {map} $modular-scale - [settings.$modular-scale]
///   Modular scale configuration.
///
/// @output Computed base and ratio values.
///
/// @access private
///
/// @since 1.0.0

@function _settings(
    $base: false,
    $ratio: false,
    $thread: false,
    $modular-scale: settings.$modular-scale) {
    // Logic: Assign default or inline settings
    $base: if($base, $base, settings.$modular-base);
    $ratio: if($ratio, $ratio, settings.$modular-ratio);
    $thread: map.get($modular-scale, $thread);

    // Logic: Assign user settings for $base
    @if map.get($modular-scale, base) {
        $base: map.get($modular-scale, base);
    }

    // Logic: Assign user settings for $ratio
    @if map.get($modular-scale, ratio) {
        $ratio: map.get($modular-scale, ratio);
    }

    // Logic: Assign user settings for thread $base and $ratio
    @if $thread {
        @if map.get($thread, base) {
            $base: map.get($thread, base);
        }
        @if map.get($thread, ratio) {
            $ratio: map.get($thread, ratio);
        }
    }

    @return $base $ratio;
}

/// Sorts a list.
///
/// @name _sort
///
/// @group mixkit/modular
///
/// @param {list} $list
///
/// @output Sorted list.
///
/// @access private
///
/// @since 1.0.0

// ! Refactor

@function _sort($list) {
    // Vars
    $sorted: false;

    // Logic: Perform while $sorted is false
    @while not $sorted {
        $sorted: true;

        // Swap integers
        @for $i from 2 through list.length($list) {
            $n1: list.nth($list, $i - 1);
            $n2: list.nth($list, $i);
            @if $n1 > $n2 {
                $list: list.set-nth($list, $i, $n1);
                $list: list.set-nth($list, $i - 1, $n2);
                $sorted: false;
            }
        }
    }

    @return $list;
}

/// Return a value based on modular computation.
///
/// @name modular-scale|ms
///
/// @group mixkit/modular
///
/// @param {number (with unit)} $value - [0]
///   Number of steps to increment up or down the scale.
///
/// @param {number (with unit)} $base - [false]
///   A valid CSS length. i.e 1em, 16px
///
/// @param {number} $ratio - [false]
///   Ratio to be used. Ratio variables are stored in "mixkit/modular/helpers"
///
/// @param {map} $thread - [false]
///   Thread to reference.
///
/// @param {map} $modular-scale - [settings.$modular-scalde]
///   Modular scale configuration.
///
/// @require {function (mixkit/modular)} _settings
///
/// @require {function (mixkit/modular)} _sort
///
/// @output CSS length.
///
/// @access public
///
/// @since 1.0.0

// ! Update to math.div() for division when Sass releases new syntax.
// ! "x/y" and "(x/y)" will break.

@function modular-scale(
    $value: 0,
    $base: false,
    $ratio: false,
    $thread: false,
    $modular-scale: settings.$modular-scale) {
    // Vars
    $settings: _settings($base, $ratio, $thread, $modular-scale);
    $base: list.nth($settings, 1);
    $ratio: list.nth($settings, 2);

    // Logic: Return if single-stranded
    @if list.length($base) == 1 {
        @return math.pow($ratio, $value) * $base;
    }

    // Logic: Return if multi-stranded
    @else if list.length($base) > 1 {
        // Create new bases list, set main base
        $new-bases: (list.nth($base, 1));
        $main-base: list.nth($new-bases, 1);

        // Process other base values
        @for $i from 2 through list.length($base) {
            $subject: list.nth($base, $i);

            // Logic: Target is greater than main base
            @if $subject > $main-base {
                @while $subject > $main-base {
                    // ! Update to math.div() here
                    $subject: ($subject / $ratio);
                }
                $subject: $subject * $ratio;
            }

            // Logic: Target is less than main base
            @else if $subject < $main-base {
                @while $subject < $main-base {
                    $subject: $subject * $ratio;
                }
            }

            // Push into $new-bases
            $new-bases: list.append($new-bases, $subject);
        }
        // Sort $new-bases list
        $new-bases: _sort($new-bases);

        // Find step to use in calculation
        $calc-step: math.floor($value / list.length($new-bases));

        // Find base to use in calculation
        $calc-base: math.round(($value / list.length($new-bases) - $calc-step) * list.length($new-bases)) + 1;

        // Return scaled value
        @return math.pow($ratio, $calc-step) * list.nth($new-bases, $calc-base);
    }
}

// Alias: ms
@function ms($args...) {
    @return meta.call(meta.get-function("modular-scale"), $args...);
}
