<template>
  <div class="game-container">
    <canvas id="canvas" @mousedown="handleMouseDown" @mouseup="handleMouseUp" @touchstart="handleTouchStart"
      @touchend="handleTouchEnd"></canvas>
  </div>
</template>

<script setup name="Basketball" lang="ts">
import { onMounted, ref, reactive, onBeforeUnmount } from 'vue';
import { initSpeech, speckText, playMusic, controlMusic, speckCancel, chineseNumber } from '@/utils/speech';
import { useWebSocket } from '@/utils/bodyposeWs';
const { proxy } = getCurrentInstance() as any;
const router = useRouter();
const { bodyposeWs, startDevice, checkBodypose, openBodypose, terminateBodypose, suspendBodypose, resumeBodypose, getBodyposeState, closeWS } = useWebSocket();

const data = reactive<any>({
  bodyposeData: {},//姿态信息
  bodyposeState: false,//姿态识别窗口状态
  parameter: {},//参数
  deviceInfo: {},//设备信息
  againNum: 0,//再次启动次数
  againTimer: null,//定时状态
  wsState: false,//WS状态
  clientObj: {},//浏览器对象
  myThrow: 0,//0放下1举投
});

const { bodyposeData, bodyposeState, parameter, deviceInfo, againNum, againTimer, wsState, clientObj, myThrow } = toRefs(data);

/**
 * 输出犯规
 */
watch(
  () => myThrow.value,
  (newData,oldData) => {
    console.log("ppp",oldData,newData)
    if (newData == 1) {
      console.log("投篮")
      handleMouseDown();
      setTimeout(()=>{
        handleMouseUp();
      },100)
      
    }else{
      
    }
  },
  { immediate: true }
);

/**
 * 初始化
 */
const getInit = async () => {
  console.log("触发姿态识别")
  let deviceid = localStorage.getItem('deviceid') || '';
  if (!deviceid) {
    proxy?.$modal.msgError(`请重新登录绑定设备号后使用`);
    return false;
  }
  bodyposeState.value = true;
  if (wsState.value) {
    proxy?.$modal.msgWarning(`操作过快，请稍后重试`);
    setTimeout(() => {
      bodyposeState.value = false;
    }, 1000)
    return false;
  }
  speckText("正在姿态识别");
  bodyposeWs((e: any) => {
    //console.log("bodyposeWS", e)
    if (e?.wksid) {
      wsState.value = true;
      //获取设备信息
      startDevice({ deviceid: deviceid });
      console.log("获取设备信息")
    }
    if (e?.type == 'fe_device_init_result') {
      //接收设备信息并发送请求
      if (e?.device_info) {
        deviceInfo.value = e.device_info;
        getCheckBodypose();
        console.log("返回设备信息，检查是否支持姿态识别")
      } else {
        proxy?.$modal.msgError(`设备信息缺失，请重新登录绑定设备号后使用`);
      }
    }
    if (e?.cmd == 'check_bodyposecontroller_available') {
      let handcontroller_id = deviceInfo.value.handcontroller_id;
      if (e?.code == 0) {
        //查看姿态识别状态，如果不处于关闭就先关闭再重新启动（可能会APP退出然后工作站还在运行的可能性）
        getBodyposeState(handcontroller_id);
        againNum.value = 0;
        againTimer.value = null;
        clearTimeout(againTimer.value);
        console.log("查看姿态识别状态")
      } else {
        //尝试多次查询姿态识别状态
        if (againNum.value <= 2) {
          againTimer.value = setTimeout(() => {
            getCheckBodypose();
          }, 500)
          againNum.value++;
        } else {
          let msg = "";
          if (e.code == 102402) {
            msg = `多次连接失败请重试，姿态识别模块被占用`;
          } else {
            msg = `多次连接失败请重试，姿态识别模块不可用，code：${e.code}`;
          }
          proxy?.$modal.msgWarning(msg);
          againNum.value = 0;
          againTimer.value = null;
          clearTimeout(againTimer.value);
          getCloseBodypose();//直接关闭 
        }

      }
    }
    if (e?.cmd == 'get_bodyposecontroller_state') {
      let handcontroller_id = deviceInfo.value.handcontroller_id;
      //state说明: 0：关闭，3：空闲，36：工作中
      if ([3, 36].includes(e.state)) {
        getCloseBodypose();
        proxy?.$modal.msgWarning(`请重新姿态识别`);
      } else {
        openBodypose(handcontroller_id);
      }
    }
    if (e?.type == 'bodyposecontroller_result') {
      let arr = e.data.result.keypoints;
      let result = [];
      for (let i = 0; i < arr.length; i += 3) {
        result.push(arr.slice(i, i + 2));
      }
      //console.log("result", result)
      bodyposeData.value = result;
      getCanvas(result);
    }
    if (e?.cmd == 'terminate_bodyposecontroller') {
      if (e?.code == 0) {
        closeWS();
        bodyposeState.value = false;
      }
    }
    if (e?.type == 'disconnect') {
      wsState.value = false;
    }
  });
};


/**
 * 查询姿态识别状态
*/
const getCheckBodypose = () => {
  let handcontroller_id = deviceInfo.value.handcontroller_id;
  //检查是否支持姿态识别
  checkBodypose(handcontroller_id);
};

/**
 * 关闭姿态识别
*/
const getCloseBodypose = () => {
  let handcontroller_id = deviceInfo.value.handcontroller_id;
  terminateBodypose(handcontroller_id);
  bodyposeState.value = false;
  speckCancel(); //停止播报
  setTimeout(() => {
    if (wsState.value) {
      closeWS();
    }
  }, 3000)
};


const getCanvas = (data) => {
  let leftA = data[6][1];
  let rightA = data[5][1];
  let leftB = data[10][1];
  let rightB = data[9][1];

  if (leftB > leftA || rightB > rightA) {
    myThrow.value = 1;
  } else {
    myThrow.value = 0;
  }

};

// 游戏主类的响应式状态
const gameState = reactive({
  version: '0.1',
  balls: [],
  hoops: [],
  texts: [],
  res: {},
  score: 0,
  started: false,
  gameOver: false,
  ballX: 320 / 2,
  ballY: 880,
  ballVel: 300,
  ballAngleVel: 100,
  ballAngle: 0,
  ballsShot: 1,
  ballCharge: 0,
  time: 60,
  toNextSecond: 1,
  sound: false,
  state: 'menu',
  click: false,
  canvas: null,
  ctx: null,
  animationFrameId: null,
  then: 0
});

// 篮筐类
class Hoop {
  constructor(x, y) {
    this.x = x;
    this.y = y;
    this.points = [
      { x: x + 7, y: y + 18 },
      { x: x + 141, y: y + 18 }
    ];
  }

  drawBack(ctx, game) {
    drawImage(
      ctx,
      game.res['/static/images/basketball/hoop.png'],
      this.x,
      this.y,
      0, 0, 148, 22, 0, 0, 0
    );
  }

  drawFront(ctx, game) {
    drawImage(
      ctx,
      game.res['/static/images/basketball/hoop.png'],
      this.x,
      this.y + 22,
      0, 22, 148, 178 - 22, 0, 0, 0
    );
  }
}

// 篮球类
class Ball {
  constructor(x, y) {
    this.x = x;
    this.y = y;
    this.vx = 0;
    this.vy = 0;
    this.speed = 100;
    this.canBounce = true;
    this.angle = 270;
    this.gravity = 0;
    this.falling = false;
    this.bounces = 0;
    this.scored = false;
    this.drawAngle = 0;
    this.angleVel = 100;
    this.solid = false;
    this.z = 1;
  }
  
  setAngle(angle) {
    this.angle = angle;
    this.vx = this.speed * Math.cos(this.angle * Math.PI / 180);
    this.vy = this.speed * Math.sin(this.angle * Math.PI / 180);
    this.gravity = 0;
  }

  shoot(power) {
    this.speed = power + Math.floor(Math.random() * 40);
    this.setAngle(270);
  }

  update(delta) {
    this.y += this.gravity * delta;
    this.gravity += 1500 * delta;
    this.x += this.vx * delta;
    this.y += this.vy * delta;

    if (this.vx > 500) this.vx = 500;
    if (this.vy > 500) this.vy = 500;

    if (this.y < 300) {
      this.solid = true;
    }

    if (this.gravity > this.speed) {
      this.falling = true;
    }

    if (this.x + 47 > 640) {
      this.vx = this.vx * -1;
      this.x = 640 - 47;
    }

    if (this.x - 47 < 0) {
      this.vx = this.vx * -1;
      this.x = 47;
    }

    this.drawAngle += this.angleVel * delta;
  }

  draw(ctx, game) {
    drawImage(
      ctx,
      game.res['/static/images/basketball/ball.png'],
      Math.floor(this.x - (93 / 2)),
      Math.floor(this.y - (93 / 2)),
      0, 0, 93, 93, 93 / 2, 93 / 2,
      this.drawAngle
    );
  }
}

// 弹出文字类
class PopText {
  constructor(string, x, y) {
    this.string = string;
    this.x = x;
    this.y = y;
    this.vy = -500;
    this.opacity = 1;
  }

  update(delta) {
    this.y += this.vy * delta;
    this.vy += 1000 * delta;

    if (this.vy > 0 && this.opacity > 0) {
      this.opacity -= 2 * delta;
    }

    if (this.opacity <= 0) {
      this.opacity = 0;
    }
  }

  draw(ctx, game) {
    ctx.globalAlpha = this.opacity;
    game.drawText(ctx, this.string, this.x + 15, this.y);
    ctx.globalAlpha = 1;
  }
}

// 工具函数：绘制旋转图像
function drawImage(ctx, image, x, y, sx, sy, w, h, rx, ry, a) {
  ctx.save();
  ctx.translate(x + rx, y + ry);
  ctx.rotate(a * Math.PI / 180);
  ctx.drawImage(image, sx, sy, w, h, -rx, -ry, w, h);
  ctx.restore();
}

// 事件处理函数
const handleMouseDown = () => {
  gameState.click = true;
};

const handleMouseUp = () => {
  gameState.click = false;
};

const handleTouchStart = () => {
  gameState.click = true;
};

const handleTouchEnd = () => {
  gameState.click = false;
};

// 游戏方法
const setupCanvas = () => {
  gameState.canvas = document.getElementById('canvas');
  gameState.canvas.width = 640;
  gameState.canvas.height = 960;
  gameState.ctx = gameState.canvas.getContext('2d');
};

const resizeToWindow = () => {
  const w = gameState.canvas.width / gameState.canvas.height;
  const h = window.innerHeight;
  const ratio = h * w;
  gameState.canvas.style.width = Math.floor(ratio) + 'px';
  gameState.canvas.style.height = Math.floor(h) + 'px';
};

const drawLoadingScreen = () => {
  const ctx = gameState.ctx;
  ctx.fillStyle = 'black';
  ctx.fillRect(0, 0, 960, 640);
  ctx.textAlign = 'center';
  drawText(ctx, 'Loading...', 640 / 2, 960 / 2, 40);
  ctx.textAlign = 'left';
};

const getResources = () => {
  const images = [
    '/static/images/basketball/background.png',
    '/static/images/basketball/title.png',
    '/static/images/basketball/ball.png',
    '/static/images/basketball/hoop.png'
  ];

  const sounds = [
    '/static/images/basketball/bounce_1.wav'
  ];

  return gameState.sound ? images.concat(sounds) : images;
};

const drawText = (ctx, string, x, y, size = 30) => {
  ctx.font = size + 'px Contrail One';
  ctx.lineWidth = 5;
  ctx.strokeStyle = 'white';
  ctx.strokeText(string, x, y);
  ctx.fillStyle = '#0098BF';
  ctx.fillText(string, x, y);
};

const playSound = (name) => {
  if (gameState.sound && gameState.res[name]) {
    gameState.res[name].currentTime = 0;
    gameState.res[name].play().catch(e => console.log('Sound play error:', e));
  }
};

const loadResources = () => {
  drawLoadingScreen();
  const resources = getResources();
  let loaded = 0;

  return new Promise((resolve) => {
    resources.forEach(resource => {
      const type = resource.split('.').pop();

      if (type === 'png') {
        const image = new Image();
        image.src = resource;
        image.addEventListener('load', () => {
          loaded++;
          gameState.res[resource] = image;
          if (loaded === resources.length) resolve();
        });
      } else if (['wav', 'mp3'].includes(type)) {
        const sound = new Audio();
        sound.src = resource;
        sound.addEventListener('canplaythrough', () => {
          loaded++;
          gameState.res[resource] = sound;
          if (loaded === resources.length) resolve();
        });
      }
    });
  });
};

const gameLoop = (timestamp) => {
  if (!gameState.then) gameState.then = timestamp;
  const delta = (timestamp - gameState.then) / 1000;

  // 更新游戏状态
  update(delta);
  // 绘制游戏
  draw();

  gameState.then = timestamp;
  gameState.animationFrameId = requestAnimationFrame(gameLoop);
};

const update = (delta) => {
  if (gameState.state === 'menu') {
    if (gameState.click) {
      gameState.state = 'play';
      gameState.click = false;
    }
    return;
  }

  if (gameState.state === 'play') {
    // 更新篮球横向移动
    gameState.ballX += gameState.ballVel * delta;
    if (gameState.ballX > 640 - 93) {
      gameState.ballVel = -gameState.ballVel;
      gameState.ballX = 640 - 93;
    }
    if (gameState.ballX < 0) {
      gameState.ballVel = -gameState.ballVel;
      gameState.ballX = 0;
    }

    // 更新所有篮球
    for (let i = gameState.balls.length - 1; i >= 0; i--) {
      const ball = gameState.balls[i];

      if (ball.falling) {
        // 检测与篮筐的碰撞
        gameState.hoops.forEach(hoop => {
          const cx = hoop.x + (148 / 2);
          const cy = hoop.y + 40;
          const dx = cx - ball.x;
          const dy = cy - ball.y;
          const mag = Math.sqrt(dx * dx + dy * dy);

          if (mag < 47 + 5 && !ball.scored) {
            ball.setAngle(90);
            gameState.score += 100;
            gameState.texts.push(new PopText('+ 100', hoop.x, hoop.y));
            ball.scored = true;
          }

          if (!ball.scored) {
            hoop.points.forEach(point => {
              const dx = point.x - ball.x;
              const dy = point.y - ball.y;
              const mag = Math.sqrt(dx * dx + dy * dy);
              const angle = Math.atan2(point.y - ball.y, point.x - ball.x);

              if (mag > 47 + 7 && !ball.canBounce) {
                ball.canBounce = true;
              }

              if (mag < 47 + 5 && ball.canBounce) {
                playSound('/static/images/basketball/bounce_1.wav');
                ball.bounces++;
                ball.setAngle((angle * 180 / Math.PI) + 180 + Math.floor(Math.random() * 5) - Math.floor(Math.random() * 5));
                ball.bounces = Math.min(ball.bounces, 3);

                const deg = angle * 180 / Math.PI;
                if (deg > 0 && deg < 180) {
                  ball.gravity = 750 + (ball.bounces * 50);
                }

                ball.angleVel = -ball.angleVel;
                ball.canBounce = false;
              }
            });
          }
        });
      }

      ball.update(delta);

      // 移除超出屏幕的球
      if (ball.y > 960) {
        gameState.ballX = ball.x;
        gameState.balls.splice(i, 1);
      }
    }

    // 更新计时器
    if (gameState.time > 0) {
      gameState.toNextSecond -= delta;
      if (gameState.toNextSecond <= 0) {
        gameState.time--;
        gameState.toNextSecond = 1;
      }
    } else {
      gameState.state = 'over';
    }

    // 处理投篮逻辑
    if (gameState.click && gameState.ballY <= 950) {
      if (gameState.balls.length < 1) {
        const ball = new Ball(gameState.ballX + (93 / 2), gameState.ballY);
        ball.drawAngle = gameState.ballAngle;
        ball.shoot(1480);
        gameState.balls.push(ball);
        gameState.ballY = 961;
      }
    }

    // 重置篮球位置
    if (gameState.balls.length < 1 && gameState.ballY > 880) {
      gameState.ballY -= 100 * delta;
    }

    if (!gameState.click) {
      gameState.ballsShot = 0;
    }

    // 更新弹出文字
    gameState.texts.forEach((text, i) => {
      text.update(delta);
      if (text.opacity <= 0) {
        gameState.texts.splice(i, 1);
      }
    });

    // 更新篮球角度
    gameState.ballAngle += 100 * delta;
  }

  // 游戏结束状态处理
  if (gameState.state === 'over' && gameState.click) {
    gameState.gameOver = false;
    gameState.started = false;
    gameState.score = 0;
    gameState.time = 60;
    gameState.balls = [];
    gameState.state = 'menu';
    gameState.click = false;
  }
};

const draw = () => {
  const ctx = gameState.ctx;
  if (!ctx) return;

  // 绘制背景
  if (gameState.res['/static/images/basketball/background.png']) {
    ctx.drawImage(gameState.res['/static/images/basketball/background.png'], 0, 0);
  }

  // 绘制菜单状态
  if (gameState.state === 'menu') {
    if (gameState.res['/static/images/basketball/title.png']) {
      ctx.drawImage(
        gameState.res['/static/images/basketball/title.png'],
        640 / 2 - (492 / 2),
        100
      );
    }
    ctx.textAlign = 'center';
    drawText(ctx, '请做投篮动作开始游戏', 640 / 2, 520, 40);
    drawText(ctx, '(?) Tap to throw ball. Try to make as many hoops before the time runs out!', 640 / 2, 940, 20);
    ctx.textAlign = 'left';
  }

  // 绘制游戏状态
  if (gameState.state === 'play') {
    // 绘制篮筐背景
    gameState.hoops.forEach(hoop => hoop.drawBack(ctx, gameState));

    // 绘制正在下落的球
    gameState.balls.forEach(ball => {
      if (ball.falling) ball.draw(ctx, gameState);
    });

    // 绘制篮筐前景
    gameState.hoops.forEach(hoop => hoop.drawFront(ctx, gameState));

    // 绘制未下落的球
    gameState.balls.forEach(ball => {
      if (!ball.falling) ball.draw(ctx, gameState);
    });

    // 绘制当前可投掷的球
    if (gameState.balls.length < 1 && gameState.res['/static/images/basketball/ball.png']) {
      drawImage(
        ctx,
        gameState.res['/static/images/basketball/ball.png'],
        gameState.ballX,
        gameState.ballY,
        0, 0, 93, 93,
        45, 45,
        gameState.ballAngle
      );
    }

    // 绘制分数和时间
    drawText(ctx, 'Score: ' + gameState.score, 45, 70, 50);
    drawText(ctx, 'Time: ' + gameState.time, 435, 70, 50);

    // 绘制弹出文字
    gameState.texts.forEach(text => text.draw(ctx, { drawText }));
  }

  // 绘制游戏结束界面
  if (gameState.state === 'over') {
    ctx.textAlign = 'center';
    drawText(ctx, 'Game Over', 640 / 2, 200, 80);
    drawText(ctx, 'Score: ' + gameState.score, 640 / 2, 400, 50);
    drawText(ctx, 'Click to Continue', 640 / 2, 800, 50);
    ctx.textAlign = 'center';
  }
};

// 初始化游戏
const initGame = async () => {
  setupCanvas();
  resizeToWindow();
  window.addEventListener('resize', resizeToWindow);

  await loadResources();

  // 添加篮筐
  gameState.hoops = [
    new Hoop(110, 520),
    new Hoop(640 - 148 - 110, 520),
    new Hoop(640 / 2 - (148 / 2), 260)
  ];

  // 开始游戏循环
  gameState.animationFrameId = requestAnimationFrame(gameLoop);
};



// 生命周期钩子
onMounted(() => {
  initGame();
});

onBeforeMount(() => {
  getInit();
});

onBeforeUnmount(() => {
  closeWS();
  if (gameState.animationFrameId) {
    cancelAnimationFrame(gameState.animationFrameId);
  }
  window.removeEventListener('resize', resizeToWindow);
});
</script>

<style scoped>
.game-container {
  width: 100vw;
  height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
  background-color: #000;
  overflow: hidden;
}

#canvas {
  display: block;
  touch-action: none;
  user-select: none;
}
</style>