<template>
  <div ref="chartContainer" class="traffic-timespace-chart"></div>
</template>

<script>
import * as echarts from 'echarts';

export default {
  name: 'TrafficTimeSpace',
  props: {
    // 路口名称数组（从起点到终点）
    intersections: {
      type: Array,
      required: true
    },
    // 各路口到起点的距离（米），与 intersections 一一对应
    distances: {
      type: Array,
      required: true
    },
    // 绿波带数据: [{ yBottom, yTop, xBL, xBR, xTL, xTR, label, direction }]
    // direction: 'up' | 'down'
    waveData: {
      type: Array,
      default: () => []
    },
    // 绿灯数据: [{ y, start, end }]
    greenData: {
      type: Array,
      default: () => []
    },
    // 可视窗口显示的时间跨度（秒）
    viewWindow: {
      type: Number,
      default: 350
    },
    // 是否启用自动滚动
    autoScroll: {
      type: Boolean,
      default: false
    },
    // 自动滚动速度（秒/帧）
    scrollSpeed: {
      type: Number,
      default: 0.5
    },
    // 上行波带颜色
    upWaveColor: {
      type: String,
      default: 'rgba(46, 196, 182, 0.45)'
    },
    // 下行波带颜色
    downWaveColor: {
      type: String,
      default: 'rgba(104, 231, 95, 0.4)'
    },
    // 波带文字颜色
    waveLabelColor: {
      type: String,
      default: '#e0f7fa'
    }
  },
  data() {
    return {
      chart: null,
      scrollTimer: null,
      currentViewTime: 0
    };
  },
  computed: {
    maxDistance() {
      return Math.max(...this.distances);
    },
    maxDataTime() {
      let max = 0;
      this.waveData.forEach(w => {
        max = Math.max(max, w.xBR, w.xTR);
      });
      this.greenData.forEach(g => {
        max = Math.max(max, g.end);
      });
      return max;
    },
    // 转为 ECharts custom series 所需的数组格式
    echartsWaveData() {
      return this.waveData.map(w => [
        w.yBottom, w.yTop, w.xBL, w.xBR, w.xTL, w.xTR, w.label || '', w.direction || 'up'
      ]);
    },
    echartsGreenData() {
      return this.greenData.map(g => [g.y, g.start, g.end]);
    },
    echartsRedData() {
      return this.distances.map(y => [y]);
    },
    // 路口名称需要反转以匹配 Y 轴方向
    reversedIntersections() {
      return [...this.intersections].reverse();
    }
  },
  mounted() {
    this.$nextTick(() => {
      this.initChart();
      if (this.autoScroll) this.startScroll();
    });
  },
  beforeDestroy() {
    this.stopScroll();
    if (this._resizeObserver) {
      this._resizeObserver.disconnect();
      this._resizeObserver = null;
    }
    if (this.chart) {
      this.chart.dispose();
      this.chart = null;
    }
    window.removeEventListener('resize', this._resizeHandler);
  },
  watch: {
    waveData() { this.updateChart(); },
    greenData() { this.updateChart(); },
    autoScroll(val) {
      val ? this.startScroll() : this.stopScroll();
    }
  },
  methods: {
    initChart() {
      this.chart = echarts.init(this.$refs.chartContainer);
      this._resizeHandler = () => this.chart && this.chart.resize();
      window.addEventListener('resize', this._resizeHandler);

      // 监听容器尺寸变化（弹窗拉伸、延迟渲染等场景）
      if (typeof ResizeObserver !== 'undefined') {
        this._roaPending = false;
        this._resizeObserver = new ResizeObserver(() => {
          if (!this._roaPending) {
            this._roaPending = true;
            requestAnimationFrame(() => {
              this._roaPending = false;
              this.chart && this.chart.resize();
            });
          }
        });
        this._resizeObserver.observe(this.$refs.chartContainer);
      }

      this.updateChart();
    },

    updateChart() {
      if (!this.chart) return;

      const self = this;
      const distances = this.distances;
      const intersections = this.reversedIntersections;
      const maxDist = this.maxDistance;

      this.chart.setOption({
        backgroundColor: 'transparent',
        animation: false,
        tooltip: { show: false },
        grid: { left: 110, right: 30, top: 40, bottom: 40 },
        xAxis: {
          type: 'value',
          min: this.currentViewTime,
          max: this.currentViewTime + this.viewWindow,
          axisLabel: { color: '#7b95b9', formatter: '{value}s', fontSize: 13 },
          splitLine: { show: true, lineStyle: { color: '#1a305d', type: 'solid' } },
          axisLine: { lineStyle: { color: '#31548e' } }
        },
        yAxis: {
          type: 'value',
          min: 0,
          max: maxDist,
          interval: distances.length > 1 ? distances[1] - distances[0] : 300,
          axisLabel: {
            interval: 0,
            color: '#9cb1d4',
            fontWeight: 'bold',
            fontSize: 13,
            formatter: value => distances.includes(value) ? intersections[distances.indexOf(value)] : ''
          },
          splitLine: { show: true, lineStyle: { color: '#1a305d' } }
        },
        series: [
          {
            type: 'custom',
            renderItem: function (params, api) {
              return self.renderWave(params, api);
            },
            data: this.echartsWaveData,
            clip: true,
            z: 1
          },
          {
            type: 'custom',
            renderItem: function (params, api) {
              return self.renderRedBackground(params, api);
            },
            data: this.echartsRedData,
            clip: true,
            z: 2
          },
          {
            type: 'custom',
            renderItem: function (params, api) {
              return self.renderGreenLight(params, api);
            },
            data: this.echartsGreenData,
            clip: true,
            z: 3
          }
        ]
      });
    },

    renderRedBackground(params, api) {
      const y = api.value(0);
      const startX = api.coord([0, y])[0];
      const endX = api.coord([this.maxDataTime, y])[0];
      return {
        type: 'rect',
        shape: { x: startX, y: api.coord([0, y])[1] - 3, width: endX - startX, height: 6 },
        style: { fill: '#f02828' }
      };
    },

    renderGreenLight(params, api) {
      const y = api.value(0);
      const p1 = api.coord([api.value(1), y]);
      const p2 = api.coord([api.value(2), y]);
      return {
        type: 'rect',
        shape: { x: p1[0], y: p1[1] - 4, width: p2[0] - p1[0], height: 8 },
        style: api.style({ fill: '#68e75f' })
      };
    },

    renderWave(params, api) {
      const yBottom = api.value(0), yTop = api.value(1);
      const xBL = api.value(2), xBR = api.value(3), xTL = api.value(4), xTR = api.value(5);
      const text = api.value(6), dir = api.value(7);

      const ptBL = api.coord([xBL, yBottom]), ptBR = api.coord([xBR, yBottom]);
      const ptTL = api.coord([xTL, yTop]), ptTR = api.coord([xTR, yTop]);
      const angle = -Math.atan2(ptTL[1] - ptBL[1], ptTL[0] - ptBL[0]);
      const fillColor = dir === 'up' ? this.upWaveColor : this.downWaveColor;

      return {
        type: 'group',
        children: [
          {
            type: 'polygon',
            shape: { points: [ptBL, ptBR, ptTR, ptTL] },
            z2: 1,
            style: api.style({ fill: fillColor, stroke: 'none' })
          },
          {
            type: 'text',
            x: (ptBL[0] + ptTR[0]) / 2,
            y: (ptBL[1] + ptTR[1]) / 2,
            rotation: angle,
            z2: 5,
            style: {
              text: text,
              fill: this.waveLabelColor,
              font: 'bold 15px sans-serif',
              textAlign: 'center',
              textVerticalAlign: 'middle'
            }
          }
        ]
      };
    },

    startScroll() {
      this.stopScroll();
      this.scrollTimer = setInterval(() => {
        this.currentViewTime += this.scrollSpeed;
        if (this.currentViewTime > this.maxDataTime - this.viewWindow) {
          this.currentViewTime = 0;
        }
        if (this.chart) {
          this.chart.setOption({
            xAxis: { min: this.currentViewTime, max: this.currentViewTime + this.viewWindow }
          });
        }
      }, 16);
    },

    stopScroll() {
      if (this.scrollTimer) {
        clearInterval(this.scrollTimer);
        this.scrollTimer = null;
      }
    },

    resize() {
      if (this.chart) this.chart.resize();
    }
  }
};
</script>

<style scoped>
.traffic-timespace-chart {
  width: 100%;
  height: 100%;
}
</style>
