import 'chartjs-adapter-moment';
import {
  ChangeDetectionStrategy,
  ChangeDetectorRef,
  Component,
  ElementRef,
  Input
} from "@angular/core";

import {Chart as ChartJs, ChartType, DefaultDataPoint} from "chart.js";
import "chartjs-adapter-moment";
import {
  AdvancedProperty, ChartConfigDto,
  ChartScaleSettings, ChartTitleSettings,
  ChartV2AdditionalElementProvider, ChartV2RpcService,
  Control, Filter,
  Filterable, FilterDelegate, NotNull, PointSettings, UnsupportedOperationError,
  Visible
} from "@webbpm/base-package";
import {ChartLegendSettings} from "./model/ChartLegendSettings";
import {ChartBarSettings} from "./model/ChartBarSettings";
import {ChartPlugin} from "./plugin/ChartPlugin";
import {DoughnutCenterLabelsPlugin} from "./plugin/DoughnutCenterLabelsPlugin";
import {ChartUtils} from "./ChartUtils";

@Component({
             moduleId: module.id,
             selector: 'ervu-chart-v2',
             templateUrl: './../../../../../src/resources/template/ervu-dashboard/ErvuChartV2.html',
             changeDetection: ChangeDetectionStrategy.OnPush
           })
export class ErvuChartV2 extends Control implements Filterable {
  @Visible("false")
  public disabled: boolean;

  @Input()
  public indexAxis: string = 'x';
  @Input()
  public title: ChartTitleSettings;
  @Input()
  public legend: ChartLegendSettings;
  @Input()
  public points: PointSettings;
  @Input()
  public scales: ChartScaleSettings[];

  public bars: ChartBarSettings;

  @NotNull()
  public loadOnStart: boolean = true;

  @Visible("false")
  @Input()
  public chart: ChartJs<ChartType, DefaultDataPoint<ChartType>, unknown>;

  @NotNull()
  @AdvancedProperty()
  public showTooltip: boolean = true;

  @NotNull()
  @AdvancedProperty()
  public responsive: boolean = true;

  @NotNull()
  @AdvancedProperty()
  public maintainAspectRatio: boolean = false;

  @NotNull()
  @AdvancedProperty()
  public addDataSetsVisibilityToggleDataset: boolean = false;

  @AdvancedProperty()
  @Visible("addDataSetsVisibilityToggleDataset==true")
  @NotNull("addDataSetsVisibilityToggleDataset==true")
  public hideAllDatasetsLabel: string = 'Cкрыть все';

  @AdvancedProperty()
  @Visible("addDataSetsVisibilityToggleDataset==true")
  @NotNull("addDataSetsVisibilityToggleDataset==true")
  public showAllDatasetsLabel: string = 'Показать все';

  @AdvancedProperty()
  public noDataText: string = "Нет данных для отображения";
  @AdvancedProperty()
  public noDataFont: string = "20px Arial";
  @AdvancedProperty()
  public noDataStyle: string = "rgb(100,100,100)";

  private chartRpcService: ChartV2RpcService;
  private filterMap: { [key: string]: Filter } = {};
  private loadEnabled: boolean = true;
  private filterDelegate: FilterDelegate;
  private chartConfig;
  private plugins: ChartPlugin[] = [];

  private noDataPlugin = {
    afterDraw: (chart) => {

      if (ChartUtils.isEmpty(chart)) {
        let ctx = chart.ctx;
        let width = chart.width;
        let height = chart.height;
        ctx.textAlign = "center";
        ctx.textBaseline = "middle";
        ctx.font = this.noDataFont;
        ctx.fillStyle = this.noDataStyle;
        ctx.fillText(this.noDataText, width / 2, height / 2);
        ctx.restore();
      }
    },
  };

  private addAdditionalElementsPlugin = {
    afterDraw: () => {
      this.getScripts(ChartV2AdditionalElementProvider)
          .forEach(additionalElementProvider =>
                       additionalElementProvider.addAdditionalElement(this))
    },
  };

  constructor(el: ElementRef, cd: ChangeDetectorRef) {
    super(el, cd);
  }

  initialize(): void {
    super.initialize();
    this.chartRpcService = this.getScript(ChartV2RpcService);
    this.filterDelegate = this.getScript(FilterDelegate);
    this.plugins = this.getScripts('ervu-dashboard.component.chart.plugin.ChartPlugin');
  }

  start(): void {
    super.start();

    if (this.loadOnStart) {
      this.chartRpcService
          .getChartData(null, null, [])
          .then((config: ChartConfigDto) => {
            this.loadDataAndPaint(
                config
            )
          })
    }
  }

  addFilter(filter: Filter): void {
    this.filterMap[filter.componentGuid] = filter;

    if (this.filterDelegate) {
      this.filterDelegate.addFilter(filter);
    }
  }

  disableInitialLoading() {
    this.loadOnStart = false;
    this.loadEnabled = false;

    if (this.filterDelegate) {
      this.filterDelegate.disableInitialLoading();
    }
  }

  reload(filters: Filter[]): void {
    filters.forEach((filter: Filter) => this.addFilter(filter));
    this.refresh();

    if (this.filterDelegate) {
      this.filterDelegate.reload(filters);
    }
  }

  removeFilter(guid: string): Filter {
    let ret = this.filterMap[guid];
    delete this.filterMap[guid];

    if (this.filterDelegate) {
      this.filterDelegate.removeFilter(guid);
    }
    return ret;
  }

  getFilters(): Filter[] {
    return Object.values(this.filterMap);
  }

  @Visible()
  public refresh() {
    this.chartRpcService
        .getChartData(null, null, this.getFilters())
        .then((configDto: ChartConfigDto) => this.loadDataAndPaint(configDto));
  }

  public loadDataAndPaint(config: ChartConfigDto) {
    this.initAndSaveChartData(config);
    this.repaint(this.chartConfig);
  }

  private initAndSaveChartData(chartConfig) {
    //To make visible empty chart in preview mode
    if (!chartConfig) {
      chartConfig = {};
    }
    let chartOptions: any = {};
    chartConfig.options = chartOptions;
    chartOptions.maintainAspectRatio = this.maintainAspectRatio;
    chartOptions.responsive = this.responsive;

    if (this.indexAxis) {
      chartOptions.indexAxis = this.indexAxis;
    }

    chartOptions.plugins = chartOptions.plugins ? chartOptions.plugins : {};

    chartOptions.plugins.legend = this.legend ? this.legend : {};
    chartOptions.plugins.tooltip = {enabled: this.showTooltip};

    if (this.title && this.title.text) {
      chartOptions.plugins.title = this.title;
    }

    if (this.addDataSetsVisibilityToggleDataset && chartConfig.data.datasets &&
        chartConfig.data.datasets.length > 0) {
      let datasetLength = chartConfig.data.datasets.length;
      chartConfig.data.datasets.push({
                                       label: this.hideAllDatasetsLabel,
                                       xAxisID: {display: false},
                                       yAxisID: {display: false}
                                     });

      chartOptions.plugins.legend.onClick = (e, legendItem, legend) => {
        const index = legendItem.datasetIndex;
        const chartObj = legend.chart;

        let datasetMeta = chartObj.getDatasetMeta(index);

        if (datasetLength == index) {
          let showAll = datasetMeta.label == this.showAllDatasetsLabel;
          chartObj.data.datasets[index].label =
              showAll ? this.hideAllDatasetsLabel : this.showAllDatasetsLabel;

          for (let i = 0; i < datasetLength; i++) {
            chartObj.setDatasetVisibility(i, showAll);
          }
          chartObj.update();
        }
        else {
          if (chartObj.isDatasetVisible(index)) {
            chartObj.hide(index);
            legendItem.hidden = true;
          }
          else {
            chartObj.show(index);
            legendItem.hidden = false;
          }
        }
      };
    }

    if (chartConfig.data.datasets) { // remove it after change type of ChartDataSetDto.backgroundColor
      chartConfig.data.datasets.forEach(dataset => {
        if (dataset.backgroundColors) dataset.backgroundColor = dataset.backgroundColors;
      });
    }

    if (this.points) {
      chartOptions.elements = chartOptions.elements ? chartOptions.elements : {};
      chartOptions.elements.point = this.points;
    }

    if (this.scales) {
      chartOptions.scales = {};
      this.scales.forEach((scale: ChartScaleSettings) => {
        chartOptions.scales[scale.scaleId] = scale.toChartJsSettings();
      });
    }

    if (this.bars) {
      chartOptions.scales = chartOptions.scales ? chartOptions.scales : {};
      chartOptions.scales.x = this.bars.x;
      chartOptions.scales.y = this.bars.y
    }

    chartConfig.tooltips = {
      callbacks: {
        title: function (tooltipItem, data) {
          return data.labels[tooltipItem[0].index]
        },
        label: function (tooltipItem, data) {
          let dataset = data.datasets[tooltipItem.datasetIndex];
          return dataset.label + ": " + dataset.data[tooltipItem.index];
        }
      }
    };
    this.chartConfig = chartConfig;
  }

  private repaint(chartData) {
    if (this.chart) {
      this.chart.destroy();
    }
    let canvasEl = this.el.nativeElement.querySelector('canvas');
    let chartConfig = {
      plugins: [this.noDataPlugin, this.addAdditionalElementsPlugin, ...this.plugins]
    };

    this.chart = new ChartJs(canvasEl, jQuery.extend(true, chartConfig, chartData));
  }

  clear() { //todo SUPPORT-4909
    throw new UnsupportedOperationError("Unsupported operation");
  }
}
