import {
  Component,
  OnInit,
  Input,
  Output,
  EventEmitter,
  ViewChild,
  ElementRef,
  ChangeDetectorRef,
  OnChanges,
  SimpleChanges
} from '@angular/core';
import { ucapAnimations } from '../animations';
import { StringUtil } from '@ucap-webmessenger/core';

const ChangesVALUE = 'value';

@Component({
  selector: 'ucap-step-input',
  templateUrl: './step-input.component.html',
  styleUrls: ['./step-input.component.scss'],
  animations: ucapAnimations
})
export class StepInputComponent implements OnInit, OnChanges {
  @Input()
  value = 0;

  @Input()
  readonly maxLength = 2;

  @Input()
  readonly min: number;

  @Input()
  readonly max: number;

  @Input()
  placeholder = '';

  @Input()
  readonly step = 1;

  @Input()
  disabled: boolean;

  @Input()
  paddingZero: boolean;

  @Output()
  changed = new EventEmitter<number>();

  @ViewChild('input', { static: true })
  input: ElementRef<HTMLInputElement>;

  isFocused: boolean;

  private get displayValue() {
    return this._displayValue;
  }
  private set displayValue(v: string) {
    this._displayValue = v;
    this.input.nativeElement.value = v;
  }
  // tslint:disable-next-line: variable-name
  private _displayValue: string;

  constructor(private changeDetectorRef: ChangeDetectorRef) {}

  ngOnInit() {
    this.changeValue(this.value, true);
  }

  ngOnChanges(changes: SimpleChanges): void {
    const valueChanges = changes[ChangesVALUE];

    if (!!valueChanges && !valueChanges.firstChange) {
      this.changeValue(valueChanges.currentValue, true);
    }
  }

  increase() {
    if (!this.disabled) {
      this.changeValue(this.value + this.step);
    }
  }

  decrease() {
    if (!this.disabled) {
      this.changeValue(this.value - this.step);
    }
  }

  onKeydown(event: KeyboardEvent) {
    if (!this.permittedKey(event)) {
      event.preventDefault();
      return;
    }

    if (/^[0-9]$/i.test(event.key)) {
      this.changeValue(Number(`${String(this.displayValue)}${event.key}`));
      return;
    }

    switch (event.key) {
      case 'ArrowUp':
        this.increase();
        return;
      case 'ArrowDown':
        this.decrease();
        return;
    }
  }

  onFocus() {
    this.isFocused = true;
  }

  onBlur() {
    this.isFocused = false;

    const dv = Number(this.input.nativeElement.value);

    if (isNaN(dv)) {
      this.changeValue(this.min);
    } else {
      this.changeValue(dv);
    }
  }

  private changeValue(v: number, force: boolean = false) {
    if (!isNaN(v)) {
      if (this.max < v) {
        v = this.min;
      } else if (this.min > v) {
        v = this.max;
      }

      v = this.step * Math.round(v / this.step);

      let dv: string;
      if (this.paddingZero) {
        dv = StringUtil.zeroFill(v, this.maxLength);
      } else {
        dv = String(v);
      }

      if (force || v !== this.value) {
        this.value = v;
        this.changed.emit(v);
      }

      if (this.input.nativeElement.value !== dv) {
        this.displayValue = dv;
      }
    }
  }

  private permittedKey(event: KeyboardEvent): boolean {
    if (
      ['.', 'Backspace', 'Tab', 'Escape', 'Esc'].some(n => n === event.key) ||
      // Allow: Ctrl/cmd+A
      ('A' === event.key &&
        (true === event.ctrlKey || true === event.metaKey)) ||
      // Allow: Ctrl/cmd+C
      ('C' === event.key &&
        (true === event.ctrlKey || true === event.metaKey)) ||
      // Allow: Ctrl/cmd+X
      ('X' === event.key &&
        (true === event.ctrlKey || true === event.metaKey)) ||
      // Allow: home, end, left, right, up, down
      [
        'Home',
        'End',
        'ArrowDown',
        'Down',
        'ArrowLeft',
        'Left',
        'ArrowRight',
        'Right',
        'ArrowUp',
        'Up'
      ].some(n => n === event.key)
    ) {
      return true;
    }

    return /^[0-9]$/i.test(event.key);
  }
}
