////
/// @group data-grid
/// A page grid layout component that uses data attribute instead of class names, for brevity and readability
////

@use "sass:map";
@use "sass:math";

@use "../breakpoint";
@use "../color";
@use "../element";
@use "../layout";
@use "../selector";
@use "../typography";
@use "../utils";

/// Module Settings
/// @type Map
/// @prop {String} attribute [data-grid] Default attribute to use for grid mixin.
/// @prop {String} attribute-container [data-grid-container] Default attribute to use for grid's container.
/// @prop {Number} columns [12] Default for grid mixin.
/// @prop {Number} gutter [14px] Default gutter for grid mixin.
/// @prop {String} position-class-column-first [position-column-first] Classname for position system (JS) grid uses to display rules (layout can flow, script will update classes)
/// @prop {String} position-class-column-last [position-column-last] See definition above
/// @prop {String} position-class-row-first [position-row-first] See definition above
/// @prop {String} position-class-row-last [position-row-last] See definition above
/// @prop {Dimension} sticky-bottom [var(--ulu-sticky-bottom-offset, 0)] When a column is sticky bottom this is the value for the sticky offset, set to --ulu-sticky-bottom-offset by default
/// @prop {Dimension} sticky-top [var(--ulu-sticky-top-offset, 0)] When a column is sticky top this is the value for the sticky offset, set to --ulu-sticky-top-offset by default
/// @prop {String} rule-color ["rule"] The color of the rule. This uses color.scss, so the value of this option should be a color variable from color.scss.
/// @prop {Dimension} rule-size [1px] The width of the rule
/// @prop {Map} extra-breakpoints [Map] Default extra breakpoints for grid mixin
/// @prop {Map} extra-gutter-scales [Map] Extra options for gutter scales.
/// @prop {Map} extra-rule-styles [Map] Extra options for rule styles.

$config: (
  "attribute":           "data-grid",
  "attribute-container": "data-grid-container",
  "attribute-init":      "data-grid-init",
  "breakpoint":          false,                   // Fallback to default
  "columns":             12,
  "gutter":              14px,
  "position-class-column-first": "position-column-first",
  "position-class-column-last":  "position-column-last",
  "position-class-row-first":    "position-row-first",
  "position-class-row-last":     "position-row-last",
  "sticky-bottom":               var(--ulu-sticky-bottom-offset, 0),
  "sticky-top":                  var(--ulu-sticky-top-offset, 0),
  "rule-color" : "rule",
  "rule-size" : 1px,
  "rule-fade-duration" : 400ms,
  "extra-breakpoints":   (
    "medium" : (
      "breakpoint": "medium",
      "gutter":     15px
    ),
    "large" : (
      "breakpoint": "large",
      "gutter":     20px
    )
  ),
  "extra-gutter-scales" : (
    "small": 0.6
  ),
  "extra-rule-styles" : (
    "light" : (
      "size" : 1px,
      "color": "rule-light"
    )
  ),
) !default;


/// Change modules $config
/// @param {Map} $changes Map of changes
/// @example scss
///   @include ulu.component-data-grid-set(( "property" : value ));

@mixin set($changes) {
  $config: map.merge($config, $changes) !global;
}

/// Get a config option
/// @param {Map} $name Name of property
/// @example scss
///   @include ulu.component-data-grid-get("property");

@function get($name) {
  @return utils.require-map-get($config, $name, "data-grid [config]");
}

/// Output component stylesheet
/// @example scss
///  @include ulu.component-data-grid-styles();

@function get-gutter($name: null, $custom-map: null) {
  @if (not $name) {
    @return get("gutter");
  } @else {
    $map: if($custom-map, $custom-map, get("extra-breakpoints"));
    $settings: utils.require-map-get($map, $name, "grid [get-gutter]");
    @return map.get($settings, "gutter");
  }
}

/// Get the default breakpoint for the grid (when it starts to be a grid (vs stacked/mobile)

@function get-default-breakpoint() {
  $breakpoint: get("breakpoint");
  @if ($breakpoint) {
    @return $breakpoint;
  } @else {
    @return breakpoint.get("default");
  }
}

/// Prints default grid styles, if you want to customize further please use the create mixin
/// @demo data-grid#align
/// @example scss
///  @include ulu.component-data-grid-styles();

@mixin styles() {
  @include create();
}

/// Output data grid styles
/// - Can be used instead of styles() to have full control over options or for alternate grids (grids with different settings/column count) 
/// @param {Number} $columns Columns in grid
/// @param {Number} $breakpoint Breakpoint key for starting the grid
/// @param {Map} $extra-breakpoints Map with other breakpoints to add (map of breakpoint and gutter see config.extra-breakpoints for an example (smallest to largest)
/// @param {Number} $gutter Size in pixels for the gutters
/// @param {Boolean} $include-rules Print styles for including rules
/// @param {Number} $rule-size Size of the rule (border/separator)
/// @param {Map} $extra-rule-styles Map of other rule styles to add (map of maps of size, and color), key is the styles name ("name": ("size" : 4px, "color" : "color name" || color))
/// @param {String} $extra-gutter-scales A map of gutter scales used like `data-grid="gutter-scale: large`, configuration map property becomes scale name and value is the amount (multiplier) to apply to the grid's gutter ie `( "large" : 2.25 )`
/// @param {Map} $attribute Attribute to use for selecting grid and children. Children attribute get's "-item" as a suffix ("ie. data-grid, data-grid-item")
/// @param {CssDuration} $rule-fade-duration The amount of time for rules to fade in (after init, script positioning), set to falsey value to disable rule fade in (rules will always be shown)

@mixin create(
  $columns: get("columns"),
  $breakpoint: get-default-breakpoint(),
  $extra-breakpoints: if(breakpoint.exists("large"), get("extra-breakpoints"), false),
  $gutter: get("gutter"),
  $include-rules: false,
  $rule-size: get("rule-size"),
  $rule-color: get("rule-color"),
  $extra-rule-styles: get("extra-rule-styles"),
  $extra-gutter-scales: get("extra-gutter-scales"),
  $attribute: get("attribute"),
  $attribute-init: get("attribute-init"),
  $include-container: true,
  $container-attribute: get("attribute-container"),
  $container-gutter-scales: true,
  $sticky-top: get("sticky-top"),
  $sticky-bottom: get("sticky-bottom"),
  $rule-fade-duration: get("rule-fade-duration")
) {
  $attribute-item: "#{ $attribute }-item";
  $select-grid: '[#{ $attribute }*="columns: #{ $columns }"]';
  $select-grid-not-init: '[#{ $attribute }]:not([#{ $attribute-init }])';
  $select-item: '[#{ $attribute-item }]';
  $select-rule-col: "::before";
  $select-rule-row: "::after";
  $select-container: "[#{ $container-attribute }]";
  // These through off syntax highlighting when inside interpolation
  $position-class-column-first: get("position-class-column-first");
  $position-class-column-last: get("position-class-column-last");
  $position-class-row-first: get("position-class-row-first");
  $position-class-row-last: get("position-class-row-last");

  #{ $select-grid } {
    @include layout.clearfix(); // For legacy browsers

    // Ensure empty elements still take up space in layout
    > #{ $select-item } {
      min-height: 1px; 
      width: 100%;
    }
    // This is for value of true and also covers all options below
    &#{'[#{ $attribute }*="gutters-row:"]'} { 
      > #{ $select-item } {
        padding-top:  $gutter;
        padding-bottom: $gutter;
      }
      @if $extra-gutter-scales {
        @each $scale, $amount in $extra-gutter-scales {
          &#{'[#{ $attribute }*="gutter-scale: #{ $scale }"]'} { 
            > #{ $select-item } {
              padding-top:  $gutter * $amount;
              padding-bottom: $gutter * $amount;
            }
          }
        }
      }
    }
    &#{'[#{ $attribute }*="gutters-row: top"]'} { 
      > #{ $select-item } {
        &.#{ $position-class-row-last } {
          padding-bottom: 0;
        }
      }
    }
    &#{'[#{ $attribute }*="gutters-row: bottom"]'} { 
      > #{ $select-item } {
        &.#{ $position-class-row-first } {
          padding-top: 0;
        }
      }
    }
    &#{'[#{ $attribute }*="gutters-row: fit"]'} { 
      margin-top: -$gutter;
      margin-bottom: -$gutter;
      @if $extra-gutter-scales {
        @each $scale, $amount in $extra-gutter-scales {
          &#{'[#{ $attribute }*="gutter-scale: #{ $scale }"]'} { 
            margin-top: -($gutter * $amount);
            margin-bottom: -($gutter * $amount);
          }
        }
      }
    }
    // Rules: Applies to both column and row
    &#{'[#{ $attribute }*="rules"]'} { 
      > #{ $select-item } {
        position: relative;
        @if ($rule-fade-duration) {
          &#{ $select-rule-col },
          &#{ $select-rule-row } {
            // For when grid init fades them in
            opacity: 1;
            transition: opacity $rule-fade-duration ease-in; 
          }
        }
      }
    }
    // Rules: Row
    &#{'[#{ $attribute }*="rules-row:"]'} { 
      > #{ $select-item } {
        &#{ $select-rule-row } {
          position: absolute;
          left: 0;
          right: 0;
          height: $rule-size;
          background: color.get($rule-color);
        }
      }
    }
    // Rules: Column
    &#{'[#{ $attribute }*="rules-column:"]'} { 
      > #{ $select-item } {
        &#{ $select-rule-col } {
          position: absolute;
          top: 0;
          bottom: 0;
          width: $rule-size;
          background: color.get($rule-color);
        }
      }
    }
    // Options for the left or right
    @include -create-rule-col-position(
      $position: "left", 
      $size: $rule-size, 
      $attribute: $attribute, 
      $select-item: $select-item, 
      $select-rule-col: $select-rule-col, 
      $end-class: $position-class-column-first
    );
    @include -create-rule-col-position(
      $position: "right", 
      $size: $rule-size, 
      $attribute: $attribute, 
      $select-item: $select-item, 
      $select-rule-col: $select-rule-col, 
      $end-class: $position-class-column-last
    );
    &#{'[#{ $attribute }*="rules-column-align: gutter"]'} {
      > #{ $select-item } {
        &#{ $select-rule-col } {
          top: $gutter;
          bottom: $gutter;
        }
      }
      @if $extra-gutter-scales {
        @each $scale, $amount in $extra-gutter-scales {
          &#{'[#{ $attribute }*="gutter-scale: #{ $scale }"]'} { 
            > #{ $select-item } {
              &#{ $select-rule-col } {
                top: $gutter * $amount;
                bottom: $gutter * $amount;
              }
            }
          }
        }
      }
    }
    // Options for the left or right
    @include -create-rule-row-position(
      $position: "top", 
      $size: $rule-size, 
      $attribute: $attribute, 
      $select-item: $select-item, 
      $select-rule-row: $select-rule-row, 
      $between: false
    );
    @include -create-rule-row-position(
      $position: "bottom", 
      $size: $rule-size, 
      $attribute: $attribute, 
      $select-item: $select-item, 
      $select-rule-row: $select-rule-row, 
      $between: false
    );

    &#{'[#{ $attribute }*="rules-row: between"]'} {
      > #{ $select-item } {
        &#{ $select-rule-row } {
          bottom: math.floor(math.div($rule-size, 2)) * -1;
        }
        // For removing the rule per row (via equal heights for now)
        &.#{ $position-class-row-last } {
          &#{ $select-rule-row } {
            content: none !important;
          }
        }
      }
    }
    &#{'[#{ $attribute }*="rules-row-align: gutter"]'} {
      > #{ $select-item } {
        &#{ $select-rule-row } {
          @include breakpoint.min($breakpoint) {
            left: $gutter;
            right: $gutter;
          }
        }
      }
      @if $extra-gutter-scales {
        @each $scale, $amount in $extra-gutter-scales {
          &#{'[#{ $attribute }*="gutter-scale: #{ $scale }"]'} { 
            > #{ $select-item } {
              &#{ $select-rule-row } {
                @include breakpoint.min($breakpoint) {
                  left: $gutter * $amount;
                  right: $gutter * $amount;
                }
              }
            }
          }
        }
      }
    }
    &#{'[#{ $attribute }*="rules-row-persist: true"]'},
    &#{'[#{ $attribute }*="rules-row-stacked-only: true"]'} {
      > #{ $select-item } {
        &#{ $select-rule-row } {
          content: '';
        }
      }
    }
    // IE for mobile/small (non grid / stacked)
    &#{'[#{ $attribute }*="rules-row-stacked-only: true"]'} {
      > #{ $select-item } {
        &#{ $select-rule-row } {
          @include breakpoint.min($breakpoint) {
            content: none !important;
          }
        }
      }
    }
  }

  @include breakpoint.min($breakpoint) {

    $select-gutter-false: '[#{ $attribute }*="gutters: false"]';
    $select-gutter-false-child: '#{ $select-container }:has(>#{ $select-gutter-false })';

    @if $include-container {
      #{ $select-container } {
        padding-left: $gutter;
        padding-right: $gutter;
      }
      #{ $select-gutter-false-child } {
        padding-left: 0;
        padding-right: 0;
      }
      @if $extra-gutter-scales and $container-gutter-scales {
        @each $scale, $amount in $extra-gutter-scales {
          #{'#{ $select-container }:has(>[#{ $attribute }*="gutter-scale: #{ $scale }"])'} { 
            padding-left: ($gutter * $amount);
            padding-right: ($gutter * $amount);
          }
        }
      }
    }
    // Select the attribute with matching column count
    #{ $select-grid } {
      // Defaults for grid an item
      display: flex;
      flex-flow: row wrap;
      align-items: stretch;
      margin-left: -$gutter;
      margin-right: -$gutter;

      > #{ $select-item } {
        float: left; // Fallback
        padding-left: $gutter;
        padding-right: $gutter;
      }
      > #{'[#{ $attribute-item }*="sticky: top"]'} {
        position: sticky;
        align-self: start;
        top: $sticky-top;
      }
      > #{'[#{ $attribute-item }*="sticky: bottom"]'} {
        position: sticky;
        align-self: end;
        bottom: $sticky-bottom;
      }

      @if $extra-gutter-scales {
        @each $scale, $amount in $extra-gutter-scales {
          &#{'[#{ $attribute }*="gutter-scale: #{ $scale }"]'} { 
            margin-left: -($gutter * $amount);
            margin-right: -($gutter * $amount);

            > #{ $select-item } {
              padding-left: $gutter * $amount;
              padding-right: $gutter * $amount;
            }
          }
        }
      }
      
      // Alignment options
      &#{'[#{ $attribute }*="justify: center"]'} { 
        justify-content: center;  
      }
      &#{'[#{ $attribute }*="justify: end"]'} { 
        justify-content: flex-end;  
      }
      &#{'[#{ $attribute }*="align: center"]'} { 
        align-items: center;  
      }
      &#{'[#{ $attribute }*="align: end"]'} { 
        align-items: flex-end;
      }
      &#{'[#{ $attribute }*="align: start"]'} { 
        align-items: flex-start; 
      }
      // Alignment of the children when using stretch
      // - There is no stetch-start (that's defualt);
      // - There is no stetch-middle (that's covered by stretch-);
      &#{'[#{ $attribute }*="align: stretch-"]'} { 
        // set the child to display as a single flex item
        > #{ $select-item } {
          display: flex;
          flex-direction: column;
          justify-content: center;
        }
      }
      &#{'[#{ $attribute }*="align: stretch-end"]'} { 
        > #{ $select-item } {
          justify-content: flex-end;
        }
      }
      // Other options
      &#{ $select-gutter-false } { 
        margin-left: 0;
        margin-right: 0;
        > #{ $select-item } {
          padding-left: 0;
          padding-right: 0;
        }
      }
      // Activate Rules
      &#{'[#{ $attribute }*="rules-column:"]'} {
        > #{ $select-item } {
          &#{ $select-rule-col } {
            content: '';
          }
        }
      }
      &#{'[#{ $attribute }*="rules-row:"]'} {
        > #{ $select-item } {
          &#{ $select-rule-row } {
            content: '';
          }
        }
      }
      // Override first or last column removing the rule using matching specificity
      &#{'[#{ $attribute }*="rules-column:"][#{ $attribute }*="rules-column-ends: true"]'} {
        > #{ $select-item } {
          &#{ $select-rule-col } {
            content: '';
          }
        }
      }
      // Generated props for item
      @include -create-column-widths(
        $columns: $columns,
        $attribute-item: $attribute-item,
        $suffix: ''
      );
      @include -create-column-offsets(
        $columns: $columns,
        $attribute-item: $attribute-item,
        $suffix: ''
      );
    }
  }
  @if $extra-breakpoints {
    @each $name, $options in $extra-breakpoints {
      $g: map.get($options, "gutter");
      @include -create-extra-breakpoint(
        $name: $name, 
        $breakpoint: map.get($options, "breakpoint"),
        $gutter: if($g, $g, $gutter),
        $columns: $columns,
        $attribute: $attribute,
        $attribute-item: $attribute-item,
        $select-grid: $select-grid,
        $select-item: $select-item,
        $extra-gutter-scales: $extra-gutter-scales,
        $include-container: $include-container,
        $select-container: $select-container,
        $container-gutter-scales: $container-gutter-scales
      );
    }
  }
  @if $extra-rule-styles {
    @each $name, $options in $extra-rule-styles {
      $color: utils.require-map-get($options, "color", "grid [extra rule style]");
      $size: utils.require-map-get($options, "size", "grid [extra rule style]");
      #{ $select-grid } {

        &#{'[#{ $attribute }*="rules-column-style: #{ $name }"]'} {
          > #{ $select-item } {
            &#{ $select-rule-col } {
              background-color: color.get($color);
              width: $size;
            }
          }
          @include -create-rule-col-position(
            $position: "left", 
            $size: $size, 
            $attribute: $attribute, 
            $select-item: $select-item, 
            $select-rule-col: $select-rule-col, 
            $end-class: false
          );
          @include -create-rule-col-position(
            $position: "right", 
            $size: $size, 
            $attribute: $attribute, 
            $select-item: $select-item, 
            $select-rule-col: $select-rule-col, 
            $end-class: false
          );
        }
        &#{'[#{ $attribute }*="rules-row-style: #{ $name }"]'} {
          > #{ $select-item } {
            &#{ $select-rule-row } {
              background-color: color.get($color);
              height: $size;
            }
          }
          @include -create-rule-row-position(
            $position: "top", 
            $size: $size, 
            $attribute: $attribute, 
            $select-item: $select-item, 
            $select-rule-row: $select-rule-row, 
            $between: false
          );
          @include -create-rule-row-position(
            $position: "bottom", 
            $size: $size, 
            $attribute: $attribute, 
            $select-item: $select-item, 
            $select-rule-row: $select-rule-row, 
            $between: false
          );
          @include -create-rule-row-position(
            $position: "bottom", 
            $size: $size, 
            $attribute: $attribute, 
            $select-item: $select-item, 
            $select-rule-row: $select-rule-row, 
            $between: true
          );
        }
      }
    }
  }

  // Disable grid rules until init attribute it present (from positioning script)
  // - In order to keep the rules for this compact it will only hide them if the init
  //   is not present
  
  @if ($rule-fade-duration) {
    #{ $select-grid-not-init } {
      > #{ $select-item } {
        &#{ $select-rule-col },
        &#{ $select-rule-row } {
          opacity: 0 !important;
        }
      }
    }
  }
}

@mixin -create-extra-breakpoint(
  $name, 
  $breakpoint,
  $gutter,
  $columns,
  $attribute,
  $attribute-item,
  $select-grid,
  $select-item,
  $extra-gutter-scales,
  $include-container,
  $select-container,
  $container-gutter-scales
) {
  @include breakpoint.min($breakpoint) {

    @if $include-container {
      #{ $select-container } {
        padding-left: $gutter;
        padding-right: $gutter;
      }
      @if $extra-gutter-scales and $container-gutter-scales {
        @each $scale, $amount in $extra-gutter-scales {
          #{'#{ $select-container }:has(>[#{ $attribute }*="gutter-scale: #{ $scale }"])'} { 
            padding-left: ($gutter * $amount);
            padding-right: ($gutter * $amount);
          }
        }
      }
    }

    #{ $select-grid } {
      margin-left: -$gutter;
      margin-right: -$gutter;

      > #{ $select-item } {
        padding-left: $gutter;
        padding-right: $gutter;
      }
      
      @if $extra-gutter-scales {
        @each $scale, $amount in $extra-gutter-scales {
          &#{'[#{ $attribute }*="gutter-scale: #{ $scale }"]'} { 
            margin-left: -($gutter * $amount);
            margin-right: -($gutter * $amount);
            > #{ $select-item } {
              padding-left: $gutter * $amount;
              padding-right: $gutter * $amount;
            }
          }
        }
      }

      &#{'[#{ $attribute }*="gutters-row:"]'} { 
        > #{ $select-item } {
          padding-top:  $gutter;
          padding-bottom: $gutter;
        }
        @if $extra-gutter-scales {
          @each $scale, $amount in $extra-gutter-scales {
            &#{'[#{ $attribute }*="gutter-scale: #{ $scale }"]'} { 
              > #{ $select-item } {
                padding-top:  $gutter * $amount;
                padding-bottom: $gutter * $amount;
              }
            }
          }
        }
      }

      @include -create-column-widths(
        $columns: $columns,
        $attribute-item: $attribute-item,
        $suffix: "-#{ $name }"
      );
      @include -create-column-offsets(
        $columns: $columns,
        $attribute-item: $attribute-item,
        $suffix: "-#{ $name }"
      );
    }
  }
}

@mixin -create-column-widths(
  $columns,
  $attribute-item,
  $suffix: ''
) {
  $column-width: math.div(100%, $columns);

  @for $i from 1 through $columns {
    $width: $column-width * $i;
    & > #{'[#{ $attribute-item }*="width#{ $suffix }: #{ $i }"]'} {
      width: $width;
      flex-basis: $width;
      max-width: $width;
    }
  }
}  

@mixin -create-column-offsets(
  $columns,
  $attribute-item,
  $suffix: ''
) {
  $column-width: math.div(100%, $columns);

  @for $i from 1 through $columns {
    & > #{'[#{ $attribute-item }*="offset#{ $suffix }: #{ $i }"]'} {
      margin-left: $column-width * $i;
    }
  }
  // For Zero (in case using breakpoints to remove)
  & > #{'[#{ $attribute-item }*="offset#{ $suffix }: 0"]'} {
    margin-left: 0;
  }
}

@mixin -create-rule-col-position(
  $position, 
  $size, 
  $attribute, 
  $select-item, 
  $select-rule-col, 
  $end-class: false
) {
  &#{'[#{ $attribute }*="rules-column: #{ $position }"]'} {
    > #{ $select-item } {
      &#{ $select-rule-col } {
        #{$position}: math.floor(math.div($size, 2)) * -1;
      }
      // For removing the rule per row (via equal heights for now)
      @if $end-class {
        &.#{ $end-class } {
          &#{ $select-rule-col } {
            content: none;
          }
        }
      }
    }
  }
}

@mixin -create-rule-row-position(
  $position, 
  $size, 
  $attribute, 
  $select-item, 
  $select-rule-row, 
  $between: false
) {
  $key: if($between, 'between', $position);

  &#{'[#{ $attribute }*="rules-row: #{ $key }"]'} {
    > #{ $select-item } {
      &#{ $select-rule-row } {
        content: "";
        #{ $position }: math.floor(math.div($size, 2)) * -1;
      }
    }
  }
}