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

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 {AdjusterService} from './adjuster/adjuster.service';
import {krajeLayer, obce, obceIndexLayer, okresyLayer} from './app.config';

@Injectable({providedIn: 'root'})
export class AppService {
  // https://colorbrewer2.org/?type=qualitative&scheme=Paired&n=12
  colorPalette = [
    '#a6cee3',
    '#1f78b4',
    '#b2df8a',
    '#33a02c',
    '#fb9a99',
    '#e31a1c',
    '#fdbf6f',
    '#ff7f00',
    '#cab2d6',
    '#6a3d9a',
    '#ffff99',
    '#b15928',
  ];

  constructor(
    public adjusterService: AdjusterService,
    public adjusterEventService: AdjusterEventService,
    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.colorPalette = this.generateRandomColorPalette(
          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: obce,
        editor: {editable: false},
        visible: true,
        style: this.generateStyle(method.codename),
        title: `Obce ČR: ${method.name.replaceAll(/\((.+?)\)/g, '')} clusters`,
        attributions: ['CC-BY ČÚZK, 2021'],
      });
      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);
    this.hsConfig.default_layers.push(okresyLayer);
    this.hsConfig.default_layers.push(krajeLayer);
  }

  /**
   * https://martin.ankerl.com/2009/12/09/how-to-create-random-colors-programmatically/
   * @private
   * @description Only generates random colors if the current palette does not provide enough colors for all the clusters
   * @param {number} colorCount Number of colors to randomly generate
   * @returns {Array<Array<number> | string>} Array of RGB colors
   */
  private generateRandomColorPalette(colorCount: number) {
    const palette = this.colorPalette;
    const goldenRatioConjugate = 0.618033988749895;
    let i = palette.length;
    while (i < colorCount) {
      let h = Math.random();
      h += goldenRatioConjugate;
      h %= 1;
      h *= 360;
      palette.push(hsv2rgb(h, 0.5, 0.95));
      i++;
    }
    return palette;
    //return `rgba(${r}, ${g}, ${b}, 0.7)`;
  }

  /**
   * @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.colorPalette[feature.get(method) - 1],
          }),
          stroke: new Stroke({
            color: '#FFF',
            width: 0.25,
          }),
        });
      }
    };
  }
}
