// PLUMBER - Easy baseline grids with SASS
// https://jamonserrano.github.io/plumber-sass
// Copyright 2016 Viktor Honti
// MIT License

@mixin plumber(
	$font-size: null,
	$line-height: null,
	$leading-top: null,
	$leading-bottom: null,
	$grid-height: null,
	$baseline: null,
	$use-baseline-origin: null
) {
	// *** VALIDATE PARAMETERS ***
	// font-size
	@if not $font-size {
		$font-size: -plumber-get-default(font-size);
	}
	@if not unitless($font-size) or $font-size <= 0 {
		@error '$font-size parameter must be a positive unitless number, got #{$font-size} instead';
	}

	// line-height
	@if not $line-height {
		$line-height: -plumber-get-default(line-height);
	}
	@if not unitless($line-height) or $line-height != round($line-height) or $line-height < 1 {
		@error '$line-height parameter must be a positive unitless integer, got #{$line-height} instead';
	}

	// leading-top
	@if not $leading-top {
		$leading-top: -plumber-get-default(leading-top);
	}
	@if not -plumber-is-integer($leading-top) {
		@error '$leading-top parameter must be a non-negative integer, got #{$leading-top} instead.';
	}

	// leading-bottom
	@if not $leading-bottom {
		$leading-bottom: -plumber-get-default(leading-bottom);
	}
	@if not -plumber-is-integer($leading-bottom) {
		@error '$leading-bottom parameter must be a non-negative integer, got #{$leading-bottom} instead';
	}

	// grid-height
	@if not $grid-height {
		$grid-height: -plumber-get-default(grid-height);
	}
	@if unitless($grid-height) or $grid-height < 0 {
		@error '$grid-height parameter must be a positive unit, got #{$grid-height} instead';
	}

	// baseline
	@if not $baseline {
		$baseline: -plumber-get-default(baseline);
	}
	@if not $baseline {
		@error '$baseline must be passed as a parameter or defined in defaults';
	} @else if not (unitless($baseline) and $baseline >= 0 and $baseline < 1) {
		@error '$baseline parameter must be a unitless fraction between 0 and 1, got #{$baseline} instead';
	}

	// use-baseline-origin
	@if not $use-baseline-origin {
		$use-baseline-origin: -plumber-get-default(use-baseline-origin);
	}
	@if type-of($use-baseline-origin) != bool {
		@error '$use-baseline-origin parameter must be Boolean, got #{$use-baseline-origin} instead';
	}

	// *** CALCULATE BASELINE CORRECTION ***
	// the distance of the original baseline from the bottom
	$baseline-from-bottom: ($line-height - $font-size) / 2 + ($font-size * $baseline);
	// the corrected baseline will be on the nearest gridline
	$corrected-baseline: round($baseline-from-bottom);
	// the difference between the original and the corrected baseline
	$baseline-difference: $corrected-baseline - $baseline-from-bottom;
	
	// if baseline origin is used for leadings substract the distance of the baseline from the edges
	@if $use-baseline-origin == true {
		$leading-top: $leading-top - ($line-height - $corrected-baseline);
		$leading-bottom: $leading-bottom - $corrected-baseline;
	}

	// *** CALCULATE FONT SIZE AND LINE HEIGHT
	$font-size: $font-size * $grid-height;
	$line-height: $line-height * $grid-height;

	// *** CALCULATE MARGINS AND PADDINGS ***
	$padding-top: null;
	$margin-top: null;
	$margin-bottom: null;
	$padding-bottom: null;
	
	@if $baseline-difference < 0 {
		// add top leading
		$margin-top: $leading-top * $grid-height;
		// push the baseline down to the next gridline
		$padding-top: - $baseline-difference * $grid-height;
		// add the remaining distance to reach the next gridline
		$padding-bottom: (1 + $baseline-difference) * $grid-height;
		// add bottom leading and remove the 1 excess grid height that comes from pushing down
		$margin-bottom: ($leading-bottom - 1) * $grid-height;
	} @else {
		// add top leading and remove the 1 excess grid height that comes from pulling up
		$margin-top: ($leading-top - 1) * $grid-height;
		// pull the baseline up to the previous gridline
		$padding-top: (1 - $baseline-difference) * $grid-height;
		// add the remaining distance to reach the next gridline
		$padding-bottom: $baseline-difference * $grid-height;
		// add bottom leading
		$margin-bottom: $leading-bottom * $grid-height;
	}
	
	// round pixel values to decrease browser inconsistencies
	@if unit($grid-height) == "px" {
		$line-height: -plumber-round($line-height);
		$margin-top: -plumber-round($margin-top);
		$padding-top: -plumber-round($padding-top);
		$padding-bottom: -plumber-round($padding-bottom);
		$margin-bottom: -plumber-round($margin-bottom);
	}

	// *** CSS OUTPUT ***
	font-size: $font-size;
	line-height: $line-height;
	margin-top: $margin-top;
	padding-top: $padding-top;
	padding-bottom: $padding-bottom;
	margin-bottom: $margin-bottom;
}

// *** DEFAULTS *** 
// Do not change it here, use the plumber-set-defaults mixin instead!
$-plumber-defaults: (
	font-size: 2,
	line-height: 3,
	leading-top: 0,
	leading-bottom: 0,
	grid-height: 1rem,
	baseline: null,
	use-baseline-origin: false,
) !default;

// Merge provided settings into the defaults map 
@mixin plumber-set-defaults($defaults...) {
	$-plumber-defaults: map-merge($-plumber-defaults, keywords($defaults)) !global;
}

// Get a default value 
@function -plumber-get-default($key) {
	@return map-get($-plumber-defaults, $key);
}

// Check if a value is a non-negative integer
@function -plumber-is-integer($value) {
	@return (unitless($value) and $value == round($value));
}

// Round value to the nearest quarter pixel
// This provides reasonable precision and prevents grid creep (by fractions adding up) in most browsers
@function -plumber-round($value) {
	@return round($value * 4) / 4;
}
