// @import '~mathsass/dist/math';
// @import '~sass-unitconverter/unitconverter';

// ** Utils ********************************************************************
// == Data Structure ===========================================================
// -- List ---------------------------------------------------------------------
// -- To List --
@function range($values) {
  $iter-num: 0;
  $range-list: ();

  // Get Length
  @if is-num($values) {
    $iter-num: $values;
  }
  @else {
    $iter-num: length($values);
  }

  // Append of count
  @for $i from 1 through $iter-num {
    $range-list: append($range-list, $i);
  }
  @return $range-list;
}

@function repeat($value, $size) {
  $repeat-list:();
  @each $i in range($size) {
    $repeat-list: append($repeat-list, $value);
  }
  @return $repeat-list;
}

@function list($args...) {
  $list: ();
  @each $arg in $args {
    $list: append($list, $arg);
  }
  @return $list;
}

@function flatten($list...) {
  $newList: ();
  @each $value in $list {
    @if is-list($value) {
      // One Depth Flatten
      @each $deepV in $value {
        $newList: append($newList, $deepV);
      }
    }
    @else {
      $newList: append($newList, $value);
    }
  }
  @return $newList;
}

// -- At List --
@function contain($values, $item, $type: 'key') {
  @if      is-list($values) {
    @return index($values, $item) != null;
  }
  @else if is-map($values) {
    @if      $type == 'key' {
      @return map-has-key($values, $item);
    }
    @else if $type == 'value' {
      @return contain(map-values($values), $item);
    }
    @else if $type == 'map' {
      @return not is-null(map-index($values, $item, 'map'));
    }
  }
}

// https://hugogiraudel.com/2013/08/08/advanced-sass-list-functions/
@function replace-nth($list, $index, $value) {
  $result: null;

  @if not is-num($index) {
    @warn "$index: #{quote($index)} is not a number for `replace-nth`.";
  } @else if $index == 0 {
    @warn "List index 0 must be a non-zero integer for `replace-nth`.";
  } @else if abs($index) > length($list) {
    @warn "List index is #{$index} but list is only #{length($list)} item long for `replace-nth`.";
  } @else {
    $result: ();
    $index: if($index < 0, length($list) + $index + 1, $index);

    @for $i from 1 through length($list) {
      @if $i == $index {
        $result: append($result, $value);
      } @else {
        $result: append($result, nth($list, $i));
      }
    }
  }

  @return $result;
}

// https://gist.github.com/Jakobud/ec056b52f3673cc369dc97f2c2428424
@function list-remove($list, $index) {
  $newList: ();
  @for $i from 1 through length($list) {
    @if $i != $index {
      $newList: append($newList, nth($list,$i), 'space');
    }
  }
  @return $newList;
}

// -- Map ----------------------------------------------------------------------
@function map-index($map, $key-value, $type: 'key') {
  @if      $type == 'key'   {
    @return index(map-keys($map),   $key-value);
  }
  @else if $type == 'value' {
    @return index(map-values($map), $key-value);
  }
  @else if $type == 'map'   {
    @return index($map, to-list($key-value));
  }
}

@function map-nth($map, $n, $type: 'key') {
  @if      $type == 'key'   {
    @return nth(nth($map, $n), 1);
  }
  @else if $type == 'value' {
    @return nth(nth($map, $n), 2);
  }
}

// -- Convert ------------------------------------------------------------------
// https://hugogiraudel.com/2014/04/28/casting-map-into-list/
@function to-list($value, $keep: 'both') {
  $keep: if(index('keys' 'values', $keep), $keep, 'both');

  @if is-map($value) {
    $keys: ();
    $values: ();

    // Each Sets
    @each $key, $val in $value {
      $keys: append($keys, $key);
      $values: append($values, $val);
    }

    // Types of list
    @if $keep == 'keys' {
      @return $keys;
    } @else if $keep == 'values' {
      @return $values;
    } @else {
      @return zip($keys, $values);
    }
  }

  @return if(not is-list($value), ($value,), $value);
}

@function to-map($keys, $values) {
  $map: ();

  @each $i in range($keys) {
    $map-value: (nth($keys, $i): nth($values, $i));
    $map: map-merge($map, $map-value);
  }
  @return $map;
}

@function empty-map($x: x) {
  @return map-remove(($x:$x), $x);
}

// -- Sort ---------------------------------------------------------------------
// https://gist.github.com/Jakobud/744b98b629abe018766f6d506a2e92ae
// https://css-tricks.com/snippets/sass/sorting-function/
@function sort-compare($a, $b) {
  @if is-num($a, false) and is-num($b, false) {
    @return $a < $b;
  }

  // If string
  $order: "0" "1" "2" "3" "4" "5" "6" "7" "8" "9"
          "a" "b" "c" "d" "e" "f" "g" "h" "i" "j"
          "k" "l" "m" "n" "o" "p" "q" "r" "s" "t"
          "u" "v" "w" "x" "y" "z";

  $a: to-lower-case($a + unquote(""));
  $b: to-lower-case($b + unquote(""));

  @for $i from 1 through min(str-length($a), str-length($b)) {
    $char-a: str-slice($a, $i, $i);
    $char-b: str-slice($b, $i, $i);

    // Check
    @if $char-a and $char-b and index($order, $char-a) != index($order, $char-b) {
      @return index($order, $char-a) < index($order, $char-b);
    }
  }

  @return str-length($a) < str-length($b);
}

// -- List --
@function list-sort($list) {
  $sortedlist: ();
  @while length($list) > 0 {
    $value: nth($list,1);
    @each $item in $list {
      @if sort-compare($item, $value) {
        $value: $item;
      }
    }
    $sortedlist: append($sortedlist, $value, 'space');
    $list: list-remove($list, index($list, $value));
  }
  @return $sortedlist;
}

@function list-sort-index($list) {
  $sortedMap: ();
  @each $i in range($list) {
    $value: nth($list, $i);
    @if map-has-key($sortedMap, $value) {
      $other-i: map-get($sortedMap, $value);
      $i: if(is-list($other-i), append($other-i, $i), ($other-i, $i));
    }
    $sortedMap: map-merge($sortedMap, ($value: $i));
  }

  $sortedMap: map-sort($sortedMap);
  @return flatten(map-values($sortedMap)...);
}

// -- Map --
// https://gist.github.com/Jakobud/a0ac11e80a1de453cd86f0d3fc0a1410
@function map-sort($map) {
  $keys: list-sort(map-keys($map));
  $sortedMap: ();
  @each $key in $keys {
    $sortedMap: map-merge($sortedMap, ($key: map-get($map, $key)));
  }
  @return $sortedMap;
}

@function map-sort-values($map) {
  // Transform map to zipped list
  $keys: ();
  $values: ();

  @each $key, $val in $map {
    $keys: append($keys, $key);
    $values: append($values, $val);
  }

  $list: zip($keys, $values);

  $sortedMap: ();
  @while length($list) > 0 {

    // Find smallest pair
    $smallestPair: nth($list, 1);
    @each $pair in $list {
      $value: nth($pair, 2);
      $smallestValue: nth($smallestPair, 2);
      @if $value < $smallestValue {
        $smallestPair: $pair;
      }
    }

    // Add smallest pair to sorted map
    $key: nth($smallestPair, 1);
    $value: nth($smallestPair, 2);
    $sortedMap: map-merge($sortedMap, ($key: $value));

    // Remove from list smallest pair
    $smallestPairIndex: index($list, $smallestPair);
    $newList: ();
    @for $i from 1 through length($list) {
      @if $i != $smallestPairIndex {
        $newList: append($newList, nth($list, $i), "space");
      }
    }
    $list: $newList;
  }

  @return $sortedMap;
}

// == Math =====================================================================
@function count-round($number, $count: 1) {
  $digit: pow(10, $count - 1);

  $upper: $number * $digit;
  $round: floor($upper + 0.5);
  @return $round / $digit;
}

// == Other ====================================================================
// -- High Order ---------------------------------------------------------------
@function do($value, $functions...) {
  $result: $value;
  $functions: flatten($functions...);
  // Call each functions
  @each $function in $functions {
    $result: call($function, $result);
  }
  @return $result;
}

@function do-list($value, $functions...) {
  $result: $value;
  $functions: flatten($functions...);
  // Call each functions with full args
  @each $function in $functions {
    $result: call($function, $result...);
  }
  @return $result;
}

@function map($values, $function, $type: 'value', $separator: auto) {
  @if      is-list($values) {
    $newList: ();
    @if      $type == 'list' {
      $newList: do-list($values, $function);
    }
    @else if $type == 'value' {
      @each $value in $values {
        $newList: append($newList, do($value, $function), $separator);
      }
    }
    @return $newList;
  }
  @else if is-map($values)  {
    $newMap: ();
    @each $key, $value in $values {
      $newMap: map-merge($newMap, if($type == 'key',
                                     (do($key, $function): $value),
                                     ($key: do($value, $function))
      ));
    }
    @return $newMap;
  }
  @else {
    @return do($values, $function);
  }
}

// -- Calcable -----------------------------------------------------------------
@function is-calcable($size) {
  // Pass Value
  // - String
  //   font-size: 16px !important
  //   text-indent: 5em hanging each-line
  // - Num
  // - Percent
  @return (is-len($size) and not is-pct($size));
}

@function calcable-callback($input, $unit, $options) {
  @return is-calcable($input);
}

$check-callback: calcable-callback;

// -- Size ---------------------------------------------------------------------
@function limit-size($size, $min-size, $max-size) {
  @if not is-null($min-size) {
    $size: if($size < $min-size, $min-size, $size);
  }
  @if not is-null($max-size) {
    $size: if($size > $max-size, $max-size, $size);
  }

  @return $size;
}
@function limit-sizes($sizes, $min-size, $max-size) {
  $sizes:    to-unit-data($sizes,    px);
  $min-size: to-unit-data($min-size, px);
  $max-size: to-unit-data($max-size, px);

  $new-sizes: ();
  @each $size in $sizes {
    $new-size: if(is-calcable($size), limit-size($size, $min-size, $max-size), $size);
    $new-sizes: append($new-sizes, $new-size);
  }
  @return $new-sizes;
}

@function replace-size($sizes, $index, $subtitute-size) {
  $size: nth($sizes, $index);
  @return if(is-calcable($size) or is-calc($size),
             replace-nth($sizes, $index, $subtitute-size), $sizes);
}

@function check-sizes($sizesA, $sizesB) {
  $sizes: zip($sizesA, $sizesB);

  @each $sizeA, $sizeB in $sizes {
    @if is-calcable($sizeA) and is-calcable($sizeB) {
      @return $sizeA <= $sizeB;
    }
  }
  @return false;
}
