<script setup lang="ts">
import * as echarts from 'echarts/core';
import { LineChart, LineSeriesOption } from 'echarts/charts';
import {
  TitleComponent,
  // 组件类型的定义后缀都为 ComponentOption
  TitleComponentOption,
  TooltipComponent,
  TooltipComponentOption,
  GridComponent,
  GridComponentOption,
  LegendComponent,
  // 数据集组件
  DatasetComponent,
  DatasetComponentOption,
  // 内置数据转换器组件 (filter, sort)
  TransformComponent,
} from 'echarts/components';
import { LabelLayout, UniversalTransition } from 'echarts/features';
import { CanvasRenderer } from 'echarts/renderers';
import { onMounted, PropType, watch, ref, computed } from 'vue';
import { INetworkChartData } from '@/model/System/monitor-interface';
import { htmlEncode, initLeftDistance } from '@/pages/System/Monitor/monitorFunctions';
import '@/assets/common/style/echarts-formatter-style/index.scss';
import { visualMapLineColor, colors } from '@/model/colors';
import mixins from '@/utils/watchSwitch';
import { compareObjectEqual, getRealColor, getThemeMode } from '@/utils/utils';
import useStore from '@/stores';
import { showCircles } from '@/model/echartsCircles';

// 通过 ComposeOption 来组合出一个只有必须组件和图表的 Option 类型
type ECOption = echarts.ComposeOption<
| LineSeriesOption
| TitleComponentOption
| TooltipComponentOption
| GridComponentOption
| DatasetComponentOption
>;

// 注册必须的组件
echarts.use([
  TitleComponent,
  TooltipComponent,
  GridComponent,
  DatasetComponent,
  TransformComponent,
  LineChart,
  LabelLayout,
  UniversalTransition,
  CanvasRenderer,
  LegendComponent,
]);

let { componentIsShowing } = mixins();

const chartDom = ref();
let myChart: any = null;
const reg = /(?<=t).*?(?=\()/;
const props = defineProps({
  title: {
    type: String,
    default: '',
    required: true,
  },
  showData: {
    type: Object as PropType<INetworkChartData>,
    default: () => {
      return {};
    },
    required: true,
  },
  thresholdPercent: {
    type: Number,
    default: 5,
    required: true,
  },
});

const watchArr = [() => props.title, () => props.showData, () => props.thresholdPercent];

watch(
  watchArr,
  () => {
    if (componentIsShowing.value) {
      networkCharts();
    }
  },
  { deep: true },
);

let thresholdPercent = 0;
const store = useStore();
const systemTheme = computed(() => {
  return store.state.loct.systemTheme;
});

watch(systemTheme, () => {
  if (componentIsShowing.value) {
    networkCharts();
  }
});

function initThresholdPercent() {
  let str = props.thresholdPercent;
  if (typeof str === 'string') {
    thresholdPercent = parseInt(str);
  } else {
    thresholdPercent = props.thresholdPercent;
  }
}

// 判读所有的展示数据是否都等于门限值
function showMarkLine(yData: any[]) {
  let num = 0;
  let total = 0;
  for (let i = 0; i < yData.length; i++) {
    const list = yData[i];
    total += list.length;
    for (let j = 0; j < list.length; j++) {
      if (parseInt(list[j]) === thresholdPercent) {
        num++;
      } else {
        break;
      }
    }
  }
  if (num === total) {
    return false;
  }
  return true;
}

function getMarkLine(yData: any, showLineNum: number) {
  const flag = showMarkLine(yData);
  return {
    silent: true,
    symbol: 'none',
    lineStyle: {
      width: 1,
      color: flag || showLineNum === 0 ? visualMapLineColor : 'rgba(0, 0, 0, 0)',
    },
    label: {
      show: true,
      position: 'start',
      distance: [initLeftDistance((String(thresholdPercent)).length), 0],
      color: visualMapLineColor,
      fontSize: 16,
      fontWeight: '500',
    },
    data: [
      {
        type: 'now',
        yAxis: thresholdPercent,
        symbol: 'none',
      },
    ],
  };
}

// 获取区域面积渐变,只有一条线的时候渐变透明度为0.2
function getAreaStyle(params: any) {
  let { yData, itemLineData, index, showLineNum } = params;
  const haveSameLine = showLineNum === 1 ? true : haveSameData(yData, itemLineData, index);

  const max = Math.max.call(null, ...itemLineData);
  if (max < thresholdPercent) {
    return {
      opacity: haveSameLine ? 0.15 : 0,
      color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
        {
          offset: 0,
          color: colors.lineColors[index],
        },
        {
          offset: 1,
          color: '#fff',
        },
      ]),
    };
  } else {
    // 当前y轴没有设置最大值，所以可以用数据的最大值代表y轴的最大值,如果y轴设置了最大值，那就需要用y轴最大值,注意0不能作为除数
    return {
      opacity: haveSameLine ? 0.15 : 0,
      color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
        {
          offset: 0,
          color: colors.heleathAndBeyondColors.beyondColor,
        },
        {
          offset: max === 0 ? 1 : 1 - (thresholdPercent / max),
          color: colors.heleathAndBeyondColors.beyondColor,
        },
        {
          offset: max === 0 ? 1 : 1 - (thresholdPercent / max),
          color: colors.lineColors[index],
        },
        {
          offset: 1,
          color: '#fff',
        },
      ]),
    };
  }
}

function getLineStyle() {
  return {
    width: 2,
  };
}

/**
 * 判断当前展示线的数据是否和其他线条有一致的,且当前线其他相同的相比在数据中排序是最后一个,
 * 用于当数据相同时，只展示最后一个的areaStyle
 */
function haveSameData(yData: any, lineItemData: number[], index: number) {
  let num = -1;
  yData.forEach((item: number[], i: number) => {
    if (lineItemData !== item && compareObjectEqual(lineItemData, item)) {
      num = i;
    }
  });
  return num < index;
}

function getSeriesDataOfZero(config: {
  i: number;
  showLineNum: number;
  flag: boolean;
  item: any;
  yData: any;
  ports: any;
}) {
  const { i, showLineNum, flag, item, yData, ports } = config;
  return {
    name: ports[i] ?? 'Port' + (i + 1),
    type: 'line',
    smooth: true,
    showSymbol: false,
    symbolSize: 12,
    symbol: (value: any) => {
      if (thresholdPercent < value || !flag) {
        return 'image://' + showCircles.redCircle;
      }
      return 'image://' + showCircles.normalCircles[i];
    },
    data: item,
    markLine: {},
    lineStyle: getLineStyle(),
    areaStyle: getAreaStyle({
      yData,
      itemLineData: item,
      index: i,
      showLineNum,
    }),
  };
}

function getSeriesData(yData: any, showLineNum: number, ports: any) {
  const flag = showMarkLine(yData);
  return yData.map((item: any, i: number) => {
    if (i === 0) {
      return {
        name: ports[i] ?? 'Port' + (i + 1),
        type: 'line',
        smooth: true,
        showSymbol: false,
        symbolSize: 12,
        symbol: (value: any) => {
          if (thresholdPercent < value || !flag) {
            return 'image://' + showCircles.redCircle;
          }
          return 'image://' + showCircles.normalCircles[i];
        },
        data: item,
        markLine: getMarkLine(yData, showLineNum),
        lineStyle: getLineStyle(),
        areaStyle: getAreaStyle({
          yData,
          itemLineData: item,
          index: i,
          showLineNum,
        }),
      };
    } else {
      // 控制只绘制一根markLine
      return getSeriesDataOfZero({
        i,
        showLineNum,
        flag,
        item,
        yData,
        ports,
      });
    }
  });
}

// 只展示一条数据的线时
function getVisualMapArrOneLine(flag: boolean, index: number, color: string) {
  // 有展示markline的时候
  if (flag) {
    return {
      show: false,
      seriesIndex: index,
      pieces: [
        {
          gte: 0,
          lt: thresholdPercent,
          color: color,
        },
        {
          gte: thresholdPercent,
          lte: 100,
          color: visualMapLineColor,
        },
      ],
    };
  } else {
    // 没有展示markline的时候，只展示一条红色线
    return {
      show: false,
      seriesIndex: index,
      pieces: [
        {
          gte: 0,
          lt: 100,
          color: visualMapLineColor,
        },
      ],
    };
  }
}

// 展示多条数据的线时,有相同的线，只展示相同数据线的最后一条
function getVisualMapArrMoreLine(flag: boolean, index: number, color: string, yData: any) {
  const haveSameLine = haveSameData(yData, yData[index], index);
  // 有展示markline的时候
  if (flag) {
    return {
      show: false,
      seriesIndex: index,
      pieces: [
        {
          gte: 0,
          lt: thresholdPercent,
          color: color,
        },
        {
          gte: thresholdPercent,
          lte: 100,
          color: haveSameLine ? visualMapLineColor : 'rgba(0, 0, 0, 0)',
        },
      ],
    };
  } else {
    // 没有展示markline的时候，只展示一条红色线
    return {
      show: false,
      seriesIndex: index,
      pieces: [
        {
          gte: 0,
          lt: 100,
          color: haveSameLine ? visualMapLineColor : 'rgba(0, 0, 0, 0)',
        },
      ],
    };
  }
}

function getVisualMapArr(showLineNum: number, yData: any) {
  const flag = showMarkLine(yData);
  const res = yData.map((item: number[], index: number) => {
    if (showLineNum === 1) {
      return getVisualMapArrOneLine(flag, index, colors.lineColors[index]);
    } else {
      // 多条线的时候如果数据都是一样的，且门限值的数据也是这个值，那么只展示一次visualMap
      return getVisualMapArrMoreLine(flag, index, colors.lineColors[index], yData);
    }
  });
  return res;
}

function getItemStyle(item: any, showLineNum: number, num: number) {
  return `${
    item.data >= thresholdPercent && showLineNum === 1 ? visualMapLineColor : colors.lineColors[num]
  }`;
}

function getToolTip(showLineNum: number, showLineState: boolean[]): any {
  const tipBgColor = getThemeMode() === 'light' ? '#FFF' : '#2A2F37';
  return {
    trigger: 'axis',
    backgroundColor: tipBgColor,
    borderColor: tipBgColor,
    extraCssText: 'padding:8px 16px; border-radius:1px;',
    axisPointer: {
      z: 1,
      lineStyle: {
        type: 'solid',
        width: 2,
        color: getRealColor('--o-border-color-base'),
      },
    },
    formatter(param: any) {
      let res = '';
      for (let i = 0; i < param.length; i++) {
        const item = param[i];

        /**
         * 可能存在某些条线不展示，那这个时候得到的lineColors[i]不是真正应该展示颜色
         * 因为item.seriesName是Port + (index + 1)组成的，所以可以通过这个字段来获取真正的序号
         */
        let sp = '<span class="echarts-formatter-block"></span>';
        let num = Number(item.seriesName.match(reg)[0]) - 1;
        let index = getItemStyle(item,showLineNum, num);
        sp = `<span class="echarts-formatter-block color-${index.replace('#', '')}"></span>`;
        res += `<div class="echarts-formatter-other-row-value-box">
                  ${sp}
                  <span class="echarts-formatter-key">${htmlEncode(item.seriesName)}</span>
                  <span class="echarts-formatter-value">${htmlEncode(item.data)}</span>
                </div>`;
      }
      return `<span class="echarts-formatter-first-row">${htmlEncode(
        param[0].axisValue,
      )}</span><br>${res}`;
    },
  };
}

function getXaxis(xData: any): any {
  return {
    show: true,
    type: 'category',
    boundaryGap: false,
    data: xData,
    bottom: 32,
    axisLine: {
      show: true,
      lineStyle: {
        width: 1,
        type: 'solid',
        color: getRealColor('--o-border-color-light'),
      },
    },
    axisTick: {
      show: false,
    },
    axisLabel: {
      margin: 10,
      color: getRealColor('--o-text-color-tertiary'),
      fontSize: 12,
      formatter(v: any) {
        if (v === undefined) {
          return false;
        }
        return v.slice(5).replace(' ', '\n');
      },
    },
    splitLine: {
      show: false,
    },
  };
}

function getYaxis(): any {
  return {
    type: 'value',
    axisLine: {
      show: false,
    },
    splitLine: {
      lineStyle: {
        type: 'dashed',
        color: getRealColor('--o-border-color-base'),
      },
    },
    offset: 2,
    axisTick: {
      show: false,
    },
    axisLabel: {
      margin: 8,
      textStyle: {
        align: 'right',
        verticalAlign: 'middle',
        color: getRealColor('--o-text-color-tertiary'),
        fontSize: 12,
      },
      // 刚好是门限值的刻度不显示,这样门限值的数值就不会与刻度值重叠
      formatter: (value: any, index: any) => {
        if (thresholdPercent === value) {
          return '';
        }
        return value;
      },
    },
    splitNumber: 4,
  };
}

function getTitle(): any {
  return {
    text: props.title,
    textStyle: {
      color: getRealColor('--o-text-color-secondary'),
      fontWeight: 'normal',
      fontSize: '12px',
      fontFamily: 'HarmonyOS Sans SC Medium',
    },
    left: '-5px',
    top: '-4px',
  };
}

// 获取要展示线条的数量
function getShowLineNum(list: boolean[]) {
  return list.filter((item: boolean) => {
    return item;
  }).length;
}

function networkCharts(): void {
  initThresholdPercent();
  const { UtilisePercents, showLineState, PortCount, CurrentUtilisePercents } = props.showData;
  const showLineNum = getShowLineNum(showLineState);
  let xData: string[] = [];
  let yData: Array<number[]> = [];
  const ports = [] as any[];
  if (CurrentUtilisePercents) {
    const currentUtilisePercents = CurrentUtilisePercents;
    for (let i = 1; i <= currentUtilisePercents.length; i++) {
      const current = currentUtilisePercents[i - 1];
      if (current !== null) {
        ports.push(`Port${i} ( ${parseInt(current, 10)}% )`);
      } else {
        ports.push(`Port${i} ( - - )`);
      }
    }
  } else {
    for (let i = 1; i <= PortCount; i++) {
      ports.push(`Port${i} ( - - )`);
    }
  }
  for (let i = 0; i < UtilisePercents.length; i++) {
    xData.push(UtilisePercents[i].Time);
    const { UtilisePercent } = UtilisePercents[i];
    UtilisePercent.forEach((item: number, index: number) => {
      if (!yData[index]) {
        yData[index] = [];
      }
      if (showLineState[index]) {
        yData[index].push(item);
      }
    });
  }
  const legendData = yData.map((item: any, i: number) => {
    return {
      name: ports[i],
      icon: 'circle',
    };
  });
  const seriesData = getSeriesData(yData, showLineNum, ports);
  const visualMapArr = getVisualMapArr(showLineNum, yData);
  const option: ECOption = {
    color: colors.lineColors,
    title: getTitle(),
    tooltip: getToolTip(showLineNum, showLineState),
    visualMap: visualMapArr,
    legend: {
      align: 'left',
      top: 256,
      itemGap: 32,
      itemWidth: 10,
      textStyle: {
        fontSize: 12,
        padding: [4, 0, 0, 0],
      },
      smooth: false,
      data: legendData,
    },
    grid: {
      borderWidth: 0,
      containLabel: true,
      left: showLineNum === 0 ? 37 : 12,
      top: props.title ? 36 : 12,
      right: 12,
      bottom: 0,
    },
    xAxis: getXaxis(xData),
    yAxis: getYaxis(),
    series: seriesData,
  };

  if (myChart) {
    myChart.clear();
    myChart.setOption(option, true);
  } else {
    myChart = echarts.init(chartDom.value);
    myChart.setOption(option, true);
  }
}

onMounted(() => {
  networkCharts();
});
</script>

<template>
  <div ref="chartDom" class="chart"></div>
</template>

<style scoped lang="scss">
.chart {
  height: 240px;
}
</style>
