import {Component, OnDestroy, OnInit, HostListener, AfterViewInit, NgZone, ElementRef, ViewChild} from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import * as moment from 'moment-timezone';
import { GraphLoader } from '../../shared/graph-loading/graphloader';
import { ObservationService } from '../../shared/api/endpoints/services/observation.service';
import { HttpResponse } from '@angular/common/http';
import { map } from 'rxjs/operators';
import { ToastService } from '../../shared/services/toast.service';
import { Sensor } from '../../shared/api/endpoints/models/sensor';
import { SensorsService } from '../../shared/api/endpoints/services/sensors.service';
import { Subscription } from 'rxjs';
import type { View } from 'vega';

@Component({
  selector: 'app-sensor',
  templateUrl: './sensor.component.html',
  styleUrls: ['./sensor.component.scss']
})
export class SensorComponent implements OnInit, OnDestroy, AfterViewInit {

  sensorId: number;
  unitId: number;
  data = [];
  time = [];
  yFrom : number;
  yTo : number;
  from: Date = moment().hour(0).minutes(0).subtract(7, 'days').toDate();
  to: Date = moment().toDate();
  sensor: Sensor;
  dateChanged = false;
  unitDescription: string;
  today: Date = moment().toDate();
  subscription: Subscription[] = [];
  showIntervalError = false;
  private resizeObserver!: ResizeObserver; // Will watch the container size
  graphView;
  @ViewChild('viewPanel') viewElement!: ElementRef;

  constructor(
    private activatedRoute: ActivatedRoute,
    private observationService: ObservationService,
    private toastService: ToastService,
    private sensorsService: SensorsService,
    private route: ActivatedRoute,
    private ngZone: NgZone
  ) {
    this.getInitData();
    this.sensorsService.getUnitSensors({ unit_id: this.unitId }).subscribe(sensors => {
      if (sensors) {
        this.sensor = sensors.filter(value => value.sensorId === this.sensorId)[0];
        this.showGraph(); // show default graph
      }
    }, err => this.toastService.showError(err.error.message));
  }

  ngOnInit(): void {
  }

  ngAfterViewInit() {
    this.setupResizeObserver();
  }

  private setupResizeObserver() {
    this.resizeObserver = new ResizeObserver((entries) => {
      // Use NgZone.run if you need to update Angular variables/UI or requestAnimationFrame to sync with the browser paint
      this.ngZone.run(() => {
        this.onResize();
      });
    });

    // Start observing the native element
    if (this.viewElement) {
      this.resizeObserver.observe(this.viewElement.nativeElement);
    }
  }

  /**
   * Sets up default data
   */
  getInitData() {
    this.route.queryParams.subscribe(params => {
      if (params.unitName) {
        this.unitDescription = params.unitName;
      }
    });
    this.sensorId = parseInt(this.activatedRoute.snapshot.paramMap.get('sensorId'), 10);
    this.unitId = parseInt(this.activatedRoute.snapshot.paramMap.get('unitId'), 10);
  }

  /**
   * Unsubscribe after leaving
   */
  ngOnDestroy(): void {
    this.subscription.forEach(subs => subs.unsubscribe());
    if (this.resizeObserver) {
      this.resizeObserver.disconnect();
    }
  }

  /**
   * Shows get data button
   */
  onDateChanged(): void {
    if (moment(this.to).diff(moment(this.from), 'months') > 6){
      this.dateChanged = false;
      this.showIntervalError = true;
    }
    else if (this.to < this.from) {
      this.dateChanged = false;
      this.showIntervalError = true;
    }
    else{
      this.dateChanged = true;
      this.showIntervalError = false;
    }
  }

  /**
   * Gets data based on selected time range
   */
  showGraph() {
    const range: Date[] = [this.from, this.to];
    this.getObservations(range);
  }

  /**
   * Get data from observation endpoint
   * @param range from and to interval
   */
  async getObservations(range: Date[]) {
    const box = document.getElementById('view');
    const boxWidth = box.getBoundingClientRect().width;
    const boxHeight = box.getBoundingClientRect().height;

    GraphLoader.setSize(boxWidth - 50, 300);

    this.observationService.getObservation$Response({
      unit_id: this.unitId,
      sensor_id: this.sensorId,
      from: moment(range[0]).format('yyyy-MM-DD HH:mm:ssZ').slice(0, -3),
      to: moment(range[1]).format('yyyy-MM-DD HH:mm:ssZ').slice(0, -3)
    }).pipe(
      map((response: HttpResponse<any>) => {
        if (response.status === 200) {
          return response.body;
        } else if (response.status === 204) {
          this.toastService.showWarningNoData();
          return response.body;
        } else {
          return false;
        }
      })
    ).subscribe(
      async observations => {
        if (observations) {
          this.graphView = await GraphLoader.getGraph(this.sensorId, observations, this.sensor, '#view', false);
        } else {
          this.graphView = await GraphLoader.getGraph(null, null, null, '#view', null);
        }
      }, err => this.toastService.showError(err.error.message));
  }

  private onResize(): void {
    // resize graph width if window changes size
    const box = this.viewElement.nativeElement;
    if (this.graphView) {
      const newWidth = box.getBoundingClientRect().width - 50;
      GraphLoader.ResizeGraph(this.graphView, newWidth);
    }
  }

  changeGraphY() {
    console.log(this.yFrom + ' ' + this.yTo);
    // check if the values are valid - To is larger than From and they are not equal
    if (this.yTo < this.yFrom || this.yTo === this.yFrom) {
      return;
    }
    GraphLoader.ChangeYRange(this.graphView, this.yFrom, this.yTo);
  }
}
