import {
  AfterViewInit,
  Component,
  ContentChild,
  Directive,
  forwardRef,
  HostListener,
  Input,
  OnInit,
  TemplateRef
} from '@angular/core';
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
import cloneDeep from 'lodash/cloneDeep';
import {ArrayInputHelperService} from './array-input-helper.service';


@Directive({  // directive for template and input values
  // tslint:disable-next-line:directive-selector
  selector: '[rbArrayInputItem]'
})
export class RbArrayInputItemDirective {
  constructor(public templateRef: TemplateRef<any>) {
  }
}

@Directive({  // directive for change detection
  // tslint:disable-next-line:directive-selector
  selector: '[rbArrayInputListener]'
})
export class RbArrayInputListenerDirective {

  @Input() rbArrayInputListener: string;
  @Input() index;

  constructor(
    private helperService: ArrayInputHelperService
  ) { }

  @HostListener('ngModelChange', ['$event'])
  onChange(event) {  // emit new value
    this.helperService.newValue(this.rbArrayInputListener, this.index, event);
  }
}


@Component({
  // tslint:disable-next-line:component-selector
  selector: 'rb-array-input',
  templateUrl: './rb-array-input.component.html',
  styleUrls: ['./rb-array-input.component.scss'],
  providers: [{provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => RbArrayInputComponent), multi: true}]
})
export class RbArrayInputComponent implements ControlValueAccessor, OnInit, AfterViewInit {

  pushTemplate: any = '';  // array element template
  @Input('pushTemplate') set _pushTemplate(value) {
    this.pushTemplate = value;
    if (this.values.length) {
      this.updateArray();
    }
  }
  @Input() pushPath: string = null;

  @ContentChild(RbArrayInputItemDirective) item: RbArrayInputItemDirective;
  @ContentChild(RbArrayInputListenerDirective) item2: RbArrayInputListenerDirective;

  values = [];  // main array to display

  onChange = (ignore?: any): void => {};
  onTouched = (ignore?: any): void => {};


  constructor(
    private helperService: ArrayInputHelperService
  ) { }

  ngOnInit(): void {
  }

  ngAfterViewInit() {
    setTimeout(() => {  // needed to find reference
      this.helperService.values(this.item2.rbArrayInputListener).subscribe(data => {  // action on value change
        // assign value
        if (this.pushPath) {
          this.values[data.index][this.pushPath] = data.value;
        }
        else {
          this.values[data.index] = data.value;
        }
        this.updateArray();
      });
    }, 0);
  }

  updateArray() {
    let res;
    // adjust fields if pushTemplate is specified
    if (this.pushTemplate !== null) {
      if (this.pushPath) {
        // remove last element if last two are empty
        if (this.values[this.values.length - 1][this.pushPath] === '' &&
          this.values[this.values.length - 2][this.pushPath] === '') {
          this.values.pop();
        }
        // add element if last all are filled
        else if (this.values.filter(e => e[this.pushPath] !== '').length === this.values.length) {
          this.values.push(cloneDeep(this.pushTemplate));
        }
        res = this.values.filter(e => e[this.pushPath] !== '');
      }
      else {
        // remove last element if last two are empty
        if (this.values[this.values.length - 1] === '' && this.values[this.values.length - 2] === '') {
          this.values.pop();
        }
        else if (this.values.filter(e => e !== '').length === this.values.length) {  // add element if all are is filled
          this.values.push(cloneDeep(this.pushTemplate));
        }
        res = this.values.filter(e => e !== '');
      }
    }
    else {
      this.values = [this.values[0]];
      res = this.values;
    }
    if (!res.length) {
      res = [''];
    }
    this.onChange(res);  // trigger ngModel with filled elements
  }

  writeValue(obj: any) {  // add empty value on init
    if (obj) {
      if (this.pushTemplate !== null) {
        // filter out empty values
        if (this.pushPath) {
          this.values = [...obj.filter(e => e[this.pushPath] !== ''), cloneDeep(this.pushTemplate)];
        }
        else {
          this.values = [...obj.filter(e => e !== ''), cloneDeep(this.pushTemplate)];
        }
      }
      else {
        this.values = obj;
      }
    }
    else {
      if (this.pushTemplate !== null) {
        this.values = [cloneDeep(this.pushTemplate)];
      }
      else {
        this.values = [''];
      }
    }
  }

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

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