import { isNonNullable } from '../../../types/type-guard';
import { StringFormatStrategy } from './string-format-strategy.abstract';
/**
 * A class for formatting number with the given mask format
 */
export class NumberMaskStrategy extends StringFormatStrategy {
  /**
   * It creates a new instance of the Mask class.
   *
   * @param {NumberMaskConfig} maskConfig - MaskConfig
   * @param {boolean} overrideValue - Whether to override the value after blur, default is false
   */
  constructor(maskConfig, overrideValue = false) {
    super();
    this.maskConfig = { allowNegative: true };
    /**
     * if allow negative, will skip '-', e.g. when input '-' -> '-1.2'
     * if precision is not null, then user can fill input with decimal places, e.g. when input '1.' -> '1.2'
     *
     * @param {string} input - input number
     * @returns {boolean} need skip if return true, otherwise false
     */
    this.skipFormat = (input) => {
      return ((this.maskConfig.allowNegative && input === '-') ||
        (this.maskConfig.precision != undefined &&
          this.maskConfig.precision > 0 &&
          input.split('.').length - 1 === 1 &&
          input.endsWith('.') &&
          !input.startsWith('.')));
    };
    this.maskConfig = Object.assign(Object.assign({}, this.maskConfig), maskConfig);
    this.overrideValue = overrideValue;
    this.mask = new RegExp(this.getRegExp());
  }
  /**
   * Given a number string, it replaces the mask with the matched input value
   *
   * @param {string} input - input number
   * @returns {string} The formatted number string.
   */
  format(input) {
    const redressedInput = this.redressAbnormalNumber(input);
    if (this.skipFormat(redressedInput)) {
      return redressedInput;
    }
    const resultStringBuilder = [...redressedInput];
    for (let i = redressedInput.length - 1; i >= 0; i--) {
      const value = resultStringBuilder.join('');
      if (this.inputNeedFormat(value)) {
        resultStringBuilder.pop();
      }
    }
    return resultStringBuilder.join('');
  }
  /**
   * Get the position of caret
   *
   * @param {string} input - input number
   * @param {number} caretPosition - The current caret position
   * @returns {Nullable<number>} The new position of caret
   */
  calculateCaretPosition(input, caretPosition) {
    if (isNonNullable(caretPosition)) {
      const redressedInput = this.redressAbnormalNumber(input);
      return caretPosition - (input.length - redressedInput.length);
    }
  }
  /**
   * Whether the input string need delete the latest char
   *
   * @param {string} value - the input value
   * @returns {boolean} return true if the value need mask format. otherwise false
   */
  inputNeedFormat(value) {
    return !this.mask.test(value);
  }
  /**
   * Remove zero of beginning, example:
   *    00012 -> 12
   *    000.12 -> 0.12
   *    00123,456 -> 123,456
   *    .12 -> 0.12
   *
   * @param {string} input - the input value
   * @returns {string} - redressed Number
   */
  redressAbnormalNumber(input) {
    let removedZeroBeginning = input.replace(/^0+(?!$)/, '');
    if (removedZeroBeginning.startsWith('.')) {
      removedZeroBeginning = `0${removedZeroBeginning}`;
    }
    return removedZeroBeginning;
  }
  /**
   * Get the number RegExp
   *
   * @returns {string} - The number regExp
   */
  getRegExp() {
    const negativeMask = this.maskConfig.allowNegative ? `(\\-)?` : '';
    let precisionMask;
    if (this.maskConfig.precision === 0) {
      precisionMask = `^${negativeMask}(0|[1-9][0-9]*)$`;
    }
    else if (this.maskConfig.precision && this.maskConfig.precision > 0) {
      precisionMask = `^${negativeMask}(0|0\\.\\d{1,${this.maskConfig.precision}}|[1-9][0-9]*(\\.\\d{1,${this.maskConfig.precision}})?)$`;
    }
    else {
      precisionMask = `^${negativeMask}(0|0\\.\\d*|[1-9][0-9]*(\\.\\d*)?)$`;
    }
    return precisionMask;
  }
}
