import { Component, Input, forwardRef, OnInit, Optional, Host, SkipSelf, Injector, EventEmitter, Output, ElementRef, ViewChild, ChangeDetectorRef } from '@angular/core';
import { ControlValueAccessor, DefaultValueAccessor, ControlContainer, FormControl, AbstractControl, NG_VALUE_ACCESSOR, NgModel, NgControl } from '@angular/forms';

@Component({
  selector: 'input-number',
  exportAs: 'inputNumber',
  templateUrl: './input-number.component.pug',
  styleUrls: ['./input-number.component.scss'],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => InputNumberComponent),
      multi: true
    }
  ]
})
export class InputNumberComponent implements ControlValueAccessor, OnInit {
  @Input() formControlName: string;
  @Input() min: number = Number.MIN_VALUE;
  @Input() max: number = Number.MAX_VALUE;
  @Input() step: number = 1;
  @Input() defaultValue: number | string = 0;
  @Input() minIntegerDigits: number = 1;
  @Input() minFractionDigits: number = 2;
  @Input() maxFractionDigits: number = 16;
  @Input() disabled: boolean = false;
  @Input() reverse: boolean = false; 
  @Output() input: EventEmitter<any> = new EventEmitter<any>(true);

  @ViewChild('model', {read: ElementRef}) private model: ElementRef;
  @ViewChild('up', {read: ElementRef}) private up: ElementRef;
  @ViewChild('down', {read: ElementRef}) private down: ElementRef;
  
  private base: any = null;
  private priceValueInt: any = null;

  private control: FormControl | AbstractControl;
  private onChange: any = Function.prototype;
  private onTouched: any = Function.prototype;
  
  constructor(private changeDetector: ChangeDetectorRef,
              @Optional() private context: Injector,
              @Optional() @Host() @SkipSelf() private controlContainer: ControlContainer) {
  }

  set baseValue(value: string) {
    let new_base_val: number = +parseFloat(value);
    if (Number.isNaN(new_base_val)) {
      new_base_val = null;
    }
    if (this.base === new_base_val) {
      return;
    }
    const curr_val: number = +parseFloat(this.value);
    let new_number: number = 0;
    // changed parent value
    if (this.base && new_base_val) {
      // i.e. curr_val = 5%
      // in %
      new_number = curr_val;
      this.base = +new_base_val;
    }
    // switch from percent to number
    if (this.base && !new_base_val) {
      // in price
      new_number = this.base + (curr_val * (this.base / 100.0)) * (this.reverse ? -1 : 1);
      this.base = null;
    }
    // switch from number to percent
    if (!this.base && new_base_val) {
      // in %
      new_number = Math.abs(curr_val - new_base_val) / (new_base_val / 100.0);
      this.base = +new_base_val;
    }    
    this.value = +new_number;
  }

  get value(): any {
    return this.defaultValue;
  }

  set value(value: any) {
    value = +parseFloat(value);
    if (!Number.isNaN(value)) {
      value = value.toFixed(this.maxFractionDigits);
      value = Math.min(Math.max(value, this.min), this.max);
      value = value.toFixed(this.minFractionDigits);
    } else {
      value = Math.min(Math.max(0, this.min), this.max);
      value = value.toFixed(this.minFractionDigits);
    }
    // let val: string = value;
    this.priceValueInt = value;
    let val_number: number = parseFloat(value);
    if (this.base) {
      this.priceValueInt = (this.base + val_number * (this.reverse ? -1 : 1) * (this.base / 100)).toFixed(this.minFractionDigits);
      value = val_number.toFixed(2);
    }
    this.defaultValue = value;
    this.onChange(this.priceValueInt);
    this.input.emit(value);
  }

  get priceValue(): any {
    return this.priceValueInt;
  }
  ngOnInit(): void {
    if (this.controlContainer) {
      if (this.formControlName) {
        this.control = this.controlContainer.control.get(this.formControlName) as FormControl;
      } else {
        console.warn('Missing FormControlName directive from host element of the component');
      }
    } else {
      console.warn('Can\'t find parent FormGroup directive');
    }

    if (this.control) {
      this.defaultValue = +this.control.value;
    }

    if (this.base && this.defaultValue) {
      // convert number -> percent
      const val: number = this.defaultValue as number;
      this.defaultValue = (Math.abs(val - this.base) / (this.base / 100)).toFixed(this.minFractionDigits);
    }
    this.value = +this.defaultValue;
  }

  increase(): void {
    if (this.disabled) {
      return;
    }
    this.value = +this.value + this.step;
  }

  decrease(): void {
    if (this.disabled) {
      return;
    }
    this.value = +this.value - this.step;
  }

  onKeydown(event: KeyboardEvent): boolean | void {
    if (event.keyCode === 38) {
      this.increase();
    } else if (event.keyCode === 40) {
      this.decrease();
    }
  }

  writeValue(value: any): void {
    this.changeDetector.detectChanges();
    if (this.base && value) {
      // convert number -> percent
      const val: number = +parseFloat(value); // this.defaultValue as number;
      value = (Math.abs(val - this.base) / (this.base / 100)).toFixed(this.minFractionDigits);
    }
    this.value = +value;
  }

  registerOnChange(fn: (value: any) => void): void {
    this.onChange = fn;
  }

  registerOnTouched(fn: () => {}): void {
    this.onTouched = fn;
  }

  setDisabledState(isDisabled: boolean): void {
    this.disabled = isDisabled;
  }
}
