import type { JointPoint, Skeleton } from './types'

export const BASE_SKELETON: Skeleton = {
  nose: [0.5, 0.16],
  lSh: [0.42, 0.26],
  rSh: [0.58, 0.26],
  lEl: [0.36, 0.36],
  rEl: [0.64, 0.36],
  lWr: [0.33, 0.5],
  rWr: [0.67, 0.5],
  lHp: [0.45, 0.48],
  rHp: [0.55, 0.48],
  lKn: [0.45, 0.66],
  rKn: [0.56, 0.66],
  lAn: [0.44, 0.86],
  rAn: [0.57, 0.86],
}

const ARM_UPPER = Math.sqrt(
  (BASE_SKELETON.lSh[0] - BASE_SKELETON.lEl[0]) ** 2 +
  (BASE_SKELETON.lSh[1] - BASE_SKELETON.lEl[1]) ** 2,
)
const ARM_LOWER = Math.sqrt(
  (BASE_SKELETON.lEl[0] - BASE_SKELETON.lWr[0]) ** 2 +
  (BASE_SKELETON.lEl[1] - BASE_SKELETON.lWr[1]) ** 2,
)
const LEG_UPPER = Math.sqrt(
  (BASE_SKELETON.lHp[0] - BASE_SKELETON.lKn[0]) ** 2 +
  (BASE_SKELETON.lHp[1] - BASE_SKELETON.lKn[1]) ** 2,
)
const LEG_LOWER = Math.sqrt(
  (BASE_SKELETON.lKn[0] - BASE_SKELETON.lAn[0]) ** 2 +
  (BASE_SKELETON.lKn[1] - BASE_SKELETON.lAn[1]) ** 2,
)

export function cloneSkeleton(input: Skeleton): Skeleton {
  return {
    nose: [...input.nose],
    lSh: [...input.lSh],
    rSh: [...input.rSh],
    lEl: [...input.lEl],
    rEl: [...input.rEl],
    lWr: [...input.lWr],
    rWr: [...input.rWr],
    lHp: [...input.lHp],
    rHp: [...input.rHp],
    lKn: [...input.lKn],
    rKn: [...input.rKn],
    lAn: [...input.lAn],
    rAn: [...input.rAn],
  }
}

function constrainBone(a: JointPoint, b: JointPoint, len: number): JointPoint {
  const dx = b[0] - a[0]
  const dy = b[1] - a[1]
  const dist = Math.sqrt(dx * dx + dy * dy)
  if (dist < 0.001) return [a[0] + len, a[1]]
  const scale = len / dist
  return [a[0] + dx * scale, a[1] + dy * scale]
}

export function animateIdleSkeleton(target: Skeleton, t: number) {
  const beat = t * 2.2
  const bounce = Math.abs(Math.sin(beat)) * 0.04
  const sway = Math.sin(beat * 0.5) * 0.04
  const headTilt = Math.sin(beat * 0.5) * 0.02

  target.nose = [BASE_SKELETON.nose[0] + sway + headTilt, BASE_SKELETON.nose[1] - bounce * 1.2]
  target.lSh = [BASE_SKELETON.lSh[0] + sway, BASE_SKELETON.lSh[1] - bounce]
  target.rSh = [BASE_SKELETON.rSh[0] + sway, BASE_SKELETON.rSh[1] - bounce]
  target.lHp = [BASE_SKELETON.lHp[0] + sway * 0.7, BASE_SKELETON.lHp[1] - bounce * 0.5]
  target.rHp = [BASE_SKELETON.rHp[0] + sway * 0.7, BASE_SKELETON.rHp[1] - bounce * 0.5]

  const lSwing = Math.sin(beat * 0.5) * 0.1
  const rSwing = Math.sin(beat * 0.5 + Math.PI) * 0.1
  const lBob = Math.sin(beat) * 0.03
  const rBob = Math.sin(beat + Math.PI) * 0.03

  target.lEl = [BASE_SKELETON.lEl[0] + lSwing + sway, BASE_SKELETON.lEl[1] + lBob - bounce]
  target.rEl = [BASE_SKELETON.rEl[0] + rSwing + sway, BASE_SKELETON.rEl[1] + rBob - bounce]
  target.lWr = [BASE_SKELETON.lWr[0] + lSwing * 1.6 + sway, BASE_SKELETON.lWr[1] + lBob * 1.3 - bounce]
  target.rWr = [BASE_SKELETON.rWr[0] + rSwing * 1.6 + sway, BASE_SKELETON.rWr[1] + rBob * 1.3 - bounce]

  const legPhase = Math.sin(beat * 0.5)
  const kneeBend = Math.abs(Math.sin(beat)) * 0.03

  target.lKn = [
    BASE_SKELETON.lKn[0] + legPhase * 0.03 + sway * 0.5,
    BASE_SKELETON.lKn[1] - kneeBend - bounce * 0.3,
  ]
  target.rKn = [
    BASE_SKELETON.rKn[0] - legPhase * 0.03 + sway * 0.5,
    BASE_SKELETON.rKn[1] - kneeBend * 0.5 - bounce * 0.3,
  ]
  target.lAn = [
    BASE_SKELETON.lAn[0] + legPhase * 0.04 + sway * 0.3,
    BASE_SKELETON.lAn[1] + (legPhase > 0 ? -0.02 : 0),
  ]
  target.rAn = [
    BASE_SKELETON.rAn[0] - legPhase * 0.04 + sway * 0.3,
    BASE_SKELETON.rAn[1] + (legPhase < 0 ? -0.02 : 0),
  ]

  target.lEl = constrainBone(target.lSh, target.lEl, ARM_UPPER)
  target.lWr = constrainBone(target.lEl, target.lWr, ARM_LOWER)
  target.rEl = constrainBone(target.rSh, target.rEl, ARM_UPPER)
  target.rWr = constrainBone(target.rEl, target.rWr, ARM_LOWER)
  target.lKn = constrainBone(target.lHp, target.lKn, LEG_UPPER)
  target.lAn = constrainBone(target.lKn, target.lAn, LEG_LOWER)
  target.rKn = constrainBone(target.rHp, target.rKn, LEG_UPPER)
  target.rAn = constrainBone(target.rKn, target.rAn, LEG_LOWER)
}
