import { Directive, ElementRef, Input, OnDestroy, OnInit } from '@angular/core';
import { Subscription } from 'rxjs';
import { ConfigService } from '@shared/config/config.service';

@Directive({
  selector: '[appFlex]'
})
export class FlexDirective implements OnInit, OnDestroy {
  @Input() appFlex: string = null;
  @Input() xsFlex: string = null;
  @Input() smFlex: string = null;
  @Input() mdFlex: string = null;
  @Input() ltSmFlex: string = null;
  @Input() ltMdFlex: string = null;
  @Input() ltLgFlex: string = null;
  currentBreakPoint;
  breakpointSubscription: Subscription;
  private defaultSet = false;

  constructor(
    private elementRef: ElementRef,
    private config: ConfigService,
  ) { }

  private setDefaultFlex(breakpoint: string) {
    if (this.currentBreakPoint !== breakpoint) {
      this.currentBreakPoint = breakpoint;
      this.setFlex(this.appFlex);
      this.defaultSet = true;
    }
  }

  ngOnInit() {
    if (this.config.appBreakpointObservable) {
      this.breakpointSubscription = this.config.appBreakpointObservable.subscribe(breakpoint => {
        switch (breakpoint) {
          case 'xs':
            if (this.xsFlex !== null && this.currentBreakPoint !== 'xs') {
              this.currentBreakPoint = 'xs';
              this.setFlex(this.xsFlex);
              this.defaultSet = false;
            } else if (this.ltSmFlex !== null && this.currentBreakPoint !== 'xs') {
              this.currentBreakPoint = 'xs';
              this.setFlex(this.ltSmFlex);
              this.defaultSet = false;
            } else if (this.ltMdFlex !== null && this.currentBreakPoint !== 'xs') {
              this.currentBreakPoint = 'xs';
              this.setFlex(this.ltMdFlex);
              this.defaultSet = false;
            } else if (this.ltLgFlex !== null && this.currentBreakPoint !== 'xs') {
              this.currentBreakPoint = 'xs';
              this.setFlex(this.ltLgFlex);
              this.defaultSet = false;
            } else {
              this.setDefaultFlex(breakpoint);
            }
            break;
          case 'sm':
            if (this.smFlex !== null && this.currentBreakPoint !== 'sm') {
              this.currentBreakPoint = 'sm';
              this.setFlex(this.smFlex);
              this.defaultSet = false;
            } else if (this.ltMdFlex !== null && this.currentBreakPoint !== 'sm') {
              this.currentBreakPoint = 'sm';
              this.setFlex(this.ltMdFlex);
              this.defaultSet = false;
            } else if (this.ltLgFlex !== null && this.currentBreakPoint !== 'sm') {
              this.currentBreakPoint = 'sm';
              this.setFlex(this.ltLgFlex);
              this.defaultSet = false;
            } else {
              this.setDefaultFlex(breakpoint);
            }
            break;
          case 'md':
            if (this.mdFlex !== null && this.currentBreakPoint !== 'md') {
              this.currentBreakPoint = 'md';
              this.setFlex(this.mdFlex);
              this.defaultSet = false;
            } else if (this.ltLgFlex !== null && this.currentBreakPoint !== 'md') {
              this.currentBreakPoint = 'md';
              this.setFlex(this.ltLgFlex);
              this.defaultSet = false;
            } else {
              this.setDefaultFlex(breakpoint);
            }
            break;
          default:
            this.setDefaultFlex(breakpoint);
        }
      });
    }
  }

  private setFlex(value: string) {
    if (value === null || value === 'none') {
      return;
    }
    const elementAttributes = this.elementRef.nativeElement.attributes as NamedNodeMap;
    const alignItem = elementAttributes.getNamedItem('layoutalign');
    if (!alignItem) {
      // align settings are already defined on element
      this.elementRef.nativeElement.classList.add('fl-display-flex');
    }
    const directionItem = elementAttributes.getNamedItem('layoutdirection');
    const skipFlexDirectionAdd = this.elementRef.nativeElement.classList.contains('mat-mdc-form-field');
    if (!directionItem && !skipFlexDirectionAdd) {
      // set direction only if not already defined on element
      this.elementRef.nativeElement.style.flexDirection = 'row';
    }

    // decide if height or width needs to be updated
    const parentDirection = this.elementRef.nativeElement.parentElement.attributes.getNamedItem('layoutdirection');
    const parentDirectionXs = this.elementRef.nativeElement.parentElement.attributes.getNamedItem('layoutdirection-xs');
    const parentDirectionLtSm = this.elementRef.nativeElement.parentElement.attributes.getNamedItem('layoutdirection-lt-sm');
    const parentDirectionLtMd = this.elementRef.nativeElement.parentElement.attributes.getNamedItem('layoutdirection-lt-md');
    let updateHeight: boolean;
    switch (this.currentBreakPoint) {
      case 'xs':
        updateHeight = (
          parentDirection?.value === 'column' || parentDirectionXs?.value === 'column' ||
          parentDirectionLtSm?.value === 'column' || parentDirectionLtMd?.value === 'column'
        );
        break;
      case 'sm':
        updateHeight = parentDirection?.value === 'column' || parentDirectionLtMd?.value === 'column';
        break;
      default:
        updateHeight = parentDirection?.value === 'column';
    }

    this.elementRef.nativeElement.style.boxSizing = 'border-box';
    let flexNumberValue;
    let flexUnit;
    let maxDimensionValue = null;

    if (!value) {
      flexNumberValue = 0;
      flexUnit = '%';
    } else if (value === 'auto') {
      flexNumberValue = null;
      this.elementRef.nativeElement.style.flex = '1 1 auto';
    } else if (value.indexOf(' ') > 0) {
      flexNumberValue = null;
      const splitValue = value.split(' ');
      if (splitValue.length === 3) {
        if (['1', '2', '3', '4', '5'].includes(splitValue[0]) && splitValue [1] === '0') {
          this.elementRef.nativeElement.style.minWidth = splitValue[2];
          this.elementRef.nativeElement.style.maxWidth = null;
        } else if ((splitValue[0] === '0' && splitValue [1] === '0') || (splitValue[0] === '1' && splitValue [1] === '1')) {
          this.elementRef.nativeElement.style.minWidth = splitValue[2];
          this.elementRef.nativeElement.style.maxWidth = splitValue[2];
        }
      }
      flexUnit = '%'; // not used
    } else {
      if (value.endsWith('px')) {
        const parsedFlexValue = value.replace('px', '');
        flexNumberValue = parseInt(parsedFlexValue, 10);
        flexUnit = 'px';
      } else if (value.endsWith('%')) {
        const parsedFlexValue = value.replace('%', '');
        flexNumberValue = parseInt(parsedFlexValue, 10);
        flexUnit = '%';
      } else {
        flexNumberValue = 100;
        maxDimensionValue = `${parseInt(value, 10)}%`;
        flexUnit = '%';
      }
    }
    if (flexNumberValue !== null && Number.isInteger(flexNumberValue)) {
      this.elementRef.nativeElement.style.flex = `1 1 ${flexNumberValue}${flexUnit}`;
      if (flexNumberValue) {
        if (flexUnit !== '%') {
          this.elementRef.nativeElement.style.minWidth = `${flexNumberValue}${flexUnit}`;
        }
        if (updateHeight) {
          this.elementRef.nativeElement.style.maxHeight = maxDimensionValue || `${flexNumberValue}${flexUnit}`;
          this.elementRef.nativeElement.style.maxWidth = 'none';
        } else {
          this.elementRef.nativeElement.style.maxWidth = maxDimensionValue || `${flexNumberValue}${flexUnit}`;
          this.elementRef.nativeElement.style.maxHeight = 'none';
        }
      }
    } else {
      this.elementRef.nativeElement.style.flex = value;
    }

    // parent must be flex
    this.elementRef.nativeElement.parentElement.style.display = 'flex';
  }

  ngOnDestroy() {
    if (this.breakpointSubscription) {
      this.breakpointSubscription.unsubscribe();
      this.breakpointSubscription = null;
    }
  }


}
