// Copyright (c) 2015-present, salesforce.com, inc. All rights reserved
// Licensed under BSD 3-Clause - see LICENSE.txt or git.io/sfdc-license

/// Application version
/// @type String
$app-version: "1.0.0" !default;

/// Deprecation mode
///
/// ### Available modes:
/// - `disabled`: output all the code, even if deprecated
/// - `silent`: disable all warnings but don't output deprecated code
/// - `verbose`: show all warnings, even for code that is *about* to be deprecated
/// - `sensible` (default): output warnings when deprecated code is detected
/// - `fail`: prevent compilation when deprecated code is found
///
/// @type String
$deprecate-mode: "sensible" !default;

// Casts a string into a number (integer only)
//
// @param {String} $value - Value to be parsed
//
// @return {Number}
// @author @HugoGiraudel - Simplified by @kaelig to only convert unsigned integers
// @link http://hugogiraudel.com/2014/01/15/sass-string-to-number/
// @access private
@function _d-to-number($value) {
  $result: 0;
  $digits: 0;
  $numbers: (
    "0": 0,
    "1": 1,
    "2": 2,
    "3": 3,
    "4": 4,
    "5": 5,
    "6": 6,
    "7": 7,
    "8": 8,
    "9": 9
  );

  @for $i from 1 through str-length($value) {
    $character: str-slice($value, $i, $i);

    @if ($digits == 0) {
      $result: $result * 10 + map-get($numbers, $character);
    } @else {
      $digits: $digits * 10;
      $result: $result + map-get($numbers, $character) / $digits;
    }
  }

  @return $result;
}

// Major revision of a version
//
// @param {String} $version - SemVer version (e.g. '1.0.0')
// @return {Number} Major revision
//
// @example scss
//  _d-version-major('1.0.0') // 1
//
// @access private
@function _d-version-major($version) {
  @return _d-to-number(str-slice($version, 0, str-index($version, ".") - 1));
}

// Minor revision of a version
//
// @param {String} $version - SemVer version (e.g. '1.50.0')
// @return {Number} Minor revision
//
// @example scss
//  _d-version-minor('1.50.0') // 50
//
// @access private
@function _d-version-minor($version) {
  $minor-patch: str-slice(
    $version,
    str-index($version, ".") + 1,
    str-length($version)
  );
  @return _d-to-number(str-slice($minor-patch, 0, str-index($minor-patch, ".") - 1));
}

// Patch revision of a version
//
// @param {String} $version - SemVer version (e.g. '1.50.25')
// @return {Number} Patch revision
//
// @example scss
//  _d-version-patch('1.50.25') // 25
//
// @access private
@function _d-version-patch($version) {
  $minor-patch: str-slice(
    $version,
    str-index($version, ".") + 1,
    str-length($version)
  );
  @return _d-to-number(str-slice($minor-patch, str-index($minor-patch, ".") + 1, str-length($minor-patch)));
}

/// Output code only until $app-version reaches $version
/// and signal its deprecation to developers
///
/// @require $app-version
/// @require $deprecate-mode
/// @param {String} $version - SemVer-like version (e.g. '2.0.0')
/// @param {String} $message - Reason about why the code will be deprecated or possible workaround (e.g. 'Use .new-thing instead')
@mixin deprecate($version, $message: null) {
  @if (type-of($version) != "string") {
    @error 'The parameter passed to deprecate() must be a String. Good: deprecate(\'0.1.0\') / Bad: deprecate(0.1.0) .';
  }

  // Plugin is disabled. Output anyway.
  @if ("disabled" == $deprecate-mode) {
    @content;
  } @else {
    @if not ("silent" == $deprecate-mode) {
      // Assume we found code that is (or is about to be) deprecated
      $deprecation-found: true;

      @if ("verbose" == $deprecate-mode) {
        @if (&) {
          $parent: &;
          @warn '#{$parent} will be deprecated in #{$version}. Current version: #{$app-version}.';
        } @else {
          @warn 'Some code will be deprecated in #{$version}. Current version: #{$app-version}.';
        }
      }

      // Define if the code is actually deprecated
      @if (function-exists("deprecate-version-greater-than")) {
        // A custom version comparison engine was found:
        // rely on it to check if $version is greater than $app-version,
        @if (deprecate-version-greater-than($version, $app-version)) {
          @content;
          $deprecation-found: false;
        }
      } @else {
        // No custom version comparison engine was found:
        // fall back to simple version comparison tests.
        @if (_d-version-major($version) > _d-version-major($app-version)) {
          @content;
          $deprecation-found: false;
        } @else {
          @if (_d-version-major($version) == _d-version-major($app-version)) {
            @if (_d-version-minor($version) > _d-version-minor($app-version)) {
              @content;
              $deprecation-found: false;
            } @else {
              @if (_d-version-minor($version) == _d-version-minor($app-version)) {
                @if (_d-version-patch($version) > _d-version-patch($app-version)) {
                  @content;
                  $deprecation-found: false;
                }
              }
            }
          }
        }
      }

      @if ($deprecation-found) {
        $message: if($message, "\AREASON:  #{$message}", "");
        @if ("fail" == $deprecate-mode) {
          @error 'Deprecated code was found. Remove it to continue.#{$message}';
        } @else {
          @warn 'Deprecated code was found, it should be removed before its release.#{$message}';
        }
      }
    }
  }
}
