////
/// @group o-grid
/// @link http://registry.origami.ft.com/components/o-grid
////

/// Add a layout
///
/// @example scss
///  @include oGridAddLayout($layout-name: P, $layout-width: 600px);
///  @include oGridAddLayout($layout-name: XXL, $layout-width: 1600px, $gutter-width: 30px);
///
/// @param {String} $layout-name - Name of the layout (e.g. S)
/// @param {Number} $layout-width - Layout width in px
/// @param {Number} $gutter-width [null] - Gutter width in px
@mixin oGridAddLayout($layout-name, $layout-width, $gutter-width: null) {
	$temp-layouts: ();
	$temp-gutters: (default: oGridGutter());

	// Add the new layout in the correct position:
	// (we want $o-grid-layouts and $o-grid-gutters to be ordered from the smallest to the largest layout)
	@for $index from 1 through length($o-grid-layouts) {
		$previous-layout-width: if($index == 1, 0, map-get($o-grid-layouts, nth($_o-grid-layout-names, $index - 1)));
		$current-layout-name: nth($_o-grid-layout-names, $index);
		$current-layout-width: map-get($o-grid-layouts, $current-layout-name);

		$current-gutter-width: map-get($o-grid-gutters, $current-layout-name);

		@if not ($previous-layout-width > $layout-width or $current-layout-width < $layout-width) {
			$temp-layouts: map-merge($temp-layouts, ($layout-name: $layout-width));
			$temp-gutters: map-merge($temp-gutters, ($layout-name: $gutter-width));
		}

		$temp-layouts: map-merge($temp-layouts, ($current-layout-name: $current-layout-width));

		@if $current-gutter-width {
			$temp-gutters: map-merge($temp-gutters, ($current-layout-name: $current-gutter-width));
		}
	}
	$o-grid-layouts: $temp-layouts !global;
	$_o-grid-layout-names: map-keys($o-grid-layouts) !global;

	@if $gutter-width {
		$o-grid-gutters: $temp-gutters !global;
	}

	$_o-grid-max-width: map-get($o-grid-layouts, nth($_o-grid-layout-names, -1)) !global;
}

/// Get the gutter of a layout
///
/// @param {String|null|Boolean} $layout-name - One of $o-grid-layouts
@function oGridGutter($layout-name: default) {
	// This layout was assigned a gutter directly
	@if map-get($o-grid-gutters, $layout-name) {
		@return map-get($o-grid-gutters, $layout-name);
	}

	// Checking the position of the layout in the list of layouts
	$layout-index: index($_o-grid-layout-names, $layout-name);

	// The first layout (S) should get the default gutter
	@if $layout-index == 1 {
		@return oGridGutter();
	}

	// No gutter found for this layout, let's try again with a smaller one
	$layout: nth($_o-grid-layout-names, $layout-index - 1);
	@return oGridGutter($layout);
}

/// Get the max width of a layout
///
/// @example
///  .my-large-container { width: oGridGetMaxWidthForLayout(L); }
///
/// @param {String} $layout-name - one of $o-grid-layouts
/// @param {String} $grid-mode [$o-grid-mode]
@function oGridGetMaxWidthForLayout($layout-name, $grid-mode: $o-grid-mode) {
	$grid-is-responsive: $grid-mode != 'fixed';

	$index: index($_o-grid-layout-names, $layout-name);

	// Largest layout: return its width directly
	@if $index == length($_o-grid-layout-names) {
		@return $_o-grid-max-width;
	}

	// Smaller layouts:
	@if $grid-is-responsive {
		// - The grid is responsive (fluid or snappy):
		//   return the next larger layout width
		$next-layout: nth($_o-grid-layout-names, $index + 1);
		@return map-get($o-grid-layouts, $next-layout);
	} @else {
		// - The grid is fixed, return the current layout width
		@return map-get($o-grid-layouts, $layout-name);
	}
}

/// % width of an element in the grid
///
/// @example
///  .one-half   { width: oGridColspan(1/2); }      // 50%
///  .other-half { width: oGridColspan(one-half); } // 50%
///  .sidebar    { width: oGridColspan(5); }        // 41.66667%
///  .two-thirds { width: oGridColspan(2/3); }      // 66.66667%
///  .4-out-of-6 { width: oGridColspan(4, 6); }     // 66.66667%
///
/// @param {Number | String} $span - Number of columns the element spans over
/// @param {Number} $total-cols [$o-grid-columns] - Number of columns in the grid
///
/// @returns {Number} width of the element in the grid, in percents
@function oGridColspan($span, $total-cols: $o-grid-columns) {
	// Match the HTML helper API with human-friendly numbers
	@if $span == 'one-half'       { $span: 1/2; }
	@if $span == 'one-quarter'    { $span: 1/4; }
	@if $span == 'one-third'      { $span: 1/3; }
	@if $span == 'two-thirds'     { $span: 2/3; }
	@if $span == 'three-quarters' { $span: 3/4; }

	@if $span == 'full-width' {
		@return 100%;
	} @else {
		@if $span >= 1 {
			// A number of columns is supplied: converting it into a fraction
			// of the total number of columns
			@return percentage($span / $total-cols);
		} @else {
			// A fraction (1/2) or a number (0.5) is supplied:
			// converting it into a percentage
			@return percentage($span);
		}
	}
}

/// Apply styles at a given layout size
/// Wrapper for the Sass MQ mq() mixin
///
/// @link https://git.io/sass-mq Sass MQ documentation
///
/// @example
///  // Turn the color of an element red at medium layout size and up
///  @include oGridRespondTo(M) {
///  	element {
///  		color: red;
///  	}
///  }
///  // Turn the color of an element blue until medium layout
///  element {
///  	@include oGridRespondTo($until: M) {
///  		color: blue;
///  	}
///  }
///  // Turn the color of an element green from small layout until medium layout
///  element {
///  	@include oGridRespondTo($from: S, $until: M) {
///  		color: green;
///  	}
///  }
///
/// @param {String} from - one of $o-grid-layouts
/// @param {String} until - one of $o-grid-layouts
@mixin oGridRespondTo($from: false, $until: false) {
	$grid-is-responsive: $o-grid-mode != 'fixed';

	$original-scope: $_o-grid-scope;
	$_o-grid-scope: 'respondTo' !global;

	@include mq(
		$from: $from,
		$until: $until,
		$responsive: $grid-is-responsive,
		$breakpoints: $o-grid-layouts,
		$static-breakpoint: $o-grid-fixed-layout
	) {
		@content;
	}

	// Restore previously set scope
	$_o-grid-scope: $original-scope !global;
}

/// Target styles at Internet Explorer 8 only
@mixin oGridTargetIE8 {
	// Users may target styles at IE8, but only in the global scope,
	// but it shouldn't output any code inside of a oGridRespondTo call
	@if 'global' == $_o-grid-scope {
		@if 'inline' == $o-grid-ie8-rules {
			@media \0screen {
				@content;
			}
		}
		@if 'only' == $o-grid-ie8-rules {
			@content;
		}
	}
}

/// Target styles at modern browsers that support @media queries properly
@mixin oGridTargetModernBrowsers {
	/// Output code as-is if called inside of a media query,
	/// since it will target modern browsers already
	@if $_o-grid-scope == 'respondTo' {
		@content;
	} @else {
		@if not ('only' == $o-grid-ie8-rules) {
			@media only screen {
				@content;
			}
		}
	}
}


/// Human friendly names for portions and centering:
///
/// - hide
/// - full-width
/// - one-half
/// - one-third
/// - two-thirds
/// - one-quarter
/// - three-quarters
/// - center
/// - uncenter
///
/// @access private
///
/// @param {String} $layout-name [null]
@mixin _oGridHumanFriendlyKeywords($layout-name: null) {
	[data-o-grid-colspan~="#{$layout-name}hide"] {
		display: none;
	}

	// Center and un-center
	[data-o-grid-colspan~="#{$layout-name}center"] {
		@include oGridCenter;
	}
	// Since writing [data-o-grid-colspan~="uncenter"] wouldn't make much sense
	// we only allow "uncenter" combined with a layout (e.g. XLuncenter)
	@if $layout-name != null {
		[data-o-grid-colspan~="#{$layout-name}uncenter"] {
			@include oGridUncenter;
		}
	}

	// Portions
	@each $human-friendly-name in (full-width, one-half, one-third, two-thirds, one-quarter, three-quarters) {
		[data-o-grid-colspan~="#{$layout-name}#{$human-friendly-name}"] {
			// sass-lint:disable mixins-before-declarations

			// Restore visibility from `display: none`
			// if `data-o-grid-colspan` was set to `0` or `hide`
			display: block;

			// Define width in %
			@include _oGridFixSafariWrap($human-friendly-name);
			min-width: oGridColspan($human-friendly-name);
			max-width: oGridColspan($human-friendly-name);
			width: oGridColspan($human-friendly-name);
		}
	}
}

/// Shuffle columns around using pull, push and offset
///
/// @access private
///
/// @param {String} $layout-name [null]
@mixin _oGridShuffleColumns($layout-name: null) {
	@for $colspan from 0 through ($o-grid-columns - 1) {
		[data-o-grid-colspan~="#{$layout-name}push#{$colspan}"] {
			@include oGridPush($colspan);
		}
		[data-o-grid-colspan~="#{$layout-name}pull#{$colspan}"] {
			@include oGridPull($colspan);
		}
		[data-o-grid-colspan~="#{$layout-name}offset#{$colspan}"] {
			@include oGridOffset($colspan);
		}
	}
}

/// Base column styles and responsive layout width
///
/// @example scss Block has column styles
///   el { @include oGridColspan(); }
///
/// @example scss 4-column wide block
///   el { @include oGridColspan(4); }
///
/// @example scss Half-width block
///   el { @include oGridColspan(1/2); }
///
/// @example scss Block is full-width by default, 8-column wide on Medium layouts and hidden on Large layouts
///   el { @include oGridColspan((default: 12, M: 8, L: hide)); }
///
/// @param {Number | Map} $span [null]
@mixin oGridColspan($span: null, $width-only: false) {
	@if not $width-only {
		position: relative; // Required for push and pull
		float: left;
		flex: 1 1 0%;
		// Turn autoprefixer off for the box-sizing rules
		// in order to fix positioning on older browsers.
		/*autoprefixer: off*/
		// sass-lint:disable no-vendor-prefixes
		-webkit-box-sizing: border-box;
		-moz-box-sizing: border-box;
		box-sizing: border-box;
		// sass-lint:enable no-vendor-prefixes

		@include oGridTargetIE8 {
			// sass-lint:disable no-important
			min-width: 0 !important;
		}

		@if $o-grid-mode == 'fixed' {
			padding-left: oGridGutter($o-grid-fixed-layout);
		} @else {
			@each $layout-name in map-keys($o-grid-gutters) {
				@if $layout-name == 'default' {
					padding-left: oGridGutter();
				} @else {
					@include oGridRespondTo($layout-name) {
						padding-left: oGridGutter($layout-name);
					}
				}
			}

			@include oGridTargetIE8 {
				// Output grid modifiers for the fixed layout displayed by IE8
				padding-left: oGridGutter($o-grid-fixed-layout);
			}
		}
	}
	@if $span {
		@include _oGridColumnWidth($span);
	}
}

/// Cross browser column widths across layouts
///
/// @example scss
///   el { @include _oGridColumnWidth(4); }
///   el { @include _oGridColumnWidth(1/2); }
///   el { @include _oGridColumnWidth(hide); }
///   el { @include _oGridColumnWidth((default: 12, M: 8, L: hide)); }
///
/// @param {Number | Map} $span
@mixin _oGridColumnWidth($span) {
	// Special case: the column is hidden by default
	@if $span == 'hide' {
		display: none;
	} @else {
		// $span is a number or a keyword, so we're outputting the default width for that column
		@if type-of($span) == number or type-of($span) == string {
			// sass-lint:disable mixins-before-declarations

			// Restore visibility from `display: none`
			// if `data-o-grid-colspan` was set to `0` or `hide`
			display: block;

			// Define width in %
			@include _oGridFixSafariWrap($span);
			min-width: oGridColspan($span);
			max-width: oGridColspan($span);

			// For IE8
			width: oGridColspan($span);
		}
	}

	// $span is a map, we're looping through all of the layouts
	@if type-of($span) == map {
		@each $layout-name, $layout-span in $span {
			@if $layout-name == 'default' {
				@include _oGridColumnWidth($layout-span);
			} @else {
				@if $layout-span == 'hide' {
					// Target IE8 only if the layout is smaller than the maximum width of the fixed layout
					@if index($_o-grid-layout-names, $layout-name) <= index($_o-grid-layout-names, $o-grid-fixed-layout) {
						@include oGridTargetIE8 {
							display: none;
						}
					}
					@include oGridRespondTo($layout-name) {
						display: none;
					}
				} @else {
					@include oGridRespondTo($layout-name) {
						// sass-lint:disable mixins-before-declarations

						// Restore visibility from `display: none`
						// if `data-o-grid-colspan` was set to `0` or `hide`
						display: block;

						// Define width in %
						@include _oGridFixSafariWrap($layout-span);
						min-width: oGridColspan($layout-span);
						max-width: oGridColspan($layout-span);
					}
					// Target IE8 only if the layout is smaller than the maximum width of the fixed layout
					@if index($_o-grid-layout-names, $layout-name) <= index($_o-grid-layout-names, $o-grid-fixed-layout) {
						@include oGridTargetIE8 {
							display: block;
							width: oGridColspan($layout-span);
						}
					}
				}
			}
		}
	}
}

/// Grid container
///
/// @param {String} $grid-mode [$o-grid-mode]
/// @param {Boolean} $bleed [false]
@mixin oGridContainer($grid-mode: $o-grid-mode, $bleed: false) {
	position: relative;
	margin-left: auto;
	margin-right: auto;
	min-width: $o-grid-min-width;
	// Older browsers get a fixed-width layout
	max-width: oGridGetMaxWidthForLayout($o-grid-fixed-layout);

	// Turn autoprefixer off for the box-sizing rules
	// in order to fix positioning on older browsers.
	/*autoprefixer: off*/
	// sass-lint:disable no-vendor-prefixes
	-webkit-box-sizing: border-box;
	-moz-box-sizing: border-box;
	box-sizing: border-box;
	// sass-lint:enable no-vendor-prefixes

	@if $bleed {
		padding-left: 0;
		padding-right: 0;
	}

	@if $grid-mode == 'fixed' {
		// If the grid isn't fluid, we set it to a certain width
		width: oGridGetMaxWidthForLayout($o-grid-fixed-layout, $grid-mode: 'fixed');
		@if not $bleed {
			padding-left: oGridGutter($o-grid-fixed-layout);
			padding-right: oGridGutter($o-grid-fixed-layout);
		}
	} @else {
		max-width: $_o-grid-max-width;

		@each $layout-name in map-keys($o-grid-gutters) {
			@if $layout-name == 'default' {
				@if not $bleed {
					padding-left: oGridGutter();
					padding-right: oGridGutter();
				}
			} @else {
				@include oGridRespondTo($layout-name) {
					@if not $bleed {
						padding-left: oGridGutter($layout-name);
						padding-right: oGridGutter($layout-name);
					}
				}
			}
		}

		@each $layout-name in $_o-grid-layout-names {
			@if index($_o-grid-layout-names, $layout-name) >= index($_o-grid-layout-names, $o-grid-start-snappy-mode-at) {
				@include oGridRespondTo($layout-name) {
					// If the grid mode is snappy, all rows should be snappy
					@if $grid-mode == 'snappy' {
						max-width: map-get($o-grid-layouts, $layout-name);
					}
					@if $grid-mode == 'fluid' {
						// If the grid mode is fluid, then use a class to make a row or a set of rows snappy
						.o-grid-snappy &,
						&--snappy {
							max-width: map-get($o-grid-layouts, $layout-name);
						}
					}
				}
			}
		}

		// Serve a fixed-width layout to IE8
		@include oGridTargetIE8 {
			@if not $bleed {
				padding-left: oGridGutter($o-grid-fixed-layout);
				padding-right: oGridGutter($o-grid-fixed-layout);
			}
			width: oGridGetMaxWidthForLayout($o-grid-fixed-layout, $grid-mode: 'fixed');
		}
	}
}

/// Base row styles
///
/// @param {String} $grid-mode [$o-grid-mode]
@mixin oGridRow {
	clear: both;
	flex-wrap: wrap; // Note that this breaks in old Firefox
	display: flex;

	@media print {
		display: inherit;
	}

	@if $o-grid-mode == 'fixed' {
		margin-left: -1 * oGridGutter($o-grid-fixed-layout);
	} @else {
		@each $layout-name in map-keys($o-grid-gutters) {
			@if $layout-name == 'default' {
				margin-left: -1 * oGridGutter();
			} @else {
				@include oGridRespondTo($layout-name) {
					margin-left: -1 * oGridGutter($layout-name);
				}
			}
		}

		@include oGridTargetIE8 {
			margin-left: -1 * oGridGutter($o-grid-fixed-layout);
		}
	}

	// Clearfix for IE9 and below
	zoom: 1;

	&:before,
	&:after {
		content: '';
		display: table;
		display: flex; // sass-lint:disable-line no-duplicate-properties
	}
	&:after {
		clear: both;
	}
}

/// Remove gutters from columns in a row
///
/// @param {string} column child selector
@mixin oGridRowCompact($column-selector: "[o-grid-colspan]") {
	margin-left: 0;

	> #{unquote($column-selector)} {
		padding-left: 0;
	}
}

/// Remove row styles
@mixin oGridResetRow {
	clear: none;
	display: block;
	flex-wrap: nowrap;
	margin-left: 0;

	&:before,
	&:after {
		display: none;
	}
}

/// Center column
@mixin oGridCenter {
	margin-left: auto;
	margin-right: auto;
	float: none;
}

/// Uncenter column
@mixin oGridUncenter {
	margin-left: 0;
	margin-right: 0;
	float: left;
}

/// Remove column styles
@mixin oGridResetColumn {
	padding-left: 0;
	padding-right: 0;
	float: none;
	width: auto;
	min-width: 0;
	max-width: none;
	flex: none;
}

/// Reorder visually: pull
///
/// @param {Number} $columns
@mixin oGridPull($columns) {
	right: oGridColspan($columns);
	left: auto;
}

/// Reorder visually: push
///
/// @param {Number} $columns
@mixin oGridPush($columns) {
	left: oGridColspan($columns);
	right: auto;
}

/// Offset: add space before a column
///
/// @param {Number} $columns
@mixin oGridOffset($columns) {
	margin-left: oGridColspan($columns);
}

/// Width and gutter removal modifiers for a given layout.
///
/// @output
///  [data-o-grid-colspan~="S1"] { width: %; }
///
/// @access private
///
/// @param {String} $layout-name - One of $o-grid-layouts
@mixin _oGridModifiersForLayout($layout-name) {
	@if $o-grid-human-friendly-selectors == true {
		@include _oGridHumanFriendlyKeywords($layout-name);
	}

	@if $o-grid-shuffle-selectors == true {
		@include _oGridShuffleColumns($layout-name);
	}

	[data-o-grid-colspan~="#{$layout-name}0"] {
		display: none;
	}

	@for $colspan from 1 through $o-grid-columns {
		[data-o-grid-colspan~="#{$layout-name}#{$colspan}"] {
			// sass-lint:disable mixins-before-declarations

			// Restore visibility from `display: none`
			// if `data-o-grid-colspan` was set to `0` or `hide`
			display: block;

			// Apply width in %
			@include _oGridFixSafariWrap($colspan);
			min-width: oGridColspan($colspan);
			max-width: oGridColspan($colspan);
			width: oGridColspan($colspan);
		}
	}
}

/// Fix a bug in Safari where items wouldn't wrap properly
/// @link https://github.com/philipwalton/flexbugs#11-min-and-max-size-declarations-are-ignored-when-wrapping-flex-items
@mixin _oGridFixSafariWrap($args...) {
	flex-basis: oGridColspan($args...);
}

/// Generate the grid with helper classes for:
/// - older browsers (no columns, @media query support)
/// - IE 8 (fixed layout, with columns)
/// - modern browsers (fluid layout, with columns)
@mixin oGridGenerate {
	// Basic layout styles
	.o-grid-container {
		@include oGridContainer($o-grid-mode);
	}
	.o-grid-container--bleed {
		padding-left: 0;
		padding-right: 0;
	}
	.o-grid-row {
		@include oGridRow;
	}
	[data-o-grid-colspan] {
		@include oGridColspan();
	}


	[data-o-grid-colspan~="0"] {
		display: none;
	}
	@for $colspan from 1 through $o-grid-columns {
		[data-o-grid-colspan~="#{$colspan}"] {
			@include oGridColspan($colspan, $width-only: true);
		}
	}

	// Compact, gutterless row of columns
	.o-grid-row--compact {
		margin-left: 0;

		> [data-o-grid-colspan] {
			padding-left: 0;
		}
	}

	@if $o-grid-human-friendly-selectors == true {
		// one-half, one-third, three-quarters, center, uncenter…
		@include _oGridHumanFriendlyKeywords;
	}

	@if $o-grid-shuffle-selectors == true {
		// pull, push, offset
		@include _oGridShuffleColumns;
	}

	// For IE 8, output grid helper classes and data- attributes
	// for the layout defined in $o-grid-fixed-layout
	@include oGridTargetIE8 {
		// Output grid modifiers for layouts up to the fixed layout displayed by IE8
		$last-layout-index: index($_o-grid-layout-names, $o-grid-fixed-layout);
		@for $index from 1 through $last-layout-index {
			@include _oGridModifiersForLayout(nth($_o-grid-layout-names, $index));
		}
	}

	// In browsers that support @media queries,
	// output grid helper classes and data- attributes for all layouts
	@each $layout-name in $_o-grid-layout-names {
		@include oGridRespondTo($layout-name) {
			@include _oGridModifiersForLayout($layout-name);
		}
	}
}
