/**
 * @license
 * Copyright 2023 Tyler Technologies, Inc. 
 * License: Apache-2.0
 */
//
// Copyright 2016 Google Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

@use 'sass:map';
@use 'sass:math';
@use '@material/density/functions' as mdc-density-functions;
@use '@material/density/variables' as mdc-density-variables;
@use '@material/feature-targeting/feature-targeting' as mdc-feature-targeting;
@use '@material/ripple/ripple' as mdc-ripple;
@use '@material/ripple/ripple-theme' as mdc-ripple-theme;
@use '@material/theme/color-custom-properties' as mdc-color-custom-properties;
@use '@material/theme/custom-properties' as mdc-custom-properties;
@use '@material/theme/theme' as mdc-theme;
@use '@material/theme/theme-color' as mdc-theme-color;
@use '@material/touch-target/variables' as touch-target-variables;
@use './checkbox-custom-properties' as checkbox-custom-properties;
@use '../theme/custom-properties' as custom-properties;
@use '../theme/theme-values' as theme-values;
@use '../theme' as theme;

$baseline-theme-color: tertiary !default; // FORGE(modify): use forge-specific theme style
$mark-color: text-primary-on-dark !default; // FORGE(modify): use MDC theme style instead of static color
$border-color: on-surface !default; // FORGE(modify): use MDC theme style instead of static color
$disabled-color: on-surface !default; // FORGE(modify): use MDC theme style instead of static color

$ripple-size: 40px !default;
$icon-size: 18px !default;
$mark-stroke-size: math.div(2, 15) * $icon-size !default;
$border-width: 2px !default;
$transition-duration: 90ms !default;
$item-spacing: 4px !default;
$focus-indicator-opacity: map.get(mdc-ripple-theme.$dark-ink-opacities, focus) !default;

$minimum-size: 28px !default;
$maximum-size: $ripple-size !default;
$density-scale: mdc-density-variables.$default-scale !default;
$density-config: (
  size: (
    minimum: $minimum-size,
    default: $ripple-size,
    maximum: $maximum-size,
  ),
) !default;

$ripple-target: '.mdc-checkbox__ripple' !default;

/// Material baseline light theme configuration of checkbox.
/// See https://material.io/components/selection-controls for spec.
$base-theme: (
  density-scale: 0,
  checkmark-color: $mark-color,
  container-checked-color: $baseline-theme-color,
  container-checked-hover-color: null,
  container-disabled-color: $disabled-color,
  outline-color: $border-color,
  outline-hover-color: null,
  ripple-color: on-surface, // FORGE(modify): use MDC theme style instead of static color
  ripple-opacity: mdc-ripple-theme.$dark-ink-opacities,
  ripple-checked-color: $baseline-theme-color,
  ripple-checked-opacity: mdc-ripple-theme.$dark-ink-opacities,
) !default;

/// Sets theme to checkbox based on provided theme configuration.
/// Only emits theme related styles.
/// @param {Map} $theme - Theme configuration to use for theming checkbox.
@mixin theme($theme, $query: mdc-feature-targeting.all()) {
  @include theme.validate-theme($base-theme, $theme);

  $ripple-color: map.get($theme, ripple-color);
  $ripple-opacity: map.get($theme, ripple-opacity);
  @if $ripple-opacity == null {
    $ripple-opacity: ();
  }
  @if $ripple-color {
    @include ripple-color($color: $ripple-color, $opacity-map: $ripple-opacity, $query: $query);
  }

  $ripple-checked-color: map.get($theme, ripple-checked-color);
  $ripple-checked-opacity: map.get($theme, ripple-checked-opacity);
  @if $ripple-checked-opacity == null {
    $ripple-checked-opacity: ();
  }
  @if $ripple-checked-color {
    @include focus-indicator-color($color: $ripple-checked-color, $query: $query);
  }

  $density-scale: map.get($theme, density-scale);
  @if $density-scale != null {
    @include density($density-scale: $density-scale, $query: $query);
  }

  $outline-color: map.get($theme, outline-color);
  $container-checked-color: map.get($theme, container-checked-color);
  @if (($outline-color and not $container-checked-color) or (not $outline-color and $container-checked-color)) {
    @error 'Both `outline-color` and `container-checked-color` keys should be provided.';
  }
  @if ($outline-color and $container-checked-color) {
    @include container-colors(
      $unmarked-stroke-color: $outline-color,
      $marked-stroke-color: $container-checked-color,
      $marked-fill-color: $container-checked-color,
      $query: $query
    );
  }

  $outline-hover-color: map.get($theme, outline-hover-color);
  $container-checked-hover-color: map.get($theme, container-checked-hover-color);
  @if (
    ($outline-hover-color and not $container-checked-hover-color) or
      (not $outline-hover-color and $container-checked-hover-color)
  ) {
    @error 'Both `outline-hover-color` and `container-checked-hover-color` keys should be provided.';
  }
  @if ($outline-hover-color and $container-checked-hover-color) {
    @include mdc-ripple-theme.states-selector() {
      @include container-colors(
        $unmarked-stroke-color: $outline-hover-color,
        $marked-stroke-color: $container-checked-hover-color,
        $marked-fill-color: $container-checked-hover-color,
        $query: $query
      );
    }
  }

  $container-disabled-color: map.get($theme, container-disabled-color);
  @if $container-disabled-color {
    @include disabled-container-colors(
      $unmarked-stroke-color: $container-disabled-color,
      $marked-fill-color: $container-disabled-color,
      $query: $query
    );
  }

  $checkmark-color: map.get($theme, checkmark-color);
  @if $checkmark-color {
    @include ink-color($checkmark-color, $query: $query);
    @include disabled-ink-color($checkmark-color, $query: $query);
  }
}

///
/// @param {Number | String} $density-scale - Density scale value for component.
///     Supported density scale values `-3`, `-2`, `-1`, `0`.
/// @return Returns ripple size of checkbox for given density scale.
///
@function get-ripple-size($density-scale) {
  @return mdc-density-functions.prop-value(
    $density-config: $density-config,
    $density-scale: $density-scale,
    $property-name: size
  );
}

///
/// Sets density scale for checkbox.
///
/// @param {Number | String} $density-scale - Density scale value for component. Supported density scale values
///     `-3`, `-2`, `-1`, `0`.
///
@mixin density($density-scale, $query: mdc-feature-targeting.all()) {
  $size: get-ripple-size($density-scale);

  @include ripple-size($size, $query: $query);
}

/// Sets ripple size of checkbox and optionally set touch target size which can
/// be more than the size of ripple.
/// @param {Number} $ripple-size - Visual ripple size of checkbox.
/// @param {Number} $touch-target-size [null] - Touch target size that is
///   visually hidden. Defaults to the size of provided `$ripple-size`.
@mixin ripple-size($ripple-size, $touch-target-size: null, $query: mdc-feature-targeting.all()) {
  $feat-structure: mdc-feature-targeting.create-target($query, structure);

  $checkbox-padding: ($ripple-size - $icon-size) * 0.5;
  $ripple-size: mdc-custom-properties.create(checkbox-custom-properties.$ripple-size, $ripple-size);
  $checkbox-padding: 'calc((_ripple-size - _icon-size) / 2)';
  $replace: (
    _ripple-size: $ripple-size,
    _icon-size: $icon-size,
  );

  @include mdc-feature-targeting.targets($feat-structure) {
    @include mdc-theme.property(padding, $checkbox-padding, $replace: $replace);
  }

  .mdc-checkbox__background {
    @include mdc-feature-targeting.targets($feat-structure) {
      @include mdc-theme.property(top, $checkbox-padding, $replace: $replace);
      @include mdc-theme.property(left, $checkbox-padding, $replace: $replace);
    }
  }

  @if ($touch-target-size) {
    $touch-target-size: mdc-custom-properties.create(checkbox-custom-properties.$touch-target-size, $touch-target-size);
  } @else {
    $touch-target-size: $ripple-size;
  }

  @include touch-target($touch-target-size: $touch-target-size, $ripple-size: $ripple-size, $query: $query);
}

///
/// Sets stroke & fill colors for both marked and unmarked state of enabled checkbox.
/// Set $generate-keyframes to false to prevent the mixin from generating @keyframes
/// @param {Color} $unmarked-stroke-color - The desired stroke color for the unmarked state
/// @param {Color} $unmarked-fill-color - The desired fill color for the unmarked state
/// @param {Color} $marked-stroke-color - The desired stroke color for the marked state
/// @param {Color} $marked-fill-color - The desired fill color for the marked state
/// @param {Boolean} $generate-keyframes [true] - Whether animation keyframes should be generated
///
@mixin container-colors(
  $unmarked-stroke-color: $border-color,
  $unmarked-fill-color: transparent,
  $marked-stroke-color: $baseline-theme-color,
  $marked-fill-color: $baseline-theme-color,
  $generate-keyframes: true,
  $query: mdc-feature-targeting.all()
) {
  $feat-animation: mdc-feature-targeting.create-target($query, animation);
  $feat-color: mdc-feature-targeting.create-target($query, color);

  // Unchecked colors
  @if (not mdc-custom-properties.is-custom-prop($unmarked-stroke-color)) {
    $unmarked-stroke-color: mdc-custom-properties.create(
      checkbox-custom-properties.$unchecked-color,
      mdc-theme-color.prop-value($unmarked-stroke-color)
    );
  }

  @include if-unmarked-enabled_ {
    @include container-colors_($unmarked-stroke-color, $unmarked-fill-color, $query: $query);
  }

  // Checked colors
  @if (not mdc-custom-properties.is-custom-prop($marked-stroke-color)) {
    // FORGE(modify): use MDC theme tertiary instead of secondary
    $marked-stroke-color: mdc-custom-properties.create(
      checkbox-custom-properties.$checked-color,
      custom-properties.create(--forge-theme-tertiary, theme-values.$tertiary)
    );
  }
  @if (not mdc-custom-properties.is-custom-prop($marked-fill-color)) {
    // FORGE(modify): use MDC theme tertiary instead of secondary
    $marked-fill-color: mdc-custom-properties.create(
      checkbox-custom-properties.$checked-color,
      custom-properties.create(--forge-theme-tertiary, theme-values.$tertiary)
    );
  }

  @include if-marked-enabled_ {
    @include container-colors_($marked-stroke-color, $marked-fill-color, $query: $query);
  }

  @if $generate-keyframes {
    $uid: mdc-theme-color.color-hash($unmarked-stroke-color) +
      mdc-theme-color.color-hash($marked-stroke-color) +
      mdc-theme-color.color-hash($unmarked-fill-color) +
      mdc-theme-color.color-hash($marked-fill-color);

    $anim-selector: if(&, '&.mdc-checkbox--anim', '.mdc-checkbox--anim');

    @include mdc-feature-targeting.targets($feat-animation, $feat-color) {
      @include container-keyframes_(
        $from-stroke-color: $unmarked-stroke-color,
        $to-stroke-color: $marked-stroke-color,
        $from-fill-color: $unmarked-fill-color,
        $to-fill-color: $marked-fill-color,
        $uid: #{$uid}
      );
    }

    // stylelint-disable max-nesting-depth

    #{$anim-selector} {
      &-unchecked-checked,
      &-unchecked-indeterminate {
        // FORGE (modify): using class-based state selector due to Shadow DOM
        &.forge-checkbox--enabled .mdc-checkbox__background {
          @include mdc-feature-targeting.targets($feat-animation) {
            animation-name: mdc-checkbox-fade-in-background-#{$uid};
          }
        }
      }

      &-checked-unchecked,
      &-indeterminate-unchecked {
        // FORGE (modify): using class-based state selector due to Shadow DOM
        &.forge-checkbox--enabled .mdc-checkbox__background {
          @include mdc-feature-targeting.targets($feat-animation) {
            animation-name: mdc-checkbox-fade-out-background-#{$uid};
          }
        }
      }
    }

    // stylelint-enable max-nesting-depth
  }
}

///
/// Sets stroke & fill colors for both marked and unmarked state of disabled checkbox.
/// @param {Color} $unmarked-stroke-color - The desired stroke color for the unmarked state
/// @param {Color} $unmarked-fill-color - The desired fill color for the unmarked state
/// @param {Color} $marked-stroke-color - The desired stroke color for the marked state
/// @param {Color} $marked-fill-color - The desired fill color for the marked state
///
@mixin disabled-container-colors(
  $unmarked-stroke-color: $disabled-color,
  $unmarked-fill-color: transparent,
  $marked-stroke-color: transparent,
  $marked-fill-color: $disabled-color,
  $query: mdc-feature-targeting.all()
) {
  @if (not mdc-custom-properties.is-custom-prop($unmarked-stroke-color)) {
    $unmarked-stroke-color: mdc-custom-properties.create(
      checkbox-custom-properties.$disabled-color,
      mdc-theme-color.prop-value($unmarked-stroke-color)
    );
  }

  @include if-unmarked-disabled_ {
    @include container-colors_($unmarked-stroke-color, $unmarked-fill-color, $query: $query);
  }

  @if (not mdc-custom-properties.is-custom-prop($marked-fill-color)) {
    $marked-fill-color: mdc-custom-properties.create(
      checkbox-custom-properties.$disabled-color,
      mdc-theme-color.prop-value($marked-fill-color)
    );
  }

  @include if-marked-disabled_ {
    @include container-colors_($marked-stroke-color, $marked-fill-color, $query: $query);
  }
}

///
/// Sets the ink color of the checked and indeterminate icons for an enabled checkbox
/// @param {Color} $color - The desired ink color in enabled state
///
@mixin ink-color($color, $query: mdc-feature-targeting.all()) {
  @include if-enabled_ {
    @include ink-color_($color, $query: $query);
  }
}

///
/// Sets the ink color of the checked and indeterminate icons for a disabled checkbox
/// @param {Color} $color - The desired ink color in disabled state
///
@mixin disabled-ink-color($color, $query: mdc-feature-targeting.all()) {
  @include if-disabled_ {
    @include ink-color_($color, $query: $query);
  }
}

/// Sets ripple color when checkbox is not in checked state.
@mixin ripple-color($color, $opacity-map: null, $query: mdc-feature-targeting.all()) {
  // FORGE (modify): using forge-specific theme style custom property
  $tertiary-prop: custom-properties.create(--forge-theme-tertiary, theme-values.$tertiary);
  @include mdc-ripple-theme.states($color: $color, $query: $query, $ripple-target: $ripple-target);
}

/// Sets focus indicator color when checkbox is in checked state.
@mixin focus-indicator-color($color, $opacity-map: null, $query: mdc-feature-targeting.all()) {
  // FORGE (modify): using forge-specific theme style custom property
  $tertiary-prop: custom-properties.create(--forge-theme-tertiary, theme-values.$tertiary);

  &.mdc-checkbox--selected {
    @include mdc-ripple-theme.states($color: $tertiary-prop, $query: $query, $ripple-target: $ripple-target);
  }

  &.mdc-ripple-upgraded--background-focused.mdc-checkbox--selected {
    @include mdc-ripple-theme.states-base-color($color: $tertiary-prop, $query: $query, $ripple-target: $ripple-target);
  }
}

/// Sets the touch target size and appropriate margin to accommodate the
/// touch target.
/// @param {Number} $touch-target-size Size of touch target (Native input) in `px`.
/// @param {Number} $ripple-size Size of ripple in `px`.
@mixin touch-target($touch-target-size, $ripple-size, $query: mdc-feature-targeting.all()) {
  $feat-structure: mdc-feature-targeting.create-target($query, structure);

  $margin: 'calc((_touch-target-size - _ripple-size) / 2)';
  $replace: (
    _touch-target-size: $touch-target-size,
    _ripple-size: $ripple-size,
  );

  @include mdc-feature-targeting.targets($feat-structure) {
    @include mdc-theme.property(margin, $margin, $replace: $replace);
  }

  $offset: 'calc((_ripple-size - _touch-target-size) / 2)';

  @include mdc-feature-targeting.targets($feat-structure) {
    // FORGE (modify): using class-based state selector due to Shadow DOM
    ::slotted(input[type='checkbox']) {
      @include mdc-theme.property(top, $offset, $replace: $replace);
      @include mdc-theme.property(right, $offset, $replace: $replace);
      @include mdc-theme.property(left, $offset, $replace: $replace);
      @include mdc-theme.property(width, $touch-target-size);
      @include mdc-theme.property(height, $touch-target-size);
    }
  }
}

//
// Private
//

///
/// Helps select the checkbox background only when its native control is in
/// enabled state.
/// @access private
///
@mixin if-enabled_ {
  // FORGE (modify): using class-based state selector due to Shadow DOM
  &.forge-checkbox--enabled {
    @content;
  }
}

///
/// Helps select the checkbox background only when its native control is in
/// disabled state.
/// @access private
///
@mixin if-disabled_ {
  // FORGE (modify): using class-based state selector due to Shadow DOM
  &.mdc-checkbox--disabled {
    @content;
  }
}

///
/// Helps select the checkbox background only when its native control is in
/// unmarked & enabled state.
/// @access private
///
@mixin if-unmarked-enabled_ {
  // FORGE (modify): using class-based state selector due to Shadow DOM
  &.forge-checkbox--enabled:not(.forge-checkbox--checked):not(.forge-checkbox--indeterminate) {
    @content;
  }
}

///
/// Helps select the checkbox background only when its native control is in
/// unmarked & disabled state.
/// @access private
///
@mixin if-unmarked-disabled_ {
  // FORGE (modify): using class-based state selector due to Shadow DOM
  &.mdc-checkbox--disabled:not(.forge-checkbox--checked):not(.forge-checkbox--indeterminate) {
    @content;
  }
}

///
/// Helps select the checkbox background only when its native control is in
/// marked & enabled state.
/// @access private
///
@mixin if-marked-enabled_ {
  // FORGE (modify): using class-based state selector due to Shadow DOM
  &.forge-checkbox--enabled.forge-checkbox--checked,
  &.forge-checkbox--enabled.forge-checkbox--indeterminate {
    @content;
  }
}

///
/// Helps select the checkbox background only when its native control is in
/// marked & disabled state.
/// @access private
///
@mixin if-marked-disabled_ {
  // FORGE (modify): using class-based state selector due to Shadow DOM
  &.mdc-checkbox--disabled.forge-checkbox--checked,
  &.mdc-checkbox--disabled.forge-checkbox--indeterminate {
    @content;
  }
}

///
/// Sets the stroke & fill colors for the checkbox.
/// This mixin should be wrapped in a mixin that qualifies state such as
/// `mdc-checkbox-if-unmarked-enabled_`.
/// @access private
///
@mixin container-colors_($stroke-color, $fill-color, $query: mdc-feature-targeting.all()) {
  $feat-color: mdc-feature-targeting.create-target($query, color);

  .mdc-checkbox__background {
    @include mdc-feature-targeting.targets($feat-color) {
      @include mdc-theme.property(border-color, $stroke-color);
      @include mdc-theme.property(background-color, $fill-color);
    }
  }
}

///
/// Sets the ink color of the checked and indeterminate icons for a checkbox.
/// This mixin should be wrapped in a mixin that qualifies state such as
/// `mdc-checkbox-if-unmarked_`.
/// @access private
///
@mixin ink-color_($color, $query: mdc-feature-targeting.all()) {
  $feat-color: mdc-feature-targeting.create-target($query, color);

  .mdc-checkbox__background {
    .mdc-checkbox__checkmark {
      @include mdc-feature-targeting.targets($feat-color) {
        @include theme.property(color, $color);
      }
    }

    .mdc-checkbox__mixedmark {
      @include mdc-feature-targeting.targets($feat-color) {
        @include theme.property(border-color, $color);
      }
    }
  }
}

@mixin container-keyframes_($from-stroke-color, $to-stroke-color, $from-fill-color, $to-fill-color, $uid) {
  @keyframes mdc-checkbox-fade-in-background-#{$uid} {
    0% {
      @include mdc-theme.property(border-color, $from-stroke-color);
      @include mdc-theme.property(background-color, $from-fill-color);
    }

    50% {
      @include mdc-theme.property(border-color, $to-stroke-color);
      @include mdc-theme.property(background-color, $to-fill-color);
    }
  }

  @keyframes mdc-checkbox-fade-out-background-#{$uid} {
    0%,
    80% {
      @include mdc-theme.property(border-color, $to-stroke-color);
      @include mdc-theme.property(background-color, $to-fill-color);
    }

    100% {
      @include mdc-theme.property(border-color, $from-stroke-color);
      @include mdc-theme.property(background-color, $from-fill-color);
    }
  }
}
