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

enum STATE {
  OFF = 'off',
  INTERMEDIATE = 'intermediate',
  ON = 'on'
}

@Component({
  selector: 'tristate-switcher',
  exportAs: 'tristateSwitcher',
  templateUrl: './switch-button-3-stages.component.pug',
  styleUrls: ['./switch-button-3-stages.component.scss'],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => TristateSwitcherComponent),
      multi: true
    }
  ]
})
export class TristateSwitcherComponent implements ControlValueAccessor, OnInit {
  @Input() state: STATE = STATE.INTERMEDIATE;
  @Input() label: {off: string | number, on: string | number} | undefined = undefined;
  @Input() disabled: boolean = false;
  @Output() stateChange: EventEmitter<any> = new EventEmitter<any>(true);

  get isOn(): boolean {
    return this.state === STATE.ON;
  }

  get isOff(): boolean {
    return this.state === STATE.OFF;
  }

  get isIntermediate(): boolean {
    return this.state === STATE.INTERMEDIATE;
  }

  @ViewChild('handle', {read: ElementRef})
  private handle: ElementRef;
  private secondState: STATE = null;
  private onChange: any = Function.prototype;
  private onTouched: any = Function.prototype;

  constructor(@Optional() private context: Injector,
              @Optional() @Host() @SkipSelf() private controlContainer: ControlContainer,
              @Optional() @Self() private element: ElementRef) {
  }

  ngOnInit(): void {
    this.state = this.verifyState(this.state);
    if (this.state === STATE.OFF || this.state === STATE.ON) {
      this.secondState = this.state;
    }
  }

  writeValue(value: number): void {
    this.state = this.valueToState(value);
  }

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

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

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

  stateToValue(state: STATE): number {
    state = this.verifyState(state);
    switch (state) {
      case STATE.OFF:
        return 0;
      case STATE.ON:
        return 1;
      case STATE.INTERMEDIATE:
      default:
        return -1;
    }
  }

  valueToState(value: number | boolean | null): STATE {
    switch (value) {
      case 0:
      case false:
        return STATE.OFF;
      case 1:
      case true:
        return STATE.ON;
      case -1:
      case null:
      default:
        return STATE.INTERMEDIATE;
    }
  }

  verifyState(state: STATE): STATE {
    switch (state) {
      case STATE.OFF:
      case STATE.ON:
        return state;
      case STATE.INTERMEDIATE:
      default:
        return STATE.INTERMEDIATE;
    }
  }

  switch(): void {
    switch (this.state) {
      case STATE.OFF:
        this.secondState = this.state;
        this.state = STATE.INTERMEDIATE;
        break;
      case STATE.ON:
        this.secondState = this.state;
        this.state = STATE.INTERMEDIATE;
        break;
      case STATE.INTERMEDIATE:
      default:
        this.state = (this.secondState === STATE.ON) ? STATE.OFF : STATE.ON;
        this.secondState = null;
        break;
    }
    this.onChange(this.stateToValue(this.state));
    this.stateChange.emit(this.stateToValue(this.state));
  }

  switchOff(): void {
    this.secondState = this.state;
    this.state = STATE.OFF;
    this.onChange(this.stateToValue(this.state));
    this.stateChange.emit(this.stateToValue(this.state));
  }

  switchOn(): void {
    this.secondState = this.state;
    this.state = STATE.ON;
    this.onChange(this.stateToValue(this.state));
    this.stateChange.emit(this.stateToValue(this.state));
  }

  switchIntermediate(): void {
    this.secondState = this.state;
    this.state = STATE.INTERMEDIATE;
    this.onChange(this.stateToValue(this.state));
    this.stateChange.emit(this.stateToValue(this.state));
  }

  @HostListener('click', ['$event']) onInput(event: MouseEvent): void {
    let target: HTMLElement = event.target as HTMLElement;
    let handle: HTMLElement = this.handle.nativeElement;
    let host: HTMLElement = this.element.nativeElement;
    let rect: ClientRect = host.getBoundingClientRect();

    if (event.clientX >= rect.left && event.clientX <= (rect.left + rect.width)) {
      let x1: number = Math.abs(event.clientX - rect.left);
      let x2: number = Math.abs(event.clientX - (rect.left + rect.width));

      if (target === handle) {
        this.switch();
      } else if (x1 < (rect.width / 2)) {
        (x1 >= (rect.width / 3) && !this.isIntermediate) ? this.switchIntermediate() : this.switchOff();
      } else if (x2 < (rect.width / 2)) {
        (x2 >= (rect.width / 3) && !this.isIntermediate) ? this.switchIntermediate() : this.switchOn();
      } else {
        this.switch();
      }
    }
  }
}
