<template>
  <div>
    <div ref="chart" class="waterfall-chart" id="waterfall-chart"></div>
    <div class="control-panel">
      <!-- 频率范围 -->
      <div class="box full-row">
        <div class="panel-block">
          <span class="label1">频率</span>
          <el-input v-model="freqMin" size="mini" placeholder="下限" />
          <span>~</span>
          <el-input v-model="freqMax" size="mini" placeholder="上限" />
          <el-button size="mini" type="primary" @click="handleFreqRange">
            应用
          </el-button>
        </div>
      </div>
    </div>
    <div class="control-panel">
      <!-- 手动标注 -->
      <div class="panel-block">
        <span class="label1">标注</span>
        <el-input v-model="manualFreq" size="mini" placeholder="频率" />
        <el-input v-model="multiple" size="mini" placeholder="倍频" />
        <el-button size="mini" type="success" @click="handleMark"
          >标注</el-button
        >
        <el-button size="mini" type="info" @click="removeMark">清除</el-button>
      </div>
    </div>
  </div>
</template>

<script>
import Plotly from "plotly.js-dist-min";

export default {
  name: "Waterfall3D",

  props: {
    // 数据格式：[freq, amp, time]
    data: {
      type: Array,
      default: () => [],
    },
  },
  data() {
    return {
      freqMin: "",
      freqMax: "",
      manualFreq: "",
      multiple: 1,

      freqRange: null, // 当前频率范围
      markLines: [], // 标注线
    };
  },

  watch: {
    data: {
      deep: true,
      immediate: true,
      handler() {
        this.$nextTick(() => {
          this.renderChart();
        });
      },
    },
  },

  methods: {
    /** ✅ 时间解析（核心修复） */
    parseTime(time) {
      if (typeof time === "number") {
        return time < 1e12 ? time * 1000 : time;
      }
      if (typeof time === "string") {
        return new Date(time.replace(/-/g, "/")).getTime();
      }
      return new Date(time).getTime();
    },

    /** ✅ 时间格式化 */
    formatTime(t) {
      const d = new Date(t);
      const pad = (n) => String(n).padStart(2, "0");

      return `${d.getFullYear()}/${pad(d.getMonth() + 1)}/${pad(
        d.getDate(),
      )} ${pad(d.getHours())}:${pad(d.getMinutes())}`;
    },
    renderChart() {
      if (!this.data.length) return;

      /** =========================
   * 1. 分组（时间 + Hz）
   ========================== */
      const groupMap = new Map();

      this.data.forEach(([freq, amp, time, Hz]) => {
        const t = this.parseTime(time);
        const key = `${t}_${Hz}`;

        if (!groupMap.has(key)) {
          groupMap.set(key, {
            time: t,
            Hz,
            points: [],
          });
        }

        groupMap.get(key).points.push([freq, amp]);
      });

      /** =========================
   * 2. 转数组 + 排序
   ========================== */
      const groups = [...groupMap.values()];
      //   .sort((a, b) => a.time - b.time);

      /** =========================
   * 3. 同时间分组（关键！！！）
   ========================== */
      const timeGroupMap = new Map();

      groups.forEach((g) => {
        if (!timeGroupMap.has(g.time)) {
          timeGroupMap.set(g.time, []);
        }
        timeGroupMap.get(g.time).push(g);
      });

      /** =========================
   * 4. traces
   ========================== */
      const traces = [];

      groups.forEach((group, globalIndex) => {
        const { time, Hz, points } = group;

        const sameTimeGroups = timeGroupMap.get(time);
        const index = sameTimeGroups.indexOf(group);
        const total = sameTimeGroups.length;

        const spread = 200; // 👈 控制展开宽度（可调）

        // 👉 同时间“对称展开”
        const offsetX = total > 1 ? (index - (total - 1) / 2) * spread : 0;

        const x = [];
        const y = [];
        const z = [];
        const text = [];

        points.sort((a, b) => a[0] - b[0]);

        points.forEach(([freq, amp]) => {
          if (this.freqRange) {
            if (freq < this.freqRange.min || freq > this.freqRange.max) return;
          }

          x.push(time + offsetX); // 👈 关键
          y.push(freq);
          z.push(amp + globalIndex * 0.02); // 防Z重叠

          text.push(
            `时间: ${this.formatTime(time)}<br>` +
              `采样频率: ${Hz} Hz<br>` +
              `频率: ${freq.toFixed(2)} Hz<br>` +
              `幅值: ${amp.toFixed(3)}`,
          );
        });

        traces.push({
          x,
          y,
          z,
          text,
          type: "scatter3d",
          mode: "lines",
          opacity: 0.7,
          line: {
            width: 2,
            color: "#162961",
          },
          showlegend: false,
          hovertemplate: "%{text}<extra></extra>",
        });
      });

      /** =========================
   * 5. X轴刻度（时间 + Hz）
   ========================== */
      const tickvals = groups.map((g) => g.time);

      const ticktext = groups.map((g) => {
        return `${this.formatTime(g.time)}-${g.Hz}Hz`;
      });

      /** =========================
   * 6. 标注线
   ========================== */
      if (this.markLines.length) {
        const minTime = Math.min(...groups.map((g) => g.time));
        const maxTime = Math.max(...groups.map((g) => g.time));

        this.markLines.forEach((line) => {
          traces.push({
            x: [minTime, maxTime],
            y: [line.freq, line.freq],
            z: [0, 0],
            type: "scatter3d",
            mode: "lines+text",
            line: {
              color: "#ff0000",
              width: 4,
              dash: "dash",
            },
            text: ["", line.label],
            textposition: "top right",
            showlegend: false,
            hovertemplate: `${line.label}<extra></extra>`,
          });
        });
      }

      /** =========================
   * 7. layout
   ========================== */
      const layout = {
        // title: "3D 瀑布频谱图",
        paper_bgcolor: "#f5f7fa",
        margin: { l: 0, r: 0, b: 0, t: 40 },

        scene: {
          xaxis: {
            autorange: "reversed",
            title: "时间",
            tickvals,
            ticktext,
            tickangle: 30,
            gridcolor: "#fff",
            backgroundcolor: "#e0e7f1",
            showbackground: true,
          },
          yaxis: {
            title: "频率 (Hz)",
            gridcolor: "#fff",
            backgroundcolor: "#e0e7f1",
            showbackground: true,
          },
          zaxis: {
            title: "幅值",
            gridcolor: "#fff",
            backgroundcolor: "#e0e7f1",
            showbackground: true,
          },
          aspectmode: "manual",
          aspectratio: { x: 2.0, y: 2.8, z: 1.2 },
          camera: {
            up: {
              x: -0.1644947035315824,
              y: -0.07969781808287146,
              z: 0.9831529638377166,
            },
            center: {
              x: -0.052807476121180814,
              y: 0.02451796399554085,
              z: -0.022911006648570736,
            },
            eye: {
              x: 2.980700714870927,
              y: 1.6273671421077383,
              z: 0.6145682420564063,
            },
            projection: {
              type: "orthographic",
            },
          },
        },
      };

      /** =========================
   * 8. 渲染
   ========================== */
      Plotly.react(this.$refs.chart, traces, layout, {
        responsive: true,
        displayModeBar: false,
      });
    },
    //     renderChart() {
    //       if (!this.data.length) return;

    //       /** =========================
    //  * 1. 分组
    //  ========================== */
    //       //   console.log(this.data, "groupMap");
    //       const groupMap = new Map();

    //       this.data.forEach(([freq, amp, time, Hz]) => {
    //         const t = this.parseTime(time);
    //         const key = `${t}_${Hz}`; // 👈 用key区分

    //         if (!groupMap.has(key)) {
    //           groupMap.set(key, {
    //             time: t,
    //             Hz,
    //             points: [],
    //           });
    //         }

    //         groupMap.get(key).points.push([freq, amp]);
    //       });

    //       /** =========================
    //  * 2. 排序
    //  ========================== */
    //       const times = [...groupMap.keys()].sort((a, b) => a - b);

    //       /** =========================
    //  * 3. traces
    //  ========================== */
    //       const traces = [];
    //       times.forEach((time, index) => {
    //         const points = groupMap.get(time);

    //         points.sort((a, b) => a[0] - b[0]);

    //         const x = [];
    //         const y = [];
    //         const z = [];
    //         const text = [];

    //         // const offset = index * 0.02;

    //         points.forEach(([freq, amp]) => {
    //           if (this.freqRange) {
    //             if (freq < this.freqRange.min || freq > this.freqRange.max) return;
    //           }
    //           const offsetX = index * 50; // 👈 新增
    //           const offsetZ = index * 0.02; // 👈 你已有
    //           x.push(time + offsetX);
    //           y.push(freq);
    //           z.push(amp + offsetZ);

    //           text.push(
    //             `时间: ${this.formatTime(time)}<br>` +
    //               `频率: ${freq.toFixed(2)} Hz<br>` +
    //               `幅值: ${amp.toFixed(3)}`,
    //           );
    //         });

    //         traces.push({
    //           x,
    //           y,
    //           z,
    //           text,
    //           type: "scatter3d",
    //           mode: "lines",
    //           opacity: 0.7,
    //           line: {
    //             width: 2,
    //             color: "#162961",
    //           },
    //           showlegend: false,
    //           hovertemplate: "%{text}<extra></extra>",
    //         });
    //       });
    //       /** =========================
    //        * ✅ 标注线（修复重复问题）
    //        ========================== */
    //       if (this.markLines.length) {
    //         const minTime = Math.min(...times);
    //         const maxTime = Math.max(...times);

    //         this.markLines.forEach((line) => {
    //           traces.push({
    //             x: [minTime, maxTime],
    //             y: [line.freq, line.freq],
    //             z: [0, 0],
    //             type: "scatter3d",
    //             mode: "lines+text",
    //             line: {
    //               color: "#ff0000",
    //               width: 4,
    //               dash: "dash",
    //             },
    //             text: ["", line.label],

    //             textposition: "top right",
    //             showlegend: false,
    //             hovertemplate: `${line.label}<extra></extra>`,
    //           });
    //         });
    //       }

    //       // 👉 抽稀刻度（防止重叠）
    //       const step = Math.ceil(times.length / 8);
    //       const tickvals = times.filter((_, i) => i % step === 0);
    //       const ticktext = tickvals.map(this.formatTime);

    //       /** =========================
    //          * 5. 布局（工业风）
    //          ========================== */
    //       const layout = {
    //         title: "3D 瀑布频谱图",
    //         paper_bgcolor: "#f5f7fa",
    //         margin: { l: 0, r: 0, b: 0, t: 40 },

    //         scene: {
    //           xaxis: {
    //             autorange: "reversed", //轴方向
    //             title: "时间",
    //             tickvals,
    //             ticktext,
    //             dtick: "D3",
    //             // showbackground: true,
    //             // backgroundcolor: "#ffffff",
    //             // gridcolor: "#e0e7f1",
    //             gridcolor: "#fff",
    //             backgroundcolor: "#e0e7f1",
    //             showbackground: true,
    //             linecolor: "black",
    //             ticks: "outside",
    //             ticklen: 10,
    //             tickcolor: "black",
    //             zeroline: false,
    //             tickangle: -10,
    //             tickangle: 30, // 👈 倾斜
    //             margin: {
    //               l: 20,
    //               r: 120, // 👉 关键！！给时间留空间
    //               t: 40,
    //               b: 40,
    //             },
    //           },
    //           yaxis: {
    //             title: "频率 (Hz)",
    //             gridcolor: "#fff",
    //             backgroundcolor: "#e0e7f1",
    //             showbackground: true,
    //             linecolor: "black",
    //             ticks: "outside",
    //             ticklen: 10,
    //             tickcolor: "black",
    //             zeroline: false,
    //             tickangle: -10,
    //             tickangle: 0,
    //           },
    //           zaxis: {
    //             title: "幅值",
    //             gridcolor: "#fff",
    //             backgroundcolor: "#e0e7f1",
    //             showbackground: true,
    //             linecolor: "black",
    //             ticks: "outside",
    //             ticklen: 10,
    //             tickcolor: "black",
    //             zeroline: false,
    //             tickangle: -10,
    //             tickangle: 0,
    //           },
    //           /** ✅ 核心1：比例（决定“扁不扁”） */
    //           //   bgcolor: "#e5ecf6",
    //           //   gridcolor: "#fff",
    //           aspectmode: "manual",
    //           aspectratio: { x: 2.0, y: 2.8, z: 1.2 },
    //           /** ✅ 核心2：相机（决定轴在哪边） */
    //           camera: {
    //             up: {
    //               x: -0.1644947035315824,
    //               y: -0.07969781808287146,
    //               z: 0.9831529638377166,
    //             },
    //             center: {
    //               x: -0.052807476121180814,
    //               y: 0.02451796399554085,
    //               z: -0.022911006648570736,
    //             },
    //             eye: {
    //               x: 2.980700714870927,
    //               y: 1.6273671421077383,
    //               z: 0.6145682420564063,
    //             },
    //             projection: {
    //               type: "orthographic",
    //             },
    //           },
    //         },
    //       };

    //       /** =========================
    //          * 6. 渲染
    //          ========================== */
    //       Plotly.react(this.$refs.chart, traces, layout, {
    //         responsive: true,
    //         displayModeBar: false,
    //       });
    //       // 监听图表的 relayout 事件，获取并输出相机视角
    //       const plotElement = document.getElementById(`waterfall-chart`);
    //       plotElement.on("plotly_relayout", function (eventData) {
    //         // 在每次布局变更时，打印当前相机视角
    //         if (eventData["scene.camera"]) {
    //           console.log(
    //             "当前相机视角:",
    //             eventData["scene.camera"],
    //             eventData["scene.aspectratio"],
    //           );
    //         }
    //       });
    //     },
    removeMark() {
      this.markLines = [];
      this.manualFreq = "";
      this.multiple = 1;
      this.renderChart();
    },
    handleFreqRange() {
      const min = Number(this.freqMin);
      const max = Number(this.freqMax);

      if (isNaN(min) || isNaN(max)) return;

      this.freqRange = { min, max };

      this.renderChart();
    },
    handleMark() {
      const base = Number(this.manualFreq);
      const multi = Number(this.multiple) || 1;

      if (isNaN(base)) return;

      const newLines = [];

      for (let i = 1; i <= multi; i++) {
        newLines.push({
          freq: base * i,
          label: `${i}x (${base * i}Hz)`,
        });
      }

      this.markLines = [...this.markLines, ...newLines];

      this.renderChart();
    },
  },
};
</script>

<style scoped>
.waterfall-chart {
  width: 100%;
  height: 520px;
}
.control-panel {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 12px;
  padding: 8px 12px;
  background: #f5f7fa;
  border: 1px solid #ddd;
  border-radius: 6px;
  margin-bottom: 10px;
}
/* 🌟 关键：独占一行 */
.full-row {
  width: 100%;
  .panel-block {
    width: 45%;
  }
}
.panel-block {
  display: flex;
  align-items: center;
  gap: 6px;
}

.label {
  font-size: 12px;
  color: #666;
}
.label1 {
  font-size: 12px;
  color: #666;
  display: inline-block;
  width: 75px;
}

.btn-group {
  display: flex;
  gap: 6px;
}

.btn {
  padding: 2px 8px;
  font-size: 12px;
  border: 1px solid #ccc;
  border-radius: 3px;
  cursor: pointer;
}

.btn.active {
  background: #409eff;
  color: #fff;
}
.full-width {
  width: 100%;
}

.el-cascader {
  font-size: 12px;
}

.el-cascader__tags {
  max-width: 240px;
  overflow: hidden;
}

.feature-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 10px;
  width: 100%;
}

.feature-item {
  display: flex;
  align-items: center;
  gap: 6px;
}

.el-select {
  flex: 1;
}
</style>
