<!--
  垂直校准组件 Vertical Calibration
  用于校准手机设备的垂直方向。最有的垂直方向是垂直 + 微微前倾(微微俯视)。实现思路为:
  1.  使用VueUse的运动传感器方法useDeviceMotion()实现重力感应
      使用VueUse的方向传感器方法useDeviceOrientation()实现方向感应
      方向传感器如果支持地磁传感器,也会调用地磁传感器
  2.  如果有地磁,则优先方向
      如果无地磁,但是有方向和重力,则优先重力,辅以方向
      如果无地磁,方向和重力也只有其一,那么就有啥用啥
 -->

<!--
  视图层
 -->
<template>

<!-- 布局模板容器。条件渲染:默认无权限时渲染 -->
<MySpace v-if="!permissionGranted">

  <!-- 警报框 -->
  <t-alert
    theme="info" :title="lang.FunctionIntroductionTitle"
  >
    {{ lang.FunctionIntroductionContent }}
  </t-alert>

  <MyButton @click="ensurePermissions">
    {{ lang.CallSensorButtonLabel }}
  </MyButton>

</MySpace>

<!-- 布局模板容器。条件渲染:有权限时渲染 -->
<MySpace v-else>

  <!-- 重力感应警报框 -->
  <t-alert
    v-if="isMotionSupported"
    theme="info" :title="lang.MotionSensorIntroductionTitle"
  >
    <div v-for="(content, index) of lang.MotionSensorIntroductionContent" :key="index">
      {{ content }}
    </div>
  </t-alert>

  <!-- 方向感应警报框 -->
  <t-alert
    v-if="isOrientationSupported"
    theme="info" :title="lang.OrientationSensorIntroductionTitle"
  >
    <div v-for="(content, index) of lang.OrientationSensorIntroductionContent" :key="index">
      {{ content }}
    </div>
  </t-alert>

  <!-- 操作建议警报框:有地磁传感器 -->
  <t-alert
    v-if="isGeomagneticSupported"
    theme="warning" :title="lang.OperationSuggestionIntroductionTitle"
  >
    {{ lang.OperationSuggestionGeomagneticIntroductionContent }}
  </t-alert>

  <!-- 操作建议警报框:无地磁传感器,但有重力传感器和运动传感器 -->
  <t-alert
    v-else-if="isMotionSupported && isOrientationSupported"
    theme="warning" :title="lang.OperationSuggestionIntroductionTitle"
  >
    {{ lang.OperationSuggestionNonGeomagneticIntroductionContent }}
  </t-alert>

  <!-- 重力感应数据 -->
  <div class="center">
    <table ref="tableRef">
      <!-- 表头 -->
      <thead>
        <tr>
          <th :colspan="2">{{ lang.GravityTableHead[0] }}</th>
          <th :colspan="2">{{ lang.GravityTableHead[1] }}</th>
        </tr>
      </thead>
      <!-- 表格体 -->
      <tbody>
        <tr>
          <td>{{ lang.GravityTableData[0][0] }}</td>
          <td>{{ accelerationThrottled.x === null ? "N/A" : accelerationThrottled.x.toFixed(1) }}</td>
          <td>{{ lang.GravityTableData[1][0] }}</td>
          <td>{{ orientationAlphaThrottled === null ? "N/A" : orientationAlphaThrottled.toFixed(1) }}</td>
        </tr>
        <tr>
          <td>{{ lang.GravityTableData[0][1] }}</td>
          <td>{{ accelerationThrottled.y === null ? "N/A" : accelerationThrottled.y.toFixed(1) }}</td>
          <td>{{ lang.GravityTableData[1][1] }}</td>
          <td>{{ orientationBetaThrottled === null ? "N/A" : orientationBetaThrottled.toFixed(1) }}</td>
        </tr>
        <tr>
          <td>{{ lang.GravityTableData[0][2] }}</td>
          <td>{{ accelerationThrottled.z === null ? "N/A" : accelerationThrottled.z.toFixed(1) }}</td>
          <td>{{ lang.GravityTableData[1][2] }}</td>
          <td>{{ orientationGammaThrottled === null ? "N/A" : orientationGammaThrottled.toFixed(1) }}</td>
        </tr>
        <!-- 地磁传感器 -->
        <tr v-if="isGeomagneticSupported">
          <th :colspan="2">{{ lang.GeomagneticLabel }}</th>
          <td :colspan="2">{{ isGeomagneticSupported ? lang.SupportedLabel : lang.NotSupportedLabel }}</td>
        </tr>
        <!-- 备注 -->
        <tr v-if="isNotSupportedAllRef">
          <th :colspan="4">{{ lang.NotSupportedAllLabel }}</th>
        </tr>
      </tbody>
    </table>
  </div>

  <!-- 结束校准按钮 -->
  <MyButton @click="permissionGranted = false">
    {{ lang.EndButtonLabel }}
  </MyButton>

</MySpace>

</template>


<!--
  逻辑层
 -->
<script setup>
// 从vue库导入生命周期钩子
import { onMounted, useTemplateRef, shallowRef, watch, nextTick } from "vue"
// 从vueuse库导入运动传感器和方向传感器
import { useDeviceMotion, useDeviceOrientation, refThrottled } from "@vueuse/core"
// 导入自有方法
import my from "@/utils/myFunc.js"
// 导入语言包
import { useLang, lang } from "./VerticalCalibration-lang.js"

// 解构接收运动感应的各类数据
const {
  // 是否有权限:默认都是false
  // 以此作为v-if条件渲染的依据,因此不能放在onMounted()中
  permissionGranted,
  // 是否需要请求权限
  // requirePermissions: isRequirePermissions,
  // 请求权限方法
  ensurePermissions,
  // 设备是否支持重力感应
  isSupported: isMotionSupported,
  // 设备在X、Y、Z三轴上的加速度(含重力加速度)
  // x:手机左右的加速度,左为正
  // y:手机垂直的加速度,正立为正
  // z:手机躺平的加速度,躺平(俯视)为正
  accelerationIncludingGravity: acceleration,
} = useDeviceMotion()

// 解构接收方向感应的各类数据
// 方向感应事实上也需要请求权限,但是VueUse在useDeviceOrientation这里没集成
// 所以依赖useDeviceMotion的ensurePermissions()方法
const {
  // 设备是否支持方向感应
  isSupported: isOrientationSupported,
  // 是否支持绝对方向(磁力计)
  // 亦即设备是否支持/配备使用地磁传感器获取绝对方向
  isAbsolute: isGeomagneticSupported,
  // 方向感应:Z轴(XY平面)
  alpha: orientationAlpha,
  // 方向感应:X轴(YZ平面)
  beta: orientationBeta,
  // 方向感应:Y轴(XZ平面)
  gamma: orientationGamma,
} = useDeviceOrientation()

// 节流处理
const accelerationThrottled = refThrottled(acceleration, 500)
const orientationAlphaThrottled = refThrottled(orientationAlpha, 500)
const orientationBetaThrottled = refThrottled(orientationBeta, 500)
const orientationGammaThrottled = refThrottled(orientationGamma, 500)

// 获取表格引用
const tableRef = useTemplateRef("tableRef")
// “是否其实并不支持”标记
const isNotSupportedAllRef = shallowRef(false)

/**
 * 报错的通知方法
 */
function errorDialog(error) {
  // 直接对话框报错
  my.dialog({
    theme: "danger",
    header: lang.value.ErrorDialogTitle,
    body: lang.value.ErrorDialogContent + error
  })
}

// 生命周期钩子,SSG的SPA化实现,组件挂载后执行
// 用于进行必要的各类初始化操作
onMounted(() => { try {
  // 语言包水合
  lang.value = useLang()
  // 获取硬件权限后,保持数据表格滚动到视图中间
  watch(permissionGranted, watchPermissionGrantedHandle)
} catch (error) {
  my.error("onMounted()报错:", error, errorDialog)
}})

/**
 * 获取硬件权限后:
 * 1.  更新“是否支持”标记
 * 2.  保持数据表格滚动到视图中间
 */
function watchPermissionGrantedHandle(newPermissionGrantedValue) {
  // 如果权限为false,则不执行
  if (!newPermissionGrantedValue) { return }
  // 更新“是否其实并不支持”标记
  if (
    (acceleration.value.x === null)
      && (acceleration.value.y === null)
      && (acceleration.value.z === null)
      && (orientationAlpha.value === null)
      && (orientationBeta.value === null)
      && (orientationGamma.value === null)
  ) {
    isNotSupportedAllRef.value = true
  } else {
    isNotSupportedAllRef.value = false
  }
  // 下个渲染周期执行focusOnCanvas()
  nextTick(focusOnTable).catch((error) => {
    my.error("nextTickFocusOnCanvas()报错:", error, errorDialog)
  })
  /**
   * 聚焦table的内部方法
   */
  function focusOnTable() {
    // 滚动到canvas
    tableRef.value.scrollIntoView({
      // 平滑滚动
      behavior: "smooth",
      // 垂直中心对齐
      block: "center",
      // 水平就近对齐
      inline: "nearest"
    })
  }
}

</script>


<!--
  样式层
 -->
<style lang="css" scoped>
/* 让表格内文字居中 */
td, th {
  text-align: center;
  vertical-align: middle;
}
</style>