// *************************************
//
// # JigSass Selector Helpers
// -> A collection of private helper
//    functions for jigsass-tools-selectors
//
// Author: TxHawks (tofu.hawks@gmail.com)
//
////
/// @group Selectors - 03 Helpers
////
//
// *************************************

// -------------------------------------
// Dependencies
// -------------------------------------

@if (not (global-variable-exists(jigsass-breakpoints) and mixin-exists(jigsass-mq))) {
  @error '`jigsass-tools-selectors` has a dependency on `jigsass-tools-mq`. Please import it.';
}





// -------------------------------------
// Variables
// -------------------------------------

/// Selector map cache
///
/// Selector maps are always identical, so can be
/// generated once and cached.
///
/// For internal use by _jigsass-generate-selector-map
/// ---
/// @access private
/// ---
/// @type {Map}
/// ---
$_jigsass-selector-map: false;




// -------------------------------------
// Functions
// -------------------------------------

/// Parse arguments into a string for a class name
/// ---
/// @param {String | Boolean} $name - The name of the object or util.
/// @param {String | Boolean} $from - The name of breakpoint used as the min-width condition.
/// @param {String | Boolean} $until - The name of breakpoint used as the max-width condition.
/// @param {String | Boolean} $misc - The name of a miscellaneous media query condition.
/// @param {String | Boolean} $modifier - A modifier's name
/// ---
/// @return {String} A class name (sans the `.`)
/// ---
/// @access private
/// ---
@function _jigsass-gen-class-name(
  $name,
  $from: false,
  $until: false,
  $misc: false,
  $modifier: false
) {
  $_min-str: '';
  $_max-str: '';
  $_misc-str: '';
  $_mod-str: if($modifier and $modifier != '', '--#{$modifier}', '');
  $_bp-str: _jigsass-bp-string($from, $until, $misc);
  $_bp-str: if($_bp-str == 'no-breakpoint', '', '--#{$_bp-str}');

  @return #{jigsass-get-namespace()}#{$name}#{$_mod-str}#{$_bp-str};
}


/// Check if a class was already included
/// ---
/// @param {String} $name - The name of the object or util.
/// @param {String | Boolean} $from - The name of breakpoint used as the min-width condition.
/// @param {String | Boolean} $until - The name of breakpoint used as the max-width condition.
/// @param {String | Boolean} $misc - The name of a miscellaneous media query condition.
/// @param {String | Boolean} $modifier - A modifier's name
/// ---
/// @return {Boolean}
///   Indicates if the class was generated or called in silent mode (true);
/// ---
/// @access private
/// ---
@function _jigsass-class-was-included($name, $from, $until, $misc, $modifier) {
  @if (not function-exists(jigsass-get)) {
    @error '`_jigsass-selector-was-inclded()` has a dependency on ' +
      '`jigsass-tools-maps`. Please import it.';
  }

  @if (not global-variable-exists(_jigsass-selectors)) {
    @return false;
  }

  $_bp-str: if(
    not ($from or $until or $misc),
    no-breakpoint,
    _jigsass-bp-string($from, $until, $misc)
  );

  @return jigsass-get($_jigsass-selectors, $name, $_bp-str, $modifier or no-modifier);
}


/// Return a string representing a media query.
/// ---
/// @param {String | Boolean} $from - The name of breakpoint used as the min-width condition.
/// @param {String | Boolean} $until - The name of breakpoint used as the max-width condition.
/// @param {String | Boolean} $misc - The name of a miscellaneous media query condition.
/// ---
/// @return {String} - string representation of a media-query's conditions
/// ---
/// @access private
/// ---
@function _jigsass-bp-string($from, $until, $misc) {
  // No from or until for 0-width breakpoints
  $from: if($from, if(jigsass-mq-get-breakpoint-width($from) == 0, false, $from), $from);
  $until: if($until, if(jigsass-mq-get-breakpoint-width($until) == 0, false, $until), $until);

  // Error out if any of the breakpoints is not a named breakpoint
  @if ($from and not jigsass-mq-breakpoint-defined($from)) {
    @error '_jigsass-bp-string: #{$from} is not a named width breakpoint';
  }
  @if ($until and not jigsass-mq-breakpoint-defined($until)) {
    @error '_jigsass-bp-string: #{$until} is not a named width breakpoint';
  }
  @if ($misc and not jigsass-mq-breakpoint-defined($misc)) {
    @error '_jigsass-bp-string: #{$misc} is not a named misc feature breakpoint';
  }

  $_bp-str: if($from, 'from-#{$from}', '');
  $_bp-str: $_bp-str + if($until, if($_bp-str == '', 'until-#{$until}', '-until-#{$until}'), '');
  $_bp-str: $_bp-str + if($misc, if($_bp-str == '', 'when-#{$misc}', '-when-#{$misc}'), '');

  $_bp-str: if($_bp-str == '', 'no-breakpoint', $_bp-str);

  @return $_bp-str;
}



/// Parse a string representing media query conditions
/// into a map with `from`, `until` and `misc` keys.
@function _jigsass-mq-args-parser($bp-str) {
  $conditions: (from: false, until: false, misc: false);
  $misc-index: str_index($bp-str, '-when');
  $misc-condition: if($misc-index, str_slice($bp-str, $misc-index + 6, -1), null);
  @if ($misc-condition) {
    $conditions: jigsass-set($conditions, misc, $misc-condition);
    $bp-str: str_slice($bp-str, 0, $misc-index - 1);
  }

  $until-index: str_index($bp-str, '-until');
  $until-condition: if($until-index, str_slice($bp-str, $until-index + 7, -1), null);
  @if ($until-condition) {
    $conditions: jigsass-set($conditions, until, $until-condition);
    $bp-str: str_slice($bp-str, 0, $until-index - 1);
  }

  $_cond-key: if(
    str_index($bp-str, 'from'),
    from,
    if(str-index($bp-str, 'until'), until, if(str-index($bp-str, 'when'), misc, false))
  );
  @if ($_cond-key) {
    $_cond-value: str-slice($bp-str, str_index($bp-str, '-') + 1);
    $condition: ($_cond-key: $_cond-value);
    $conditions: map-merge($conditions, $condition);
  }

  @return $conditions;
}

/// ---
/// Parse a modifier string into a map with `modifier`, `state` and `args` keys,
/// for use inside `@content` blocks.
///
/// Modifiers should be written in the following format: `'modifier-name(arg1,arg2):state'`
/// ---
/// @example scss
///   $parsed: jigsass-parse-modifier(mod(tint,1):hover);
///     // -> (modifier: mod, args: 'tint' 1, state: ':hover');
/// ---
/// @since 0.7.0
/// ---
/// @return {Map}
///   A map of parsed values.
/// ---
@function jigsass-parse-modifier($modifier) {
  $args: false;
  $state: false;

  @if (type-of($modifier) == string) {
    $state-index: str-index($modifier, ':');
    $state: if($state-index, unquote(str-slice($modifier, $state-index, -1)), false);

    $args-index: str-index($modifier, '(');
    $args-end-index: if($state-index, $state-index - 2, -2);
    $_args: if(
      $args-index,
      jigsass-str-split(str-slice($modifier, $args-index + 1, $args-end-index), ','),
      false
    );

    @if ($_args) {
      $args: ();
      $_warn-cache: $jigsass-str-to-number-suppress-warning;
      $jigsass-str-to-number-suppress-warning: true !global;

      @each $arg in $_args {
        $args: append($args, jigsass-str-to-number($arg));
      }
      $jigsass-str-to-number-suppress-warning: $_warn-cache !global;
    }

    @if ($state or $args) {
      $mod-end-index: if($args-index, $args-index - 1, if($state-index, $state-index - 1, -1));
      $modifier: if($mod-end-index == 0, false, str-slice($modifier, 1, $mod-end-index));
    }
  }

  @return (modifier: $modifier, state: $state, args: $args);
}


/// Generate a map used for generating a jigsass
/// object or utility class in a correct and
/// predictable order, allowing usage of the cascade
/// to override default classes with modifier ones
/// and non-responsive classes media responsive ones.
/// ---
/// @param {String} $name [$name]
///   The name of the object of helper to generate
///   the map for. When in the context of generating
///   a utility or object class, defaults to the name
///   of that class.
/// @param {Map} $bps [$jigsass-breakpoints]
/// ---
/// @return {Map}
/// ---
/// @example
///   $jigsass-selectors: map-merge(
///     $jigsass-selectors,
///     _jigsass-generate-selector-map(class-name)
///   );
/// ---
/// @access private
/// ---
@function _jigsass-generate-selector-map($name, $bps: $jigsass-breakpoints) {
  $selector-map: ();

  // Use cached selector map if avilable
  @if ($_jigsass-selector-map) {
    $selector-map: $_jigsass-selector-map;
  }

  // Generate selector-map if it has not already been cached.
  @else {
    $_lengths: jigsass-mq-sort-length-breakpoints(map-get($bps, lengths));
    $_features: map-get($bps, features);

    $selector-map: (
      no-breakpoint: (
        no-modifier: false,
      ),
    );

    // Width breakpoints
    @if($_lengths) {
      // Min width breakpoints
      @each $bp-min, $length-min in $_lengths {
        @if (jigsass-strip-unit($length-min) != 0) {
          $selector-map: map-merge($selector-map, (from-#{$bp-min}: (no-modifier: false)));
        }
      }

      // Max width breakpoints
      @each $bp-max, $length-max in $_lengths {
        @if (jigsass-strip-unit($length-max) != 0) {
          $selector-map: map-merge($selector-map, (until-#{$bp-max}: (no-modifier: false)));
        }
      }

      // Min and max width breakpoints
      @each $bp-min, $length-min in $_lengths {
        @each $bp-max, $length-max in $_lengths {
          @if (
            jigsass-strip-unit($length-min) != 0 and
            jigsass-strip-unit($length-max) != 0 and
            $length-max > $length-min
          ) {
            $selector-map: map-merge(
              $selector-map,
              (from-#{$bp-min}-until-#{$bp-max}: (no-modifier: false))
            );
          }
        }
      }
    }

    // Misc media query features breakpoints
    @if ($_features) {
      @each $misc-bp, $expression in $_features {
        $selector-map: map-merge($selector-map, (when-#{$misc-bp}: (no-modifier: false)));
      }

      // Width and misc media queries
      @if($_lengths) {
        // Min width and feature breakpoints
        @each $bp-min, $length-min in $_lengths {
          @if (jigsass-strip-unit($length-min) != 0) {
            @each $misc-bp, $expression in $_features {
              $selector-map: map-merge(
                $selector-map,
                (from-#{$bp-min}-when-#{$misc-bp}: (no-modifier: false))
              );
            }
          }
        }

        // Max width and feature breakpoints
        @each $bp-max, $length-max in $_lengths {
          @if (jigsass-strip-unit($length-max) != 0) {
            @each $misc-bp, $expression in $_features {
              $selector-map: map-merge(
                $selector-map,
                (until-#{$bp-max}-when-#{$misc-bp}: (no-modifier: false))
              );
            }
          }
        }

        // Min and max width and feature breakpoints
        @each $bp-min, $length-min in $_lengths {
          @each $bp-max, $length-max in $_lengths {
            @if (
              jigsass-strip-unit($length-min) != 0 and
              jigsass-strip-unit($length-max) != 0 and
              $length-max > $length-min
            ) {
              @each $misc-bp, $expression in $_features {
                $selector-map: map-merge(
                  $selector-map,
                  (from-#{$bp-min}-until-#{$bp-max}-when-#{$misc-bp}: (no-modifier: false))
                );
              }
            }
          }
        }
      }
    }

    // Cache selector-map
    $_jigsass-selector-map: $selector-map !global;
  }


  @return ($name: $selector-map);
}
