// thanks to https://stackoverflow.com/a/74129799
const { cos, sin, sqrt, acos, atan, atan2, abs, PI } = Math
const clamp = (a, b, x) => x < a ? a : x > b ? b : x
const cvs = document.createElement('canvas')
cvs.style.cssText = ` background: #00000000; `
const primaryCol = getComputedStyle(document.body).getPropertyValue('--secondary');
const secondCol = getComputedStyle(document.body).getPropertyValue('--accent');
const ctx = cvs.getContext('2d')

const RADIUS = 150
const NB_SECTIONS = 6
const LINE_WIDTH = 3

const SCALE = devicePixelRatio
const width = RADIUS * 2 + 20
const height = RADIUS * 2 + 20
cvs.width = width * SCALE
cvs.height = height * SCALE
cvs.style.width = `${width}px`
cvs.style.height = `${height}px`

document.getElementById("greeter-sim").appendChild(cvs)

const vec = (x = 0, y = 0, z = 0) => ({ x, y, z })

vec.set = (o, x = 0, y = 0, z = 0) => {
  o.x = x
  o.y = y
  o.z = z
  return o
}

const X = vec(1, 0, 0)
const Y = vec(0, 1, 0)
const Z = vec(0, 0, 1)

// orientation of camera
let theta, phi

// project v to the camera, output to o
function project(o, { x, y, z }) {
  let ct = cos(theta), st = sin(theta)
  let cp = cos(phi), sp = sin(phi)
  let a = x * ct + y * st
  return vec.set(o, y * ct - x * st, cp * z - sp * a, cp * a + sp * z)
}

// draw camera-facing section of sphere with normal v and offset o (-1 < o < 1)
const _p = vec()
function draw_section(n, o = 0) {
  let { x, y, z } = project(_p, n) // project normal on camera
  let a = atan2(y, x)           // angle of projected normal -> angle of ellipse
  let ry = sqrt(1 - o * o)       // radius of section -> y-radius of ellipse
  let rx = ry * abs(z)           // x-radius of ellipse
  let W = sqrt(x * x + y * y)
  let sa = acos(clamp(-1, 1, o * (1 / W - W) / rx)) // ellipse start angle
  let sb = z > 0 ? 2 * PI - sa : - sa                    // ellipse end angle

  ctx.beginPath()
  ctx.ellipse(x * o * RADIUS, y * o * RADIUS, rx * RADIUS, ry * RADIUS, a, sa, sb, z <= 0)
  ctx.stroke()
}

const _n = vec()
function draw_arcs() {
    for (let i = NB_SECTIONS; i--;) {
      let a = i / NB_SECTIONS * Math.PI
      draw_section(vec.set(_n, cos(a), sin(a)))
    }

  for (let i = NB_SECTIONS - 1; i--;) {
    let a = (i + 1) / NB_SECTIONS * Math.PI
    draw_section(Z, cos(a))
      //draw_section(X, cos(a))
      //draw_section(Y, cos(a))
  }
}


ctx.lineCap = 'round'
ctx.scale(SCALE, SCALE)

function render() {
  requestAnimationFrame(render)

  theta = performance.now() / 24000 * PI
  phi = cos(performance.now() / 12000 * PI)

  // 1. change the basis of the canvas
  ctx.save()
  ctx.clearRect(0, 0, width, height)
  ctx.translate(width >> 1, height >> 1)
  ctx.scale(1, -1)

  // 2. draw back arcs
  ctx.lineWidth = LINE_WIDTH / 2
  ctx.strokeStyle = secondCol
  ctx.scale(-1, -1) // the trick is to flip the canvas
  draw_arcs()
  ctx.scale(-1, -1)

  // 3. draw sphere border
  ctx.strokeStyle =  primaryCol
  ctx.lineWidth = LINE_WIDTH + 2
  ctx.beginPath()
  ctx.arc(0, 0, RADIUS, 0, 2 * Math.PI)
  ctx.stroke()

  // 4. draw front arcs
  ctx.lineWidth = LINE_WIDTH
  ctx.strokeStyle =  primaryCol
  draw_arcs()

  ctx.restore()
}

requestAnimationFrame(render)
