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

<script>
import * as echarts from 'echarts';
import echartsResize, { px2echarts } from '@/mixins/echartsResize.js';

export default {
  name: 'TrafficTimeSpace',
  mixins: [echartsResize],
  props: {
    // 【终极修复】：使用 0, 50, 0, 0 神级协调相位差
    roadSegments: {
      type: Array,
      required: true,
      default: () => [
        { name: '交叉口A', distanceNext: 450, offset: 0 },
        { name: '交叉口B', distanceNext: 669, offset: 50 },
        { name: '交叉口C', distanceNext: 1050, offset: 0 },
        { name: '交叉口D', distanceNext: 0, offset: 0 }
      ]
    },
    speedKmh: { type: Number, default: 38.9 },     // 设计车速
    cycle: { type: Number, default: 100 },         // 红绿灯周期
    greenDuration: { type: Number, default: 40 },  // 绿灯时长
    bandwidth: { type: Number, default: 31.5 },    // 波带宽(秒)

    viewWindow: { type: Number, default: 420 },
    autoScroll: { type: Boolean, default: false },
    scrollSpeed: { type: Number, default: 0.2 },

    upWaveColor: { type: String, default: 'rgba(46, 204, 113, 0.45)' },
    downWaveColor: { type: String, default: 'rgba(52, 152, 219, 0.45)' },
    showYSplitLine: { type: Boolean, default: false },
    showPhaseOffset: { type: Boolean, default: true },
    showScanLine: { type: Boolean, default: true },
    scanLineColor: { type: String, default: 'rgba(64, 158, 255, 0.9)' },
    scanLineStart: { type: Number, default: 0 }
  },
  data() {
    return {
      $_chart: null,
      scrollTimer: null,
      scanLineTimer: null,
      currentViewMinX: 0,
      currentScanX: 0,
    };
  },
  computed: {
    barWidth() {
      const scale = typeof window !== 'undefined' ? Math.max(1, window.innerWidth / 1920) : 1;
      return Math.round(8 * scale);
    },
    gap() {
      const scale = typeof window !== 'undefined' ? Math.max(1, window.innerWidth / 1920) : 1;
      return Math.round(2 * scale);
    },
    intersections() {
      let currentX = 0;
      return this.roadSegments.map((seg, i) => {
        const intersection = {
          id: `I${i}`,
          name: seg.name,
          distanceNext: seg.distanceNext,
          x: currentX,
          offsetText: `${seg.offset}秒`,
          offset: seg.offset
        };
        currentX += seg.distanceNext;
        return intersection;
      });
    },
    maxX() {
      const last = this.intersections[this.intersections.length - 1];
      return last.x + 80;
    },
    yAxisMin() {
      // 留出底部 padding，避免首个路口（distance=0）向下延伸的柱子被 clip 裁掉
      return -80;
    },
    maxDataTime() {
      return this.viewWindow * 3;
    }
  },
  mounted() {
    this.$nextTick(() => {
      this.initChart();
      if (this.autoScroll) this.startScroll();
      if (this.showScanLine) this.startScanLine();
    });
  },
  beforeDestroy() {
    this.stopScroll();
    this.stopScanLine();
    if (this.$_chart) this.$_chart.dispose();
  },
  watch: {
    roadSegments: { handler() { this.updateChart(); }, deep: true },
    speedKmh() { this.updateChart(); },
    autoScroll(val) { val ? this.startScroll() : this.stopScroll(); }
  },
  methods: {
    // 根据大屏宽度缩放字号/像素常量，避免大屏下文字过小
    fs(px) {
      const scaled = typeof px2echarts === 'function' ? px2echarts(px) : px;
      return Math.max(px, scaled);
    },

    initChart() {
      if (!this.$refs.chartRef) return;
      this.$_chart = echarts.init(this.$refs.chartRef);
      this.updateChart();
    },

    generateBarData() {
      const data = [];
      const colors = { red: '#e74c3c', green: '#2ecc71', blue: '#3498db' };

      this.intersections.forEach(intersection => {
        for (let k = -2; k <= Math.ceil(this.maxDataTime / this.cycle) + 2; k++) {
          const pStart = intersection.offset + k * this.cycle;
          const pEnd = pStart + this.greenDuration;
          const rStart = pEnd;
          const rEnd = pStart + this.cycle;

          data.push([intersection.x, pStart, pEnd, colors.green, -1]);
          data.push([intersection.x, pStart, pEnd, colors.blue, 1]);
          data.push([intersection.x, rStart, rEnd, colors.red, -1]);
          data.push([intersection.x, rStart, rEnd, colors.red, 1]);
        }
      });
      return data;
    },

    getWaveData() {
      const speedMs = this.speedKmh / 3.6; // 设计车速换算为 m/s
      const startX = this.intersections[0].x;
      const endX = this.intersections[this.intersections.length - 1].x;
      const travelTime = (endX - startX) / speedMs;

      // 正向绿波 (A -> D)：gStartY 秒起步
      const gStartY = 0;
      const greenBottomLine = [
        [startX, gStartY],
        [endX, gStartY + travelTime]
      ];
      const greenTopLine = [
        [startX, gStartY + this.bandwidth],
        [endX, gStartY + travelTime + this.bandwidth]
      ];
      const greenCoords = [...greenBottomLine, ...[...greenTopLine].reverse()];

      // 反向蓝波 (D -> A)：bStartYAtD 秒起步
      const bStartYAtD = 100;
      const blueBottomLine = [
        [endX, bStartYAtD],
        [startX, bStartYAtD + travelTime]
      ];
      const blueTopLine = [
        [endX, bStartYAtD + this.bandwidth],
        [startX, bStartYAtD + travelTime + this.bandwidth]
      ];
      const blueCoords = [...blueBottomLine, ...[...blueTopLine].reverse()];

      return [
        {
          coords: greenCoords,
          bottomLine: greenBottomLine,
          topLine: greenTopLine,
          color: this.upWaveColor,
          lineCol: '#2ecc71',
          isBlue: false
        },
        {
          coords: blueCoords,
          bottomLine: blueBottomLine,
          topLine: blueTopLine,
          color: this.downWaveColor,
          lineCol: '#3498db',
          isBlue: true
        }
      ];
    },

    updateChart() {
      if (!this.$_chart) return;

      // x 轴刻度仅对齐首路口（A）的红块首尾
      const gcd = (a, b) => (b === 0 ? Math.abs(a) : gcd(b, a % b));
      const firstOffset = this.intersections[0] ? this.intersections[0].offset : 0;
      const redStartMod = ((firstOffset + this.greenDuration) % this.cycle + this.cycle) % this.cycle; // 红块起点
      const redEndMod = ((firstOffset) % this.cycle + this.cycle) % this.cycle;                         // 红块终点 = 下周期起点
      // interval 需同时整除 redStartMod 与 redEndMod 的间距，保证 label 落点
      const tickInterval = gcd(gcd(this.greenDuration, this.cycle - this.greenDuration), firstOffset || this.cycle);

      const option = {
        backgroundColor: 'transparent',
        animation: false,
        tooltip: { show: false },
        grid: { left: this.fs(140), right: this.fs(40), top: this.fs(40), bottom: this.fs(50) },
        xAxis: {
          type: 'value', min: this.currentViewMinX, max: this.currentViewMinX + this.viewWindow,
          interval: tickInterval, name: '时间 (秒)',
          nameLocation: 'end',
          nameTextStyle: { color: '#a0aabf', padding: [0, 0, 0, 0], fontSize: this.fs(12) },
          axisLine: { show: true, onZero: false, lineStyle: { color: 'rgba(255,255,255,0.5)' } },
          axisTick: {
            show: true,
            lineStyle: { color: 'rgba(255,255,255,0.5)' },
            length: this.fs(5)
          },
          axisLabel: {
            color: '#a0aabf',
            fontSize: this.fs(10),
            formatter: (value) => {
              const mod = ((value % this.cycle) + this.cycle) % this.cycle;
              return (mod === redStartMod || mod === redEndMod) ? value : '';
            }
          },
          splitLine: { show: this.showYSplitLine, lineStyle: { type: 'dashed', color: 'rgba(255, 255, 255, 0.08)' } }
        },
        yAxis: {
          type: 'value', min: this.yAxisMin, max: this.maxX,
          axisLine: { show: true, onZero: false, lineStyle: { color: 'rgba(255,255,255,0.15)' } },
          axisTick: { show: false },
          axisLabel: { show: false },
          splitLine: { show: false }
        },
        series: [
          {
            id: 'waveSeries',
            type: 'custom',
            clip: true,
            data: this.getWaveData(),
            renderItem: (params, api) => {
              const item = this.getWaveData()[params.dataIndex];
              const pixelOffsetY = item.isBlue ? (this.barWidth * 1.5 + this.gap) : (this.barWidth / 2);

              // coords 原始格式 [距离, 时间]，互换轴后映射为 api.coord([时间, 距离])
              const mapPt = c => {
                const pt = api.coord([c[1], c[0]]);
                return [pt[0], pt[1] + pixelOffsetY];
              };
              const mappedCoords = item.coords.map(mapPt);
              const mappedBottom = item.bottomLine.map(mapPt);
              const mappedTop = item.topLine.map(mapPt);

              const segIdx = 0;
              const pB1 = mappedBottom[segIdx];
              const pB2 = mappedBottom[segIdx + 1];
              const pT1 = mappedTop[segIdx];
              const pT2 = mappedTop[segIdx + 1];

              // 波带角度统一用底线方向（atan2），保证文字沿着波带方向读
              const angle = Math.atan2(pB2[1] - pB1[1], pB2[0] - pB1[0]);
              // 绿波速度文字沿波带方向顺时针 90°；蓝波沿波带方向逆时针 90°
              const speedAngle = item.isBlue ? angle - Math.PI / 2 : angle + Math.PI / 2;

              const percent = 0.15;
              const midBottomX = pB1[0] + (pB2[0] - pB1[0]) * percent;
              const midBottomY = pB1[1] + (pB2[1] - pB1[1]) * percent;
              const midTopX = pT1[0] + (pT2[0] - pT1[0]) * percent;
              const midTopY = pT1[1] + (pT2[1] - pT1[1]) * percent;

              // 速度文字沿波带法线向外偏移；与虚线保持同侧（绿波→顶线侧，蓝波→底线侧）
              const sBDx = pB2[0] - pB1[0];
              const sBDy = pB2[1] - pB1[1];
              const sBLen = Math.sqrt(sBDx * sBDx + sBDy * sBDy) || 1;
              const sPerpX = sBDy / sBLen;
              const sPerpY = -sBDx / sBLen;
              const sDot = (pT1[0] - pB1[0]) * sPerpX + (pT1[1] - pB1[1]) * sPerpY;
              const speedOff = this.fs(14);
              let speedX, speedY;
              if (item.isBlue) {
                // 蓝波：底线外侧
                const sSign = sDot > 0 ? -1 : 1;
                speedX = midBottomX + sSign * sPerpX * speedOff;
                speedY = midBottomY + sSign * sPerpY * speedOff;
              } else {
                // 绿波：顶线外侧（虚线这边）
                const sSign = sDot > 0 ? 1 : -1;
                speedX = midTopX + sSign * sPerpX * speedOff;
                speedY = midTopY + sSign * sPerpY * speedOff;
              }

              return {
                type: 'group',
                children: [
                  { type: 'polygon', shape: { points: mappedCoords }, style: { fill: item.color } },
                  ...(() => {
                    // 虚线沿垂直于波带方向向外偏移：绿波走顶线外侧，蓝波走底线外侧（互相镜像）
                    const lineGap = this.fs(4);
                    const bDx = mappedBottom[1][0] - mappedBottom[0][0];
                    const bDy = mappedBottom[1][1] - mappedBottom[0][1];
                    const bLen = Math.sqrt(bDx * bDx + bDy * bDy) || 1;
                    const perpX = bDy / bLen;
                    const perpY = -bDx / bLen;
                    const midBx = (mappedBottom[0][0] + mappedBottom[1][0]) / 2;
                    const midBy = (mappedBottom[0][1] + mappedBottom[1][1]) / 2;
                    const midTx = (mappedTop[0][0] + mappedTop[1][0]) / 2;
                    const midTy = (mappedTop[0][1] + mappedTop[1][1]) / 2;
                    const dot = (midTx - midBx) * perpX + (midTy - midBy) * perpY;

                    let points;
                    if (item.isBlue) {
                      // 蓝波：底线外侧（远离顶线）
                      const sign = dot > 0 ? -1 : 1;
                      points = mappedBottom.map(p => [p[0] + sign * perpX * lineGap, p[1] + sign * perpY * lineGap]);
                    } else {
                      // 绿波：顶线外侧（远离底线）
                      const sign = dot > 0 ? 1 : -1;
                      points = mappedTop.map(p => [p[0] + sign * perpX * lineGap, p[1] + sign * perpY * lineGap]);
                    }
                    return [
                      { type: 'polyline', shape: { points }, style: { stroke: item.lineCol, lineDash: [4, 4], lineWidth: 1 } }
                    ];
                  })(),
                  { type: 'text', x: speedX, y: speedY, rotation: speedAngle, style: { text: `${this.speedKmh}km/h`, fill: '#fff', fontSize: this.fs(11), textAlign: 'center' } },
                  ...(() => {
                    const cxBw = (midBottomX + midTopX) / 2;
                    const cyBw = (midBottomY + midTopY) / 2;
                    const dxBw = midTopX - midBottomX;
                    const dyBw = midTopY - midBottomY;
                    const lenBw = Math.sqrt(dxBw * dxBw + dyBw * dyBw) || 1;
                    const uxBw = dxBw / lenBw;
                    const uyBw = dyBw / lenBw;
                    const gapBw = this.fs(10);
                    return [
                      { type: 'line', shape: { x1: midBottomX, y1: midBottomY, x2: cxBw - uxBw * gapBw, y2: cyBw - uyBw * gapBw }, style: { stroke: 'rgba(255, 255, 255, 0.4)' } },
                      { type: 'text', x: cxBw, y: cyBw, rotation: angle, style: { text: `${this.bandwidth}s`, fill: '#fff', fontSize: this.fs(11), textAlign: 'center', textVerticalAlign: 'middle' } },
                      { type: 'line', shape: { x1: cxBw + uxBw * gapBw, y1: cyBw + uyBw * gapBw, x2: midTopX, y2: midTopY }, style: { stroke: 'rgba(255, 255, 255, 0.4)' } }
                    ];
                  })()
                ]
              };
            }
          },
          {
            id: 'lightSeries',
            type: 'custom',
            clip: true,
            data: this.generateBarData(),
            renderItem: (params, api) => {
              const distance = api.value(0);
              const tStart = api.value(1);
              const tEnd = api.value(2);
              const color = api.value(3);
              const direction = api.value(4);

              const startP = api.coord([tStart, distance]);
              const endP = api.coord([tEnd, distance]);
              const rectY = direction === -1 ? startP[1] : (startP[1] + this.barWidth + this.gap);

              return {
                type: 'rect',
                shape: { x: startP[0], y: rectY, width: endP[0] - startP[0], height: this.barWidth },
                style: { fill: color }
              };
            }
          },
          {
            id: 'axisLabels',
            type: 'custom',
            clip: false,
            data: this.intersections,
            renderItem: (params, api) => {
              const intersection = this.intersections[params.dataIndex];
              const point = api.coord([this.currentViewMinX, intersection.x]);
              const centerYPx = point[1] + this.barWidth + this.gap / 2;
              const labelX = point[0] - this.fs(12);
              const nameFs = this.fs(12);
              const subFs = this.fs(11);

              // 名称较长时按 "-" 拆成两行显示
              const dashIdx = intersection.name.indexOf('-');
              const nameLine1 = dashIdx >= 0 ? intersection.name.slice(0, dashIdx) : intersection.name;
              const nameLine2 = dashIdx >= 0 ? intersection.name.slice(dashIdx + 1) : '';

              const children = [];
              if (nameLine2) {
                children.push({ type: 'text', x: labelX, y: centerYPx - this.fs(15), style: { text: nameLine1, fill: '#fff', textAlign: 'right', fontSize: nameFs } });
                children.push({ type: 'text', x: labelX, y: centerYPx - this.fs(1), style: { text: nameLine2, fill: '#fff', textAlign: 'right', fontSize: nameFs } });
              } else {
                children.push({ type: 'text', x: labelX, y: centerYPx - this.fs(7), style: { text: nameLine1, fill: '#fff', textAlign: 'right', fontSize: nameFs } });
              }
              if (this.showPhaseOffset) {
                const offYPx = nameLine2 ? centerYPx + this.fs(14) : centerYPx + this.fs(5);
                children.push({ type: 'text', x: labelX, y: offYPx, style: { text: `相位差: ${intersection.offsetText}`, fill: '#a0aabf', textAlign: 'right', fontSize: subFs } });
              }

              if (intersection.distanceNext) {
                const nextP = api.coord([this.currentViewMinX, intersection.x + intersection.distanceNext]);
                const nextCenterYPx = nextP[1] + this.barWidth + this.gap / 2;
                const midYPx = (centerYPx + nextCenterYPx) / 2;

                // 非反转 yAxis：距离越大像素 y 越小，nextCenterYPx < centerYPx
                const lineX = labelX - this.fs(10);
                children.push({ type: 'line', shape: { x1: lineX, y1: centerYPx - this.fs(36), x2: lineX, y2: nextCenterYPx + this.fs(22) }, style: { stroke: 'rgba(255,255,255,0.1)', lineDash: [2, 2] } });
                children.push({ type: 'text', x: lineX, y: midYPx, style: { text: `${intersection.distanceNext}m`, fill: '#a0aabf', textAlign: 'center', fontSize: subFs } });
              }
              return { type: 'group', children };
            }
          },
          ...(this.showScanLine ? [{
            id: 'scanLineSeries',
            type: 'custom',
            clip: true,
            data: [[this.currentScanX]],
            renderItem: (params, api) => {
              const xTime = api.value(0);
              const top = api.coord([xTime, this.yAxisMin]);
              const bottom = api.coord([xTime, this.maxX]);
              return {
                type: 'line',
                shape: { x1: top[0], y1: top[1], x2: bottom[0], y2: bottom[1] },
                style: { stroke: this.scanLineColor, lineWidth: this.fs(4) }
              };
            },
            z: 10
          }] : [])
        ]
      };
      this.$_chart.setOption(option, { replaceMerge: ['series'] });
    },

    startScanLine() {
      this.stopScanLine();
      this.currentScanX = this.viewWindow * Math.min(1, Math.max(0, this.scanLineStart));
      const step = this.viewWindow / 100;
      this.scanLineTimer = setInterval(() => {
        this.currentScanX += step;
        if (this.currentScanX > this.currentViewMinX + this.viewWindow) {
          this.currentScanX = this.currentViewMinX;
        }
        if (this.$_chart) {
          this.$_chart.setOption({
            series: [{ id: 'scanLineSeries', data: [[this.currentScanX]] }]
          });
        }
      }, 1000);
    },

    stopScanLine() {
      if (this.scanLineTimer) {
        clearInterval(this.scanLineTimer);
        this.scanLineTimer = null;
      }
    },

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

    stopScroll() {
      if (this.scrollTimer) {
        clearInterval(this.scrollTimer);
        this.scrollTimer = null;
      }
    }
  }
};
</script>

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