// Foundation for Sites by ZURB
// foundation.zurb.com
// Licensed under MIT Open Source

////
/// @group breakpoint
////

@function map-to-list($map, $keep: 'both') {
    $keep: if(index('keys' 'values', $keep), $keep, 'both');

    @if type-of($map) == 'map' {
        $keys: ();
        $values: ();

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

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

    @return if(type-of($map) != 'list', ($value,), $map);
}

/// A list of named breakpoints. You can use these with the `breakpoint()` mixin to quickly create media queries.
/// @type Map
$breakpoints: (
    small: 0,
    medium: 640px,
    large: 1024px,
    xlarge: 1200px,
    xxlarge: 1440px
) !default;

/// The largest named breakpoint in which to include print as a media type
/// @type Keyword
$print-breakpoint: large !default;

$-zf-zero-breakpoint: small !default;
$-zf-breakpoints-keys: map-to-list($breakpoints, 'keys');

@if nth(map-values($breakpoints), 1) != 0 {
    @error 'Your smallest breakpoint (defined in $breakpoints) must be set to "0".';
} @else {
    $-zf-zero-breakpoint: nth(map-keys($breakpoints), 1);
}

/// All of the names in this list will be output as classes in your CSS, like `.small-12`, `.medium-6`, and so on. Each value in this list must also be in the `$breakpoints` map.
/// @type List
$breakpoint-classes: (small medium large) !default;

@function -zf-map-next($map, $key) {
    $values: map-keys($map);
    $i: 0;

    @if (map-has-key($map, $key)) {
        $i: index($values, $key) + 1;
    }

    @if ($i > length($map) or $i == 0) {
        @return null;
    } @else {
        @return map-get($map, nth($values, $i));
    }
}

@function breakpoint($val: $-zf-zero-breakpoint) {
    $bp: nth($val, 1);
    $bp-max: 0;
    $dir: if(length($val) > 1, nth($val, 2), up);
    $str: '';
    $named: false;

    @if $bp == 'landscape' or $bp == 'portrait' {
        @return '(orientation: #{$bp})';
	} @else if $bp == 'retina' {
        @return '(-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi)';
    }

    @if type-of($bp) == 'string' {
        @if map-has-key($breakpoints, $bp) {
            @if $dir == 'only' or $dir == 'down' {
                $bp-max: -zf-map-next($breakpoints, $bp);
            }

            $bp: map-get($breakpoints, $bp);
            $named: true;
        } @else {
            $bp: 0;

            @warn 'breakpoint(): "#{$val}" is not defined in your $breakpoints setting.';
        }
    }

    @if $bp > 0 or $dir == 'only' or $dir == 'down' {
        @if $dir == 'only' {
            @if $named == true {
                @if $bp > 0 {
                    $str: $str + '(min-width: #{$bp + 1})';

                    @if $bp-max != null {
                        $str: $str + ' and ';
					}
			  	}

                @if $bp-max != null {
                    $str: $str + '(max-width: #{$bp-max})';
			    }
			} @else {
                @warn 'breakpoint(): Only named media queries can have an `only` range.';
			}
	   	} @else if $dir == 'down' {
            $max: if($named, $bp-max, $bp);

            @if $named or $bp > 0 {
                @if $max != null {
                    $str: $str + '(max-width: #{$max})';
                }
            }
		} @else if $bp > 0 {
            $str: $str + '(min-width: #{$bp + 1})';
	    }
    }

    @return $str;
}

/// Wraps a media query around the content you put inside the mixin. This mixin accepts a number of values:
///  - If a string is passed, the mixin will look for it in the `$breakpoints` map, and use a media query there.
///  - Unlike the Foundation default mixin every value is passed will not be converted to **em** values.
///
/// If multiple values are passed, the mixin will generate a media query for each of them as described above.
/// Since the content is duplicated for each breakpoint, this mixin should only be used with properties that
/// change across breakpoints.
///
/// @param {Keyword|Number} $value - Breakpoint name or px value to process.
///
/// @output If the breakpoint is "0px and larger", outputs the content as-is. Otherwise, outputs the content wrapped in a media query.
///
/// The `only` parameter has been added. Ex. `@media only screen()`.
@mixin breakpoint($values...) {
    @for $i from 1 through length($values) {
        $value: nth($values, $i);
        $str: breakpoint($value);
        $bp: index($-zf-breakpoints-keys, $value);
        $pbp: index($-zf-breakpoints-keys, $print-breakpoint);
        $old-zf-size: null;

        @if global-variable-exists(-zf-size) {
            $old-zf-size: $-zf-size;
        }

        $-zf-size: nth($value, 1) !global;

        @if $str == '' {
            @content;
        } @else {
            @if $bp != null and $bp <= $pbp {
                @media print, only screen and #{$str} {
                    @content;
                }
            } @else {
                @media only screen and #{$str} {
                    @content;
                }
            }
        }

        $-zf-size: $old-zf-size !global;
    }
}

@function -zf-breakpoints-less-than($key) {
    $list: ();
    $found_key: false;

    @each $name in $-zf-breakpoints-keys {
        @if ($name == $key) {
            $found_key: true;
        }

        @if not $found_key {
            $list: append($list, $name);
        }
    }

    @return $list;
}

@function -zf-closest-named-breakpoint($breakpoint) {
    $last: $-zf-zero-breakpoint;
    $found: false;
    $value: unitless-calc($breakpoint, 1px);

    @each $key, $val in $breakpoints {
        @if not $found {
            @if unitless-calc($val) > $value {
                $found: true;
            } @else {
                $last: $key;
            }
        }
    }

    @return $last;
}

@function -zf-get-bp-val($map, $value) {
    @if type-of($map) == 'number' {
        @return $map;
    }

    @if not map-has-key($breakpoints, $value) {
        @if type-of($value) == 'number' {
            $value: -zf-closest-named-breakpoint($value);
        } @else {
            @return null;
        }
    } @else if map-has-key($map, $value) {
        @return map-get($map, $value);
    } @else {
        $anchor: null;
        $found: false;

        @each $key, $val in $breakpoints {
            @if not $found {
                @if map-has-key($map, $key) {
                    $anchor: $key;
                }

                @if $key == $value {
                    $found: true;
                }
            }
        }

        @return map-get($map, $anchor);
    }
}

@mixin -zf-each-breakpoint($small: true, $auto-insert-breakpoints: true) {
    $list: $breakpoint-classes;

    @if not $small {
        $list: sl-remove($list, $-zf-zero-breakpoint);
    }

    @each $name in $list {
        $-zf-size: $name !global;

        @if $auto-insert-breakpoints {
            @include breakpoint($name) {
                @content;
            }
        } @else {
            @content;
        }
    }
}

@mixin -zf-breakpoint-value($name: auto, $map: null) {
    @if $name == auto and type-of($map) == 'map' {
        @each $k, $v in $map {
            @include breakpoint($k) {
                @include -zf-breakpoint-value($v, $map) {
                    @content;
                }
            }
        }
    } @else {
        @if type-of($name) == 'string' {
            $name: -zf-get-bp-val($map, $name);
        }

        $-zf-bp-value: $name !global;
        @content;
    }
}
