import { Component, Input, Output, EventEmitter, forwardRef, ViewChild, ElementRef, Renderer2 } from '@angular/core';
import { formatCurrency } from '@angular/common';
import { ControlValueAccessor, NG_VALUE_ACCESSOR, Validator, AbstractControl, ValidationErrors, NG_VALIDATORS } from '@angular/forms';
import { NbTooltipDirective, NbTrigger } from '@nebular/theme';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { StringUtils } from '../../utilities/string-utils';

@Component({
  selector: 'rbj-currency-input',
  templateUrl: './currency-input.component.html',
  styleUrls: ['./currency-input.component.scss'],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => CurrencyInputComponent),
      multi: true
    },
    {
      provide: NG_VALIDATORS,
      useExisting: forwardRef(() => CurrencyInputComponent),
      multi: true,
    },
  ]
})
export class CurrencyInputComponent implements ControlValueAccessor, Validator {

  @ViewChild('currencyInput', { static: true }) input: ElementRef;
  @ViewChild(NbTooltipDirective) tooltip: NbTooltipDirective;
  private destroy$: Subject<void> = new Subject<void>();
  private _ngModel: number;
  private _reverseStatus: boolean = false;
  private _disableStatus: boolean = false;
  private _initialized: boolean;
  private _lastBlurValue: number;
  realTimeUpdate: boolean
  status = '';
  displayValue: string;
  readOnly: boolean = false;
  required: boolean = false;
  leftToRight: boolean = false;
  @Input() id?= "";
  @Input() name = "";
  onChange = (value: number) => { };
  onTouched = () => { };
  @Input() min = -999999999.99;
  @Input() max = 999999999.99;
  @Input() currencyStep = '0.01';
  @Input() class = '';


  /**
   * A calculated best value should be for this field, if got mismatch will have hover message
   */
  @Input() valueShouldBe: number = null;
  /**
   * The hover message format for mismatch, Default:`The value should be {1}`, `{1}` will be replace with formatted value
   */
  @Input() valueShouldBeWarningText: string = "The value should be {1}";
  /**
   * The hover message color default:warning | empty, primary, info, success, warning, danger
   */
  @Input() valueShouldBeWarningStatus = 'warning';

  @Input() zeroStatus = '';
  @Input() zeroExpression = '';
  @Input() nullExpression = '';

  /**
   * Positive value status color default:empty | empty, primary, info, success, warning, danger
   */
  @Input() positiveStatus = '';

  /**
   * Negative value status color default:danger | primary, info, success, warning, danger
   */
  @Input() negativeStatus = 'danger';
  @Input() currencyCode = 'USD';

  @Input() nullValue = 0;
  @Input() disabled = false;
  @Input() precision = 2;
  @Input() fieldSize = 'medium';
  @Input()
  public set reverseStatus(value) {
    this._reverseStatus = typeof value !== 'undefined' && value !== false;
    this.setStatusColor();
  }
  @Input()
  public set disableStatus(value) {
    this._disableStatus = typeof value !== 'undefined' && value !== false;
    this.setStatusColor();
  }

  @Input("readonly")
  public set input_readOnly(value) {
    this.readOnly = typeof value !== "undefined" && value !== false;
  }

  @Input("required")
  public set input_required(value) {
    this.required = typeof value !== "undefined" && value !== false;
  }

  @Input("LTR")
  public set input_leftToRight(value) {
    this.leftToRight = typeof value !== "undefined" && value !== false;
  }
  @Input("realTimeUpdate")
  public set input_realTimeUpdate(value) {
    this.realTimeUpdate = typeof value !== "undefined" && value !== false;
  }
  @Input("tooltips") tooltipContent: string;
  @Output() change = new EventEmitter<number>();
  @Output() focus = new EventEmitter();
  @Output() blur = new EventEmitter<number>();

  constructor(
    private elementRef: ElementRef,
    private renderer: Renderer2) {

  }
  validate(control: AbstractControl): ValidationErrors {
    if (this.required && (this.value == null || this.value == 0)) {
      return { "currency": "" };
    }

    return null;
  }
  registerOnValidatorChange?(fn: () => void): void {
  }
  //#region Implements
  writeValue(value: string): void {
    if (value) {
      this.value = Number(value);
    } else {
      this.value = this.nullValue;
    }
    this._lastBlurValue = this.value;
    //this.onChange(this.value);
    this.setDisplayValue(this.value);
    this.setStatusColor();
  }
  registerOnChange(fn: (value: number) => void): void {
    this.onChange = fn;
  }
  registerOnTouched(fn: any): void {
    this.onTouched = fn;
  }
  setDisabledState?(isDisabled: boolean): void {
    this.disabled = isDisabled;
  }

  ngAfterViewInit() {
    this.tooltip.nbTooltipShowStateChange
      .pipe(takeUntil(this.destroy$))
      .subscribe(isShown => {
        if (!this.tooltipContent && isShown) {
          this.tooltip.hide();
        }
      })
    this.renderer.removeAttribute(this.elementRef.nativeElement, 'id')
  }

  ngOnInit() {
    // if (this.readOnly) {
    //   this.positiveStatus = '';
    //   this.negativeStatus = '';
    // }
  }
  ngOnDestroy() {
    this.destroy$.next();
    this.destroy$.complete();
  }

  //#endregion


  public get value(): number {
    return this._ngModel;
  }
  public set value(v: number) {
    if (v) v = Number(v.toFixed(this.precision));
    if (v < this.min) {
      v = this.min;
    } else if (v > this.max) {
      v = this.max;
    }

    if (this._initialized) {
      if (v != this._ngModel) {

        this._ngModel = v;
        this.onChange(this.value);


      }
    } else {
      this._initialized = true;
      this._ngModel = v;
    }
  }
  onBlur() {
    this.setDisplayValue(this.value);
    if (this.value != this._lastBlurValue) {

      this.change.emit(this.value);
      this.onChange(this.value);
      this._lastBlurValue = this.value;
      this.onTouched();
      this.blur.emit(this.value);
      this.setStatusColor();
    }
  }
  setDisplayValue(v: number) {
    if (null === v) {
      this.displayValue = this.nullExpression;
    } else if (['', 0, undefined, NaN].includes(v)) {

      this.displayValue = this.zeroExpression;
    } else {
      this.displayValue = formatCurrency(v, "en", "", this.currencyCode, `0.${this.precision}`);
    }
    //to avoid input 0 twice will not update empty to ui
    this.input.nativeElement.value = this.displayValue;
  }

  currencyInputChanged(value: string) {
    if (this.displayValue != value) {

      if (this.realTimeUpdate) {
        this.value = Number(value.replace('..', '.').replace(/[$,]/g, ''));

        if (false == [0, null, undefined, NaN].includes(this.value)) {
          let focus = this.input.nativeElement.selectionStart;
          let lengthBeforeCursor = this.input.nativeElement.value.substring(0, focus).replace(',', '').length;
          this.setDisplayValue(this.value);

          let formattedValue = this.input.nativeElement.value;
          let commaCount = 0;
          for (let i = 0; i < lengthBeforeCursor; i++) {
            const c = formattedValue[i];
            if (c == ',') commaCount++;
          }

          this.setCursorPosition(lengthBeforeCursor + commaCount);
        } else {

          //this.setDisplayValue(this.value);
        }
      } else {
        this.value = StringUtils.isNullOrWhitespace(value) ? this.nullValue : Number(value.replace(/[$,]/g, ''));
      }




    }
  }


  setCursorPosition(position) {
    let input = this.input.nativeElement;
    if (input.setSelectionRange) {
      input.focus();
      input.setSelectionRange(position, position);
    } else if (input.createTextRange) {
      var range = input.createTextRange();
      range.collapse(true);
      range.moveEnd('character', position);
      range.moveStart('character', position);
      range.select();
    }
  }
  setStatusColor() {
    if (this._disableStatus) {
      this.status = "";
      return;
    }

    if (this._ngModel > 0) this.status = this._reverseStatus ? this.negativeStatus : this.positiveStatus;
    else if (['', 0, null, undefined, NaN].includes(this._ngModel)) {
      this.status = this.zeroStatus;
    }
    else {
      this.status = this._reverseStatus ? this.positiveStatus : this.negativeStatus;
    }

    if (this.valueShouldBe != null) {
      let shouldBe = formatCurrency(this.valueShouldBe, "en", "", this.currencyCode);
      if (this.displayValue != (this.valueShouldBe == 0 ? this.zeroExpression : shouldBe)) {

        this.status = this.valueShouldBeWarningStatus;
        this.tooltipContent = this.valueShouldBeWarningText.replace('{1}', shouldBe);
      } else {

        this.tooltipContent = '';
      }
    }
  }
  get alignmentClass(): string {
    return this.leftToRight ? "text-left" : "text-right";
  }

  public onKeyDown(e: KeyboardEvent): void {


    if ([46, 8, 9, 27, 13, 110, 190].indexOf(e.keyCode) !== -1 ||
      // Allow: Ctrl+A
      (e.keyCode === 65 && (e.ctrlKey || e.metaKey)) ||
      // Allow: Ctrl+C
      (e.keyCode === 67 && (e.ctrlKey || e.metaKey)) ||
      // Allow: Ctrl+V
      (e.keyCode === 86 && (e.ctrlKey || e.metaKey)) ||
      // Allow: Ctrl+X
      (e.keyCode === 88 && (e.ctrlKey || e.metaKey)) ||
      // Allow: page up, page down, home, end, left, right
      (e.keyCode >= 33 && e.keyCode <= 39)) {
      // let it happen, don't do anything
      return;
    }

    let regMatch = e.key.match(/[\d.,-]/g);
    if (regMatch == null) {
      //e.stopPropagation()
      e.preventDefault();
    }
  }


}
