@charset 'UTF-8';

////
/// Flavor SCSS Types Object
/// @group types-object
/// @author blackmirror1980
////

/// Checks if something is an object/map
///
/// @access public
/// @param {any} $o - the value to check
/// @return {boolean} - true if is an object
@function is-object($o) {
  @return is-defined($o) and type-of($o)==map;
}

/// Deep get function for objects
///
/// @access public
/// @param {object | map} $o - the object to look in
/// @param {string} $p - the path to check for
/// @example scss - Usage
///   $o: (
///     prop1: (
///       subprop1: #333,
///       subprop2: #444
///     ),
///     prop2: (
///       subprop1: solid
///     ),
///     prop3: (),
///   )
///
/// @example scss - Result
///   $p: get($o, 'prop1.subprop2'); // #444
/// @return {any} - the value found at the path or null
@function get($o, $p) {
  $keys: $p;

  @if is-string($p) {
    $keys: string-split($p);
  }

  $length: length($keys);

  @if $length==1 {
    $keys: nth($keys, 1);
  }

  $get: map-get($o, nth($keys, 1));

  @if $length > 1 {
    @for $i from 2 through $length {
      @if is-object($get) {
        $get: map-get($get, nth($keys, $i));
      }
    }
  }

  @return $get;
}

@function get-keys($keys, $counter) {
  $return: ();
  @for $i from 1 to $counter {
    $return: append($return, nth($keys, $i));
  }
  @return $return;
}

/// Deep set function for objects
///
/// @access public
/// @param {object | map} $o - the object to modify in
/// @param {string} $p - the path to use
/// @param {any} $v - the value to use
/// @example scss - Usage
///   $o: (
///     prop1: (
///       subprop1: #333,
///       subprop2: #444
///     ),
///     prop2: (
///       subprop1: solid
///     ),
///     prop3: (),
///   )
///
///   $p: set($o, 'prop2.subprop2', #555);
///
/// @example scss - Result
///
///   $o: (
///     prop1: (
///       subprop1: #333,
///       subprop2: #444
///     ),
///     prop2: (
///       subprop1: solid
///       subprop2: #555
///     ),
///     prop3: (),
///   )
///
/// @return {any} - the object with the new value set at the specified path
@function set($o, $p, $v) {
  $keys: $p;

  @if is-string($p) {
    $keys: string-split($p);
  }

  $length: length($keys);
  $get-keys: ();
  $map-level: ();


  @if $length > 1 {
    $map-level: get($o, $keys);
  }

  $merge: (nth($keys, $length): $v);

  @if $map-level {
    $get-keys: get-keys($keys, $length);
    $merge: map-merge($map-level, $merge);
  }

  @for $i from ($length * -1 + 1) through -1 {
    $j: abs($i);
    $key: nth($keys, $j);

    @if $j > 1 {
      $get-keys: get-keys($keys, $j);
      $map-level: get($o, $get-keys);

      @if $map-level {
        $merge: map-merge($map-level, ($key: $merge));
      }
      @else {
        $merge: ($key: $merge);
      }
    }
    @else {
      $merge: ($key: $merge);
    }
  }

  $o: map-merge($o, $merge);

  @return $o;
}

/// Deep extend/merge function for objects
///
/// @access public
/// @param {object | map} $o - the object to modify in
/// @param {object | map} $objects - the objects to merge in
/// @requires {function} is-object
/// @example scss - Usage
///   $o1: (
///     prop1: (
///       subprop1: #333,
///       subprop2: #444
///     ),
///     prop2: (
///       subprop1: solid
///       subprop2: #555
///     )
///   )
///
///   $o2: (
///     prop1: (
///       subprop3: #555,
///     ),
///     prop2: (
///       subprop2: Verdana
///     ),
///   )
///
///   $o3: (
///     prop3: (
///       subprop1: 1px
///     ),
///   )
///
/// @example scss - Result
///
///   $o: (
///     prop1: (
///       subprop1: #333,
///       subprop2: #444,
///       subprop3: #555,
///     ),
///     prop2: (
///       subprop1: solid
///       subprop2: Verdana
///     ),
///     prop3: (
///       subprop1: 1px
///     ),
///   )
///
/// @return {any} - the new extended object
@function extend($o, $objects...) {
  $max: length($objects); // Loop through all maps in $objects...

  @for $i from 1 through $max {
    // Store current map
    $current: nth($objects, $i); // Loop through all tuples in current map

    @each $key, $value in $current {
      // If value is a nested map and same key from object is a nested map as well
      @if is-object($value) and is-object(map-get($o, $key)) {
        // Recursive extend
        $value: extend(get($o, $key), $value);
      } // Merge current tuple with object

      $o: map-merge($o, ($key: $value));
    }
  }

  @return $o;
}
