import { Component, Host, Input, Output, EventEmitter, OnInit, OnChanges, SimpleChanges, forwardRef, Inject, InjectionToken, OnDestroy, LOCALE_ID } from '@angular/core';
import { LayerVectorComponent, SourceVectorComponent, MapComponent } from '@farmmaps/ng-openlayers';
import { ItemService, ItemTypeService, IItem, IItemType, FolderService } from '@farmmaps/common';

import { Feature } from 'ol';
import { Point, Geometry } from 'ol/geom';
import { MapBrowserEvent } from 'ol';
import { Types } from 'ol/MapBrowserEventType';
import * as style from 'ol/style';
import * as color from 'ol/color';
import * as loadingstrategy from 'ol/loadingstrategy';
import * as condition from 'ol/events/condition';
import * as extent from 'ol/extent';
import { Vector, Cluster, Source } from 'ol/source';
import { Layer } from 'ol/layer';
import { GeoJSON } from 'ol/format';
import { Select } from 'ol/interaction';
import { IStyles } from '../../../models/style.cache';
import { FeatureIconService } from '../../../services/feature-icon.service';
import { Subscription } from 'rxjs';
import { getCenter } from 'ol/extent';
import { formatNumber } from '@angular/common';

@Component({
  selector: 'fm-map-item-source-vector',
  template: `<ng-content></ng-content>`,
  providers: [
    { provide: SourceVectorComponent, useExisting: forwardRef(() => ItemVectorSourceComponent) }
  ]
})
export class ItemVectorSourceComponent extends SourceVectorComponent implements OnInit, OnDestroy, OnChanges {
  instance: Vector<Feature<Geometry>>;
  private _format: GeoJSON;
  private _select: Select;
  private _hoverSelect: Select;
  private _iconScale = 0.05;
  @Input() features: Array<Feature<Geometry>>;
  @Input() selectedFeature: Feature<Geometry>;
  @Input() selectedItem: IItem;
  @Input() styles: IStyles;
  @Output() onFeatureSelected: EventEmitter<Feature<Geometry>> = new EventEmitter<Feature<Geometry>>();
  @Output() onFeatureHover: EventEmitter<Feature<Geometry>> = new EventEmitter<Feature<Geometry>>();
  private stylesCache: IStyles = {};
  private sub: Subscription;
  private displayMapFeatureSettings: { [code: string]: string[] } = defaultDisplayMapFeatureSettings();

  constructor(@Host() private layer: LayerVectorComponent, private itemService: ItemService, private map: MapComponent, private itemTypeService: ItemTypeService, private featureIconService$: FeatureIconService, private folderService: FolderService, @Inject(LOCALE_ID) private locale: string) {
    super(layer);
    this._format = new GeoJSON();
  }

  geometry(feature: Feature<Geometry>) {
    const view = this.map.instance.getView();
    const resolution = view.getResolution();
    let geometry = feature.getGeometry();
    const e = geometry.getExtent();
    //var size = Math.max((e[2] - e[0]) / resolution, (e[3] - e[1]) / resolution);
    if (resolution > 12) {
      geometry = new Point(extent.getCenter(e));
    }
    return geometry;
  }

  getSelectedStyle(feature: Feature<Geometry>): style.Style {
    const key = feature.get('itemType') + "_selected";
    let evaluatedStyle: style.Style = undefined;
    const styleEntry = this.stylesCache[key];
    if (styleEntry) {
      if (typeof styleEntry === 'function') {
        evaluatedStyle = styleEntry(feature);
      } else {
        evaluatedStyle = styleEntry;
      }
    } else {
      evaluatedStyle = this.stylesCache["selected"] as style.Style;
    }
    if (evaluatedStyle) {
      evaluatedStyle.setGeometry((feature: Feature<Geometry>) => this.geometry(feature));
    }
    return evaluatedStyle as style.Style
  }

  ngOnInit() {
    this.sub = this.folderService.getFolder('my_settings').subscribe(
      userSettingsRoot => {
        if (userSettingsRoot == undefined) return;
        this.itemService.getChildItemList(userSettingsRoot.code, 'vnd.farmmaps.itemtype.settings.general').subscribe(
          items => {
            if (items && items.length > 0 && items[0].data?.displayMapFeatureSettings) {
              this.displayMapFeatureSettings = items[0].data?.displayMapFeatureSettings;
            }
          }
        )
      }
    );
    this.strategy = loadingstrategy.bbox;
    this.format = new GeoJSON();
    this._select = new Select({
      style: null,
      hitTolerance: 10,
      layers: [this.layer.instance as Layer<Source>]
    });
    this._hoverSelect = new Select({
      style: (feature: Feature<Geometry>) => {
        return this.getSelectedStyle(feature);
      },
      hitTolerance: 10,
      condition: (e: MapBrowserEvent<UIEvent>) => {
        return e.type == 'pointermove';
      },
      layers: [this.layer.instance as Layer<Source>]
    });
    this.map.instance.addInteraction(this._select);
    this.map.instance.addInteraction(this._hoverSelect);
    this._select.on('select', (e) => {
      if (e.selected.length > 0 && e.selected[0]) {
        this.onFeatureSelected.emit(e.selected[0]);
      } else {
        this.onFeatureSelected.emit(null);
      }
    });
    this._hoverSelect.on('select', (e) => {
      if (e.selected.length > 0 && e.selected[0]) {
        this.onFeatureHover.emit(e.selected[0]);
      } else {
        this.onFeatureHover.emit(null);
      }
    });
    this.instance = new Vector(this);
    this.host.instance.setSource(this.instance);

    this.host.instance.setStyle((feature) => {
      const itemType = feature.get('itemType');
      let key = itemType + (this.selectedItem ? "_I" : "");
      if (!this.stylesCache[key]) {
        if (this.itemTypeService.itemTypes[itemType]) {
          const itemTypeEntry = this.itemTypeService.itemTypes[itemType];
          const fillColor = color.asArray(itemTypeEntry.iconColor);
          fillColor[3] = 0;
          this.stylesCache[key] = new style.Style({
            image: itemTypeEntry.icon ? new style.Icon({
              anchor: [0.5, 1],
              scale: 0.05,
              src: this.featureIconService$.getIconImageDataUrl(itemTypeEntry.icon)
            }) : null,
            stroke: new style.Stroke({
              color: 'red',
              width: 1
            }),
            fill: new style.Fill({
              color: fillColor
            }),
            geometry: (feature: Feature<Geometry>) => this.geometry(feature),
            text: this.getDisplayTextForFeature(feature, this.map.instance.getView().getZoom())
          });
        } else {
          key = 'file';
        }
      }
      let evaluatedStyle = null;
      const styleEntry = this.stylesCache[key];
      if (typeof styleEntry === 'function') {
        evaluatedStyle = styleEntry(feature);
      } else {
        evaluatedStyle = styleEntry;
      }
      if (evaluatedStyle && evaluatedStyle.geometry_ == null && !Array.isArray(evaluatedStyle)) {
        evaluatedStyle.setGeometry((feature) => this.geometry(feature));
      }
      return evaluatedStyle;
    });
  }

  ngOnDestroy(): void {
    if (this.sub) this.sub.unsubscribe();
  }

  ngOnChanges(changes: SimpleChanges) {
    if (changes["features"] && this.instance) {
      this.instance.clear(true);
      this._select.getFeatures().clear();
      this.instance.addFeatures(changes["features"].currentValue);
    }

    if (changes["selectedFeature"] && this.instance) {
      const features = this._hoverSelect.getFeatures();
      const feature = changes["selectedFeature"].currentValue
      //this.instance.clear(false);
      //this.instance.addFeatures(features.getArray());
      features.clear();
      if (feature) {
        //this.instance.removeFeature(feature);
        features.push(feature)
      }
    }
    if (changes["selectedItem"] && this.instance) {
      const item = changes["selectedItem"].currentValue
      if (item) {
        this.map.instance.removeInteraction(this._hoverSelect);
      } else {
        this.map.instance.addInteraction(this._hoverSelect);
      }
    }
    if (changes["styles"]) {
      const styles = changes["styles"].currentValue;
      for (const key in styles) {
        if (styles.hasOwnProperty(key)) {
          this.stylesCache[key] = styles[key];
        }
      }
    }
  }
  
  getDisplayTextForFeature(feature: Feature<Geometry>, zoom: number, overrule?: style.Text) {
    if (!feature) return null;

    const propertiesToShow: string[] = this.displayMapFeatureSettings[feature.get('itemType')];
    if (!propertiesToShow) return null;
    if (propertiesToShow.length <= 0) return null;
    if (zoom < 14) return null;

    let displayText = '';
    for (let i = 0; i < propertiesToShow.length; i++) {
      let value = feature.get(propertiesToShow[i]);
      switch (propertiesToShow[i]) {
        case "area": value = formatNumber(value, this.locale, '0.1-2') + ' ha'; break;
        case "centroid": {
          if (feature.getGeometry()) {
            const centroid = getCenter(feature.getGeometry().getExtent());
            value = Math.round(centroid[0]) + ',' + Math.round(centroid[1]);
          }
        }
          if (value) {
            displayText += value + (i < propertiesToShow.length ? '\n' : '');
          }
      }
    
      const styleText = new style.Text({
        font: '13px Calibri,sans-serif',
        fill: new style.Fill({ color: '#ffffff' }),
        stroke: new style.Stroke({ color: '#000000', width: 2 }),
        text: displayText
      });

      if (overrule) {
        if (overrule.getFont()) styleText.setFont(overrule.getFont());
        if (overrule.getFill()) styleText.setFill(overrule.getFill());
        if (overrule.getStroke()) styleText.setStroke(overrule.getStroke());
      }

      return styleText;
    }
  }
}

function defaultDisplayMapFeatureSettings() {
  return {
    'vnd.farmmaps.itemtype.cropfield': ['name', 'cropTypeName', 'area']
  };
}