import {ChartPlugin} from "./ChartPlugin";
import {Chart} from "chart.js";
import {
  ChartLabelModel
} from "../../../../generated/ervu_dashboard/model/chart/round/label/ChartLabelModel";
import {ChartUtils} from "../ChartUtils";
import {AnalyticalScope, Behavior, ChartFontFamily, Visible} from "@webbpm/base-package";
import {ErvuChartV2} from "../ErvuChartV2";
import {ChartLabelFormatter} from "./formatters/ChartLabelFormatter";

@AnalyticalScope(ErvuChartV2)
export class DoughnutCenterLabelsPlugin extends Behavior implements ChartPlugin {
  public static readonly ID = 'doughnut-center-labels';
  private static readonly DEFAULTS = {
    color: 'rgb(255, 255, 255)',
    font: {
      family: ChartFontFamily.SANS_SERIF,
      weight: '500',
      size: 14
    }
  }

  @Visible('false')
  public id: string = DoughnutCenterLabelsPlugin.ID;

  public formatters: ChartLabelFormatter[] = [];

  beforeDatasetsDraw(chart: Chart, args?: { cancellable: true }, options?: any): void {
    if (!chart.data || !chart.data.datasets || !(<any>chart.data).centerLabels || ChartUtils.isEmpty(chart)) {
      return;
    }

    let ctx = chart.ctx;
    let {top, bottom, left, right} = chart.chartArea;

    let centerLabels = ChartUtils.clone((<any>chart.data).centerLabels as ChartLabelModel[]);
    this.processLabels(centerLabels);

    let textAreaSize = this.getTextSize(chart, centerLabels);

    const diagonal = Math.sqrt(
        Math.pow(textAreaSize.width, 2) + Math.pow(textAreaSize.height, 2)
    );
    const innerDiameter = this.getInnerRadius(chart) * 2;
    const fitRatio = innerDiameter / diagonal;

    if (fitRatio < 1) {
      centerLabels.forEach(label => {
        label.font.size = Math.floor(label.font.size * fitRatio);
      });

      textAreaSize = this.getTextSize(chart, centerLabels);
    }

    ctx.textAlign = "center";
    ctx.textBaseline = "middle";

    const centerX = (left + right) / 2;
    const centerY = (top + bottom) / 2;

    const topY = centerY - textAreaSize.height / 2;

    let currentHeight = 0;
    centerLabels.forEach(label => {
      let textMetrics = ChartUtils.getTextMetrics(ctx, label.text, label.font);

      ctx.fillStyle = label.color
      ctx.font = ChartUtils.getFont(label.font);

      const lineCenterY = topY + currentHeight + textMetrics.height / 2;
      currentHeight += textMetrics.height;

      ctx.fillText(label.text, centerX, lineCenterY);
    })
  }

  protected getTextSize(chart: Chart, centerLabels: ChartLabelModel[]) {
    const {ctx} = chart;
    const prev = ctx.font;
    let width = 0;
    let height = 0;

    centerLabels.forEach(label => {
      let metrics = ChartUtils.getTextMetrics(ctx, label.text, label.font);
      width = Math.max(metrics.width, width);
      height += metrics.height;
    });

    ctx.font = prev;

    return {
      height: height,
      width: width
    };
  }

  protected processLabels(labels: ChartLabelModel[]) {
    labels.forEach((label, index) => {
      let formatter = this.formatters[index];
      if (formatter) {
        formatter.format(label, labels, index);
      }

      label.color = label.color ? label.color : DoughnutCenterLabelsPlugin.DEFAULTS.color;

      if (label.font) {
        label.font.family =
            label.font.family ? label.font.family : DoughnutCenterLabelsPlugin.DEFAULTS.font.family;
        label.font.weight =
            label.font.weight ? label.font.weight : DoughnutCenterLabelsPlugin.DEFAULTS.font.weight;
        label.font.size =
            label.font.size ? label.font.size : DoughnutCenterLabelsPlugin.DEFAULTS.font.size;
      }
      else {
        label.font = DoughnutCenterLabelsPlugin.DEFAULTS.font
      }
    });
  }

  protected getInnerRadius(chart: Chart): number {
    return chart.getSortedVisibleDatasetMetas()
        .map(meta => meta.data[0])
        .filter(meta => meta)
        .map((arc: any) => arc.innerRadius as number)
        .reduce((prev, curr) => Math.min(prev, curr), chart.width / 2);
  }
}