import {
  ChangeDetectionStrategy,
  ChangeDetectorRef,
  Component,
  effect,
  inject,
  input,
  OnDestroy
} from '@angular/core';

import TileLayer from 'ol/layer/Tile';
import { TileWMS } from 'ol/source';

import { MapComponent } from '../map.component';

import { ChartImageAdjustment, FBChart } from 'src/app/types';
import { Map } from 'ol';
import { MapService } from '../map.service';
import {
  attachImageAdjustmentFilter,
  chartLayerClassName,
  extentFromBounds,
  resolveLayerZoomRange
} from './chart-utils';

// ** Freeboard WMS Chart **
@Component({
  selector: 'ol-map > fb-wms-chart',
  template: '<ng-content></ng-content>',
  changeDetection: ChangeDetectionStrategy.OnPush,
  standalone: false
})
export class WmsChartLayerComponent implements OnDestroy {
  protected chart = input<FBChart>();
  protected zIndex = input<number>();
  protected params =
    input<Array<{ id: string; param: { [key: string]: any } }>>();
  protected overZoomTiles = input<boolean>(true);
  protected mapMaxZoom = input<number>();

  private layer: TileLayer;
  private setImageAdjustment?: (adj?: ChartImageAdjustment) => void;
  private changeDetectorRef = inject(ChangeDetectorRef);
  private mapComponent = inject(MapComponent);
  private mapService = inject(MapService);

  private map: Map;
  private featureUrl: string = undefined;

  constructor() {
    this.changeDetectorRef.detach();
    this.map = this.mapComponent.getMap();
    effect(() => {
      this.chart();
      this.zIndex();
      this.overZoomTiles();
      this.mapMaxZoom();
      this.parseChart();
    });
    effect(() => {
      if (!this.layer || !Array.isArray(this.params())) return;
      const src = this.layer.getSource();
      if (src) {
        (src as TileWMS).updateParams(this.params);
      }
    });
    effect(() => {
      const ev = this.mapComponent.pointerDownSignal();
      if (!ev) {
        return;
      }
      const view = this.map.getView();
      const prj = view.getProjection();
      const resolution = view.getResolution();
      const coord = this.map.getEventCoordinate(ev);
      const src: TileWMS = this.layer.getSource() as TileWMS;
      this.featureUrl = src.getFeatureInfoUrl(coord, resolution, prj, {
        INFO_FORMAT: 'application/json'
      });
      if (this.featureUrl) {
        this.mapService.addFeatureUrls({
          id: this.chart()[0],
          name: this.chart()[1].name,
          type: 'chart',
          subType: 'wms',
          url: this.featureUrl
        });
      }
    });
  }

  ngOnDestroy() {
    if (this.layer) {
      this.map.removeLayer(this.layer);
      this.map.render();
    }
  }

  private parseChart(chart: FBChart = this.chart()) {
    if (!this.map) {
      return;
    }

    if (!this.layer) {
      const zoom = resolveLayerZoomRange(
        chart[1],
        this.mapMaxZoom(),
        this.overZoomTiles()
      );

      this.layer = new TileLayer({
        source: new TileWMS({
          url: chart[1].url,
          params: {
            LAYERS: chart[1].layers ? chart[1].layers.join(',') : ''
          }
        }),
        preload: 0,
        zIndex: this.zIndex(),
        minZoom: zoom.min,
        maxZoom: zoom.max,
        opacity: chart[1].defaultOpacity ?? 1,
        extent: extentFromBounds(chart[1].bounds),
        className: chartLayerClassName(chart[0])
      });

      if (this.layer) {
        this.layer.set('id', chart[0]);
        this.layer.set('chartId', chart[0]);
        this.layer.set('chartType', chart[1].type);
        this.layer.set('chartFormat', chart[1].format);
        this.setImageAdjustment = attachImageAdjustmentFilter(this.layer);
        this.map.addLayer(this.layer);
      }
    } else {
      const zoom = resolveLayerZoomRange(
        chart[1],
        this.mapMaxZoom(),
        this.overZoomTiles()
      );
      this.layer.setZIndex(this.zIndex());
      this.layer.setMinZoom(zoom.min);
      this.layer.setMaxZoom(zoom.max);
      this.layer.setOpacity(chart[1].defaultOpacity ?? 1);
      this.layer.setExtent(extentFromBounds(chart[1].bounds));
      const src = this.layer.getSource() as TileWMS;
      const l = chart[1].layers ? chart[1].layers.join(',') : '';
      const p = src.getParams();
      if (p.LAYERS && p.LAYERS !== l) {
        src.updateParams({ LAYERS: l });
        src.refresh();
      }
    }
    this.setImageAdjustment?.(chart[1].imageAdjustment);
    this.map.render();
  }
}
