import { Component, OnInit, forwardRef, QueryList, ContentChildren, Input, ViewChild, ElementRef, AfterContentInit, HostListener, Output, EventEmitter } from '@angular/core';
import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms';
import { NeuOptionComponent } from '../dropdown/neu-option/neu-option.component';
import { NeuInputComponent } from '../neu-input/neu-input.component';

export const CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR:any = {
  provide: NG_VALUE_ACCESSOR,
  useExisting: forwardRef(() => NeuAutocompleteComponent),
  multi: true
}

@Component({
  selector: 'neu-autocomplete',
  templateUrl: './neu-autocomplete.component.html',
  styleUrls: ['./neu-autocomplete.component.css'],
  providers: [CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR]
})
export class NeuAutocompleteComponent implements ControlValueAccessor, AfterContentInit {

  //atributs de valors
  active:boolean = false;

  private innerValue:any;
  public input_text:string;
  private _options:QueryList<NeuOptionComponent>;
  @ContentChildren(NeuOptionComponent) set options(options:QueryList<NeuOptionComponent>) {
    if(options!=null && options!=undefined) {
      this._options = options;
      this._options.forEach(option => {
        //mirem si hem d'establir-lo
        if(JSON.stringify(option.value)===JSON.stringify(this.innerValue)) {
          this.input_text = option.text;
        }

        option.selected.subscribe(evt => {
          this.value = evt.value;
          this.input_text = evt.text;
          this.active = false;
        });
      });
    }
  }
  @Input() placeholder:string;
  @ViewChild(NeuInputComponent, { static: true }) input:NeuInputComponent;
  @ViewChild("optionsDiv") optionsDiv:ElementRef;
  @ViewChild("neu_autocomplete") neu_autocomplete:ElementRef;
  @Output() onInputChanged:EventEmitter<string> = new EventEmitter(); 
  @Input() filterMinLength:number = 0;

  //placeholders per els callbacks provided per control value accessor
  private onTouchedCallback:() => void;
  private onChangeCallback: (_:any) => void;

  constructor(private eRef: ElementRef) { }

  ngAfterContentInit() {
    this.initFocusInputSubscription();
  }

  //per quan cliquin fora del autocomplete, es plegui automaticament
  @HostListener('document:click', ['$event'])
  @HostListener('document:touchstart', ['$event'])
  clickout(event) {
    if(!this.neu_autocomplete.nativeElement.contains(event.target)) {
      this.active = false;
    }
  }

  @HostListener('window:scroll', ['$event']) 
  onScroll($event) {
    if(this.active) {
      this.renderOptionsPosition();
    }
  } 

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

  set value(v:any) {
    if(v!==this.innerValue) {
      this.innerValue = v;
      this.onChangeCallback(v);
    }
  }

  writeValue(value:any) {
    if(value && JSON.stringify(value)!==JSON.stringify(this.innerValue)) {
      this.innerValue = value;
      this._options?.forEach(o => { //si ja ve el valor donat de fora, doncs el posem
        if(JSON.stringify(o.value) === JSON.stringify(value)) {
          this.input_text = o.text;
        }
      });
    }
  }

  inputTextChanged(event:string) {
    if(event && event.length >= this.filterMinLength)
      this.onInputChanged.emit(event);
    //i des de fora s'han de filtrar les opcions
  }

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

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

  private initFocusInputSubscription() {
    this.input.focusInput.subscribe(
      focused => {
        if(focused) {
          this.renderOptionsPosition();
        }

        this.active = focused;
      }
    );
  }

  private renderOptionsPosition() {
    this.optionsDiv.nativeElement.style.top = (this.getOffset().y - this.getScroll().y + this.neu_autocomplete.nativeElement.offsetHeight) + "px";
    this.optionsDiv.nativeElement.style.left = (this.getOffset().x - this.getScroll().x) + "px";
    this.optionsDiv.nativeElement.style.width = `${this.neu_autocomplete.nativeElement.offsetWidth }px`;
  }

  private getScroll(el = this.eRef.nativeElement) {
    if(el && el.parentNode) {
      var x = 0;
      var y = 0;

      var fixed = false;
      var absolute = false;

      while(el) {
        if(el && el.parentNode) {
          const position = window.getComputedStyle(el).position;
          fixed = position == "fixed" ;
          absolute = position == "absolute";
          if(fixed) {
            el = null;
            break;
          }
          else if(absolute) {
            el = null;
            //x = 0;
            //y = 0;
            break;
          }
          else {
            x += el.scrollLeft;
            y += el.scrollTop;
            el = el.parentNode;
          }
        } else {
          el = null;
        }
      }

      return {x: x, y: y, fixed: fixed, absolute: absolute};
    }

    return null;
  }

  private getOffset(el = this.eRef.nativeElement) {
    if(el && el.parentNode) {
      var x = 0;
      var y = 0;
      var fixed = false;
      var absolute = false;

      while(el) {
        const position = window.getComputedStyle(el).position;
        absolute = absolute ? absolute : position == "absolute";
        fixed = fixed ? fixed : position == "fixed";
        if(absolute) {
          el = null;
        }
        else {
          x += el.offsetLeft;
          y += el.offsetTop;
          el = el.offsetParent;
        }
      }

      return {x: x, y: y, fixed: fixed, absolute: absolute};
    }

    return null;
  }

}
