<template>
  <div class="angle-editor-page p-40">
    <button class="open-btn px-24 py-10 color-fff border-none round-8 fs-14 pointer" @click="visible = true">打开多角度编辑器</button>

    <!-- 多角度编辑器弹窗 -->
    <div v-if="visible" class="modal-mask fixed flex-center" @click.self="visible = false">
      <div class="modal round-12 hidden">
        <!-- 头部：预设按钮 + 关闭 -->
        <header class="modal-header flex-between px-16 py-14">
          <div class="flex flex-wrap gap-6">
            <button
              v-for="p in presets"
              :key="p.value"
              class="preset-btn py-6 px-12 color-aaa border-none round-16 fs-12 pointer"
              :class="{ active: activePreset === p.value }"
              @click="selectPreset(p)"
            >
              {{ p.label }}
            </button>
          </div>
          <span class="close-x fs-22 color-888 pointer" @click="visible = false">×</span>
        </header>

        <!-- 主体：左可视化器 + 右控制面板 -->
        <div class="modal-body flex gap-20 p-24">
          <!-- 左：SVG 线框球体 -->
          <div class="visualizer relative wh-300">
            <svg
              ref="svgRef"
              :viewBox="`0 0 ${S} ${S}`"
              class="sphere-svg w-full h-full"
              :class="{ dragging: isDragging }"
              @mousedown="onSphereMouseDown"
            >
              <defs>
                <!-- 球体阴影渐变：中心透明、边缘暗，营造玻璃球内嵌效果 -->
                <radialGradient id="sphereShade" cx="50%" cy="50%" r="50%">
                  <stop offset="0%" stop-color="#000" stop-opacity="0" />
                  <stop offset="70%" stop-color="#000" stop-opacity="0" />
                  <stop offset="100%" stop-color="#000" stop-opacity="0.1" />
                </radialGradient>
                <!-- 缩略图圆形裁剪 -->
                <clipPath id="thumbClip">
                  <circle :cx="cx" :cy="cy" :r="40" />
                </clipPath>
              </defs>
              <!-- 纬线（3D 旋转投影 path） -->
              <path
                v-for="(lat, i) in latLines"
                :key="'lat' + i"
                :d="lat.d"
                class="grid-line"
                :class="lat.front ? 'grid-front' : 'grid-back'"
              />
              <!-- 经线（3D 旋转投影 path） -->
              <path
                v-for="(lon, i) in lonLines"
                :key="'lon' + i"
                :d="lon.d"
                class="grid-line"
                :class="lon.front ? 'grid-front' : 'grid-back'"
              />
              <!-- 球体外轮廓 -->
              <circle :cx="cx" :cy="cy" :r="R" class="sphere-outline" />

              <!-- 中心角色缩略图（圆形裁剪 + 边框） -->
              <foreignObject :x="cx - 40" :y="cy - 40" width="80" height="80" clip-path="url(#thumbClip)">
                <img
                  src="@/static/image/meinv.png"
                  width="80px"
                  height="80px"
                  alt=""
                  style="border-radius: 50%; object-fit: cover; opacity: 0.8"
                />
              </foreignObject>
              <!-- 缩略图圆形边框 -->
              <circle :cx="cx" :cy="cy" :r="40" class="thumb-border" />

              <!-- 球体阴影叠加：边缘渐暗，让缩略图看起来在球体内部 -->
              <circle :cx="cx" :cy="cy" :r="R" fill="url(#sphereShade)" pointer-events="none" />

              <!-- 视角指示线 + 端点 -->
              <line :x1="cx" :y1="cy" :x2="indicator.x" :y2="indicator.y" class="indicator-line" />
              <rect :x="indicator.x - 5" :y="indicator.y - 5" width="10" height="10" rx="2" class="indicator-dot" />
            </svg>

            <!-- 四个方向箭头 -->
            <span class="arrow arrow-up absolute color-666 fs-12">▲</span>
            <span class="arrow arrow-down absolute color-666 fs-12">▼</span>
            <span class="arrow arrow-left absolute color-666 fs-12">◀</span>
            <span class="arrow arrow-right absolute color-666 fs-12">▶</span>
          </div>

          <!-- 右：参数控制 -->
          <div class="flex-1 flex-col gap-22 justify-center">
            <div class="control-item flex-col gap-8">
              <label>水平环绕</label>
              <div class="flex-items-center gap-12">
                <input type="range" v-model.number="yaw" min="-180" max="180" class="slider flex-1 h-4 round-2 pointer" />
                <span class="minw-40 text-right fs-13 color-fff">{{ yaw }}°</span>
              </div>
            </div>

            <div class="control-item flex-col gap-8">
              <label>垂直俯仰</label>
              <div class="flex-items-center gap-12">
                <input type="range" v-model.number="pitch" min="-90" max="90" class="slider blue flex-1 h-4 round-2 pointer" />
                <span class="minw-40 text-right fs-13 color-fff">{{ pitch }}°</span>
              </div>
            </div>

            <div class="control-item flex-col gap-8">
              <label>景别缩放</label>
              <div class="flex-items-center gap-12">
                <select v-model="shotScale" class="select-shot flex-1 h-32 color-fff round-6 px-8 pointer">
                  <option v-for="s in shotScales" :key="s" :value="s">{{ s }}</option>
                </select>
              </div>
            </div>

            <div class="control-item flex-col gap-8">
              <label>提示词</label>
              <div class="flex-items-center gap-12">
                <button
                  class="toggle w-40 h-20 round-10 border-none relative pointer"
                  :class="{ on: promptEnabled }"
                  @click="promptEnabled = !promptEnabled"
                >
                  <span class="toggle-knob absolute top-2 left-2 wh-16 round bg-fff"></span>
                </button>
              </div>
            </div>
          </div>
        </div>

        <!-- 底部：重置 + 生成 -->
        <footer class="modal-footer flex-between px-16 py-14">
          <button class="reset-btn flex-items-center gap-6 py-6 px-14 border-none round-6 fs-13 pointer" @click="resetParams">
            <span class="fs-14">↺</span> 重置参数
          </button>
          <div class="flex-items-center gap-12">
            <span class="cost fs-13">⚡1</span>
            <button class="generate-btn py-8 px-20 color-fff border-none round-6 fs-13 pointer" @click="generate">⬆ 生成</button>
          </div>
        </footer>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, computed, onBeforeUnmount } from 'vue';

const visible = ref(false);

// ============ 球体参数 ============
const S = 300; // SVG viewBox 尺寸
const cx = S / 2;
const cy = S / 2;
const R = 120; // 球体半径

const svgRef = ref<SVGSVGElement | null>(null);
const isDragging = ref(false);

// ============ 控制状态 ============
const yaw = ref(0); // 水平环绕 -180~180
const pitch = ref(60); // 垂直俯仰 -90~90
const shotScale = ref('中景');
const promptEnabled = ref(false);
const activePreset = ref('frontal-top');

// 景别选项
const shotScales = ['远景', '全景', '中景', '近景', '特写'];

// 预设
const presets = [
  { label: '自定义', value: 'custom', yaw: 0, pitch: 0 },
  { label: '鱼眼视角', value: 'fisheye', yaw: 0, pitch: 0 },
  { label: '倾斜视角', value: 'oblique', yaw: 30, pitch: 30 },
  { label: '正面俯拍', value: 'frontal-top', yaw: 0, pitch: 60 },
  { label: '正面仰拍', value: 'frontal-low', yaw: 0, pitch: -30 },
  { label: '全景俯拍', value: 'panoramic', yaw: 0, pitch: 88 },
  { label: '背面视角', value: 'rear', yaw: 180, pitch: 0 },
];

const selectPreset = (p: { value: string; yaw: number; pitch: number }) => {
  activePreset.value = p.value;
  yaw.value = p.yaw;
  pitch.value = p.pitch;
};

// ============ 球体 3D 线框计算 ============
// 将 3D 点经 yaw（绕 Y 轴）+ pitch（绕 X 轴）旋转后正交投影到 2D
const rotateAndProject = (x: number, y: number, z: number, yawRad: number, pitchRad: number) => {
  // 绕 Y 轴旋转（yaw）
  const x1 = x * Math.cos(yawRad) + z * Math.sin(yawRad);
  const y1 = y;
  const z1 = -x * Math.sin(yawRad) + z * Math.cos(yawRad);
  // 绕 X 轴旋转（pitch）
  const y2 = y1 * Math.cos(pitchRad) - z1 * Math.sin(pitchRad);
  const z2 = y1 * Math.sin(pitchRad) + z1 * Math.cos(pitchRad);
  // 正交投影 + 保留 z 用于判断前后面
  return { sx: cx + x1, sy: cy - y2, sz: z2 };
};

// 生成一条线上采样点的 3D 坐标 → 旋转投影 → 按 z 正负拆成多段 path
// 每段独立标记 front/back，正面段实线、背面段虚线
const buildArcSegments = (points3D: { x: number; y: number; z: number }[], yawRad: number, pitchRad: number) => {
  const proj = points3D.map((p) => rotateAndProject(p.x, p.y, p.z, yawRad, pitchRad));
  const segments: { d: string; front: boolean }[] = [];
  let curPoints: typeof proj = [];
  let curFront = proj[0].sz > 0;
  for (const p of proj) {
    const isFront = p.sz > 0;
    if (isFront !== curFront) {
      // 段切换：保存当前段，开启新段（带上断点处的点保证连续）
      if (curPoints.length > 1) {
        let d = '';
        curPoints.forEach((pt, i) => {
          d += (i === 0 ? 'M' : 'L') + pt.sx.toFixed(1) + ',' + pt.sy.toFixed(1);
        });
        segments.push({ d, front: curFront });
      }
      curPoints = [p];
      curFront = isFront;
    } else {
      curPoints.push(p);
    }
  }
  if (curPoints.length > 1) {
    let d = '';
    curPoints.forEach((pt, i) => {
      d += (i === 0 ? 'M' : 'L') + pt.sx.toFixed(1) + ',' + pt.sy.toFixed(1);
    });
    segments.push({ d, front: curFront });
  }
  return segments;
};

// 纬线：固定 φ，θ 绕一圈（每 15° 一条，共 11 条）
const latLines = computed(() => {
  const angles = [-75, -60, -45, -30, -15, 0, 15, 30, 45, 60, 75];
  const yawRad = (yaw.value * Math.PI) / 180;
  const pitchRad = (pitch.value * Math.PI) / 180;
  const N = 48;
  const result: { d: string; front: boolean; key: string }[] = [];
  angles.forEach((deg, lineIdx) => {
    const phi = (deg * Math.PI) / 180;
    const pts: { x: number; y: number; z: number }[] = [];
    for (let i = 0; i <= N; i++) {
      const theta = (i / N) * 2 * Math.PI;
      pts.push({
        x: R * Math.cos(phi) * Math.sin(theta),
        y: R * Math.sin(phi),
        z: R * Math.cos(phi) * Math.cos(theta),
      });
    }
    buildArcSegments(pts, yawRad, pitchRad).forEach((seg, segIdx) => {
      result.push({ d: seg.d, front: seg.front, key: `lat-${lineIdx}-${segIdx}` });
    });
  });
  return result;
});

// 经线：固定 θ，φ 从 -90 到 90（每 15° 一条，共 12 条）
const lonLines = computed(() => {
  const angles = [0, 15, 30, 45, 60, 75, 90, 105, 120, 135, 150, 165];
  const yawRad = (yaw.value * Math.PI) / 180;
  const pitchRad = (pitch.value * Math.PI) / 180;
  const N = 48;
  const result: { d: string; front: boolean; key: string }[] = [];
  angles.forEach((deg, lineIdx) => {
    const theta = (deg * Math.PI) / 180;
    const pts: { x: number; y: number; z: number }[] = [];
    for (let i = 0; i <= N; i++) {
      const phi = -Math.PI / 2 + (i / N) * Math.PI;
      pts.push({
        x: R * Math.cos(phi) * Math.sin(theta),
        y: R * Math.sin(phi),
        z: R * Math.cos(phi) * Math.cos(theta),
      });
    }
    buildArcSegments(pts, yawRad, pitchRad).forEach((seg, segIdx) => {
      result.push({ d: seg.d, front: seg.front, key: `lon-${lineIdx}-${segIdx}` });
    });
  });
  return result;
});

// 视角指示点：将 (yaw, pitch) 投影到 2D
const indicator = computed(() => {
  const yawRad = (yaw.value * Math.PI) / 180;
  const pitchRad = (pitch.value * Math.PI) / 180;
  const x = cx + R * Math.cos(pitchRad) * Math.sin(yawRad);
  const y = cy - R * Math.sin(pitchRad);
  return { x, y };
});

// ============ 拖拽球体旋转视角 ============
const DRAG_SENSITIVITY = 0.5; // 像素 → 度
let dragStartX = 0;
let dragStartY = 0;
let dragStartYaw = 0;
let dragStartPitch = 0;

const onSphereMouseDown = (e: MouseEvent) => {
  e.preventDefault();
  e.stopPropagation();
  isDragging.value = true;
  activePreset.value = 'custom';
  dragStartX = e.clientX;
  dragStartY = e.clientY;
  dragStartYaw = yaw.value;
  dragStartPitch = pitch.value;
  window.addEventListener('mousemove', onWindowMouseMove);
  window.addEventListener('mouseup', onWindowMouseUp);
};

const onWindowMouseMove = (e: MouseEvent) => {
  if (!isDragging.value) return;
  const dx = e.clientX - dragStartX;
  const dy = e.clientY - dragStartY;
  // 水平拖动 → yaw，垂直拖动 → pitch（上拖为正）
  yaw.value = Math.round(wrapAngle(dragStartYaw + dx * DRAG_SENSITIVITY));
  pitch.value = Math.round(clamp(dragStartPitch - dy * DRAG_SENSITIVITY, -90, 90));
};

const onWindowMouseUp = () => {
  isDragging.value = false;
  window.removeEventListener('mousemove', onWindowMouseMove);
  window.removeEventListener('mouseup', onWindowMouseUp);
};

// 工具：角度限制
const clamp = (v: number, min: number, max: number) => Math.max(min, Math.min(max, v));
const wrapAngle = (v: number) => {
  while (v > 180) v -= 360;
  while (v < -180) v += 360;
  return v;
};

onBeforeUnmount(() => {
  window.removeEventListener('mousemove', onWindowMouseMove);
  window.removeEventListener('mouseup', onWindowMouseUp);
});

// ============ 操作 ============
const resetParams = () => {
  yaw.value = 0;
  pitch.value = 0;
  shotScale.value = '中景';
  promptEnabled.value = false;
  activePreset.value = 'custom';
};

const generate = () => {
  console.log('生成参数', {
    yaw: yaw.value,
    pitch: pitch.value,
    shotScale: shotScale.value,
    promptEnabled: promptEnabled.value,
  });
};
</script>

<style scoped lang="scss">
.open-btn {
  background: #409eff;
  &:hover {
    background: #5599ff;
  }
}

/* 弹窗遮罩 */
.modal-mask {
  inset: 0;
  background: rgba(0, 0, 0, 0.6);
  z-index: 9999;
}

/* 弹窗主体 */
.modal {
  width: 680px;
  background: #1a1a2e;
  border: 1px solid #333;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
}

/* 头部 */
.modal-header {
  border-bottom: 1px solid #2a2a3e;
}

.preset-btn {
  background: #2a2a3e;
  transition: all 0.2s;
  &:hover {
    background: #3a3a4e;
    color: #fff;
  }
  &.active {
    background: #409eff;
    color: #fff;
  }
}

.close-x {
  line-height: 1;
  &:hover {
    color: #fff;
  }
}

/* 主体 */
.modal-body {
  border-bottom: 1px solid #2a2a3e;
}

/* 左：可视化器 */
.visualizer {
  flex-shrink: 0;
}

.sphere-svg {
  cursor: grab;
}

.sphere-svg.dragging {
  cursor: grabbing;
}

.grid-line {
  fill: none;
  stroke-width: 1;
}
.grid-front {
  stroke: #ffffff41;
}
.grid-back {
  stroke: #ffffff41;
  opacity: 0.5;
  stroke-dasharray: 4 3;
}

.sphere-outline {
  fill: none;
  stroke: #ffffff41;
  stroke-width: 1.5;
}

.thumb-border {
  fill: none;
  stroke: #ffffff60;
  stroke-width: 1.5;
  pointer-events: none;
}

.indicator-line {
  stroke: #fff;
  stroke-width: 2;
}

.indicator-dot {
  fill: #1a1a2e;
  stroke: #fff;
  stroke-width: 1.5;
  pointer-events: none;
}

/* 方向箭头 */
.arrow {
  user-select: none;
}
.arrow-up {
  top: -4px;
  left: 50%;
  transform: translateX(-50%);
}
.arrow-down {
  bottom: -4px;
  left: 50%;
  transform: translateX(-50%);
}
.arrow-left {
  left: -4px;
  top: 50%;
  transform: translateY(-50%);
}
.arrow-right {
  right: -4px;
  top: 50%;
  transform: translateY(-50%);
}

/* 右：控制面板 */
.control-item {
  label {
    font-size: 13px;
    color: #ccc;
  }
}

.slider {
  -webkit-appearance: none;
  appearance: none;
  background: #2a2a3e;
  outline: none;

  &::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: #888;
    cursor: pointer;
  }

  &.blue::-webkit-slider-thumb {
    background: #409eff;
  }
}

.select-shot {
  background: #2a2a3e;
  border: 1px solid #3a3a4e;
  outline: none;
}

/* 开关 */
.toggle {
  background: #3a3a4e;
  transition: background 0.2s;

  &.on {
    background: #409eff;
  }
}

.toggle-knob {
  transition: transform 0.2s;
}

.toggle.on .toggle-knob {
  transform: translateX(20px);
}

/* 底部 */
.modal-footer {
  border-top: 1px solid #2a2a3e;
}

.reset-btn {
  background: #2a2a3e;
  color: #ccc;
  &:hover {
    background: #3a3a4e;
    color: #fff;
  }
}

.cost {
  color: #ffa500;
}

.generate-btn {
  background: #409eff;
  &:hover {
    background: #5599ff;
  }
}
</style>
