import hsv2rgb from 'hsv2rgb';
import {Feature} from 'ol';
import {Fill, Stroke, Style} from 'ol/style';
import {Injectable} from '@angular/core';
import {TopoJSON} from 'ol/format';
import {Vector as VectorLayer} from 'ol/layer';
import {Vector as VectorSource} from 'ol/source';

import {HsConfig} from 'hslayers-ng/config.service';
import {HsEventBusService} from 'hslayers-ng/components/core/event-bus.service';
import {HsLanguageService} from 'hslayers-ng/components/language/language.service';
import {HsLayerManagerService} from 'hslayers-ng/components/layermanager';
import {HsLayoutService} from 'hslayers-ng/components/layout/layout.service';
import {HsPanelContainerService} from 'hslayers-ng/components/layout/panels/panel-container.service';
import {HsSidebarService} from 'hslayers-ng/components/sidebar/sidebar.service';

import {AdjusterComponent} from './adjuster/adjuster.component';
import {AdjusterEventService} from './adjuster/adjuster-event.service';
import {AdjusterLegendService} from './adjuster/adjuster-legend.service';
import {AdjusterService} from './adjuster/adjuster.service';
import {krajeLayer, obceIndexLayer, okresyLayer} from './app.config';

@Injectable({providedIn: 'root'})
export class AppService {
  constructor(
    public adjusterService: AdjusterService,
    public adjusterEventService: AdjusterEventService,
    public adjusterLegendService: AdjusterLegendService,
    public hsConfig: HsConfig,
    public hsEventBus: HsEventBusService,
    public hsLanguageService: HsLanguageService,
    public hsLayerManagerService: HsLayerManagerService,
    public hsLayoutService: HsLayoutService,
    public hsPanelContainerService: HsPanelContainerService,
    public hsSidebarService: HsSidebarService
  ) {
    this.adjusterEventService.loaded.subscribe(({success}) => {
      if (success) {
        this.adjusterLegendService.refreshColorPalette(
          this.adjusterService.numberOfClusters
        );
      }
    });
    this.hsEventBus.layoutLoads.subscribe(() => {
      this.init();
    });
    this.prepareLayers();
  }

  init(): void {
    this.hsLanguageService.setLanguage('cs');
    this.hsSidebarService.buttons.push({
      panel: 'adjuster',
      module: 'pra.adjuster',
      order: 0,
      title: () =>
        this.hsLanguageService.getTranslation('ADJUSTER.adjustFactors'),
      description: 'Adjust factors for computation',
      icon: 'icon-analytics-piechart',
    });
    this.hsPanelContainerService.create(AdjusterComponent, {});
    this.hsLayoutService.setDefaultPanel('adjuster');
  }

  /**
   * Create separate layer fo each clustering method and add it to default_layers
   */
  prepareLayers(): void {
    for (const method of this.adjusterService.methods) {
      method.layer = new VectorLayer({
        source: new VectorSource({
          format: new TopoJSON({dataProjection: 'EPSG:5514'}),
          url: require('./data/obce_cr_20210310_5p_5514.topojson').default,
          overlaps: false,
        }),
        editor: {editable: false},
        visible: true,
        style: this.generateStyle(method.codename),
        title: `Obce ČR: ${method.name.replaceAll(/\((.+?)\)/g, '')} clusters`,
        attributions: ['CC-BY ČÚZK, 2021'],
        popUp: {
          attributes: [
            {
              attribute: method.codename,
              label: 'ID shluku',
            },
          ],
        },
      });
      this.hsConfig.default_layers.push(method.layer);
    }
    // obceIndexLayer, okresyLayer and krajeLayer must be pushed in this order
    // so they will display in correct order
    this.hsConfig.default_layers.push(obceIndexLayer);
    obceIndexLayer.on('featuresloadend', this.adjusterService.init());
    obceIndexLayer.getSource().legend_categories = this.adjusterLegendService.createIndexLegend();
    this.hsConfig.default_layers.push(okresyLayer);
    this.hsConfig.default_layers.push(krajeLayer);
  }

  /**
   * @description Function factory for generating style functions based on different clustering methods
   * @param {string} method selected method
   * @returns {function} style function
   */
  private generateStyle(method: string) {
    return (feature: Feature): Style => {
      if (isNaN(feature.get(method))) {
        return new Style({
          fill: new Fill({
            color: '#FFF',
          }),
          stroke: new Stroke({
            color: '#3399CC',
            width: 0.25,
          }),
        });
      } else {
        return new Style({
          fill: new Fill({
            color: this.adjusterLegendService.colorPalette[
              feature.get(method) - 1
            ],
          }),
          stroke: new Stroke({
            color: '#FFF',
            width: 0.25,
          }),
        });
      }
    };
  }
}
