<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import { BLOCK_SIZE, STAGE_WIDTH, STAGE_HEIGHT, type Block } from './level/def'
import createLevel from './level'

import useTime from './util/useTime'
import useInput from './util/useInput'
import usePlayer from './util/usePlayer'

const { updateTime, timeOfDay, clock } = useTime()
const { player, direction, dx, dy } = usePlayer()
const { inputX, inputY, running, digging, paused, help } = useInput()
const level = createLevel(STAGE_WIDTH + 2, STAGE_HEIGHT + 2)

let animationFrame = 0
let lastTick = 0

let x = ref(0)
let y = ref(12)
const floorX = computed(() => Math.floor(x.value))
const floorY = computed(() => Math.floor(y.value))
const tx = computed(() => (x.value - floorX.value) * -BLOCK_SIZE)
const ty = computed(() => (y.value - floorY.value) * -BLOCK_SIZE)
const rows = computed(() => level.grid(floorX.value, floorY.value))

type Surroundings = {
  at: Block,
  left: Block,
  right: Block,
  up: Block,
  down: Block,
}
const surroundings = computed<Surroundings>(() => {
  const px = player.x
  const py = player.y
  const row = rows.value

  return {
    at: row[py][px],
    left: row[py][px - 1],
    right: row[py][px + 1],
    up: row[py - 1][px],
    down: row[py + 1][px],
  }
})
const blocked = computed(() => {
  const { left, right, up, down } = surroundings.value
  return {
    left: !left.walkable,
    right: !right.walkable,
    up: !up.walkable,
    down: !down.walkable,
  }
})

function dig() {
  console.warn('digging not yet implemented')
}

let lastTimeUpdate = 0

const move = (thisTick: number): void => {
  animationFrame = requestAnimationFrame(move)

  // do nothing when paused
  if (paused.value) return

  const tickDelta = thisTick - lastTick
  lastTimeUpdate += tickDelta
  // update in-game time every 60ms by 0.1
  // then a day needs 10000 updates, and it takes about 10 minutes
  if (lastTimeUpdate > 60) {
    updateTime()
    lastTimeUpdate = 0
  }

  player.vx = inputX.value
  player.vy = inputY.value

  if (inputX.value) player.lastDir = inputX.value

  let dx_ = dx.value
  let dy_ = dy.value

  if (running.value) dx_ *= 2

  if (dx_ > 0 && blocked.value.right) dx_ = 0
  else if (dx_ < 0 && blocked.value.left) dx_ = 0

  if (dy_ > 0 && blocked.value.down) dy_ = 0
  else if (dy_ < 0 && blocked.value.up) dy_ = 0

  if (!inputY.value && digging.value) {
    dx_ = 0
    dig()
  }

  const optimal = 16 // 16ms per tick => 60 FPS
  const movementMultiplier = (tickDelta / optimal) * 2

  x.value += dx_ * movementMultiplier
  y.value += dy_ * movementMultiplier
  lastTick = thisTick
}

onMounted(() => {
  lastTick = performance.now()
  move(lastTick)
})
</script>

<template>
  <div id="field" :class="timeOfDay">

    <div id="blocks" :style="{transform: `translate(${tx}px, ${ty}px)`}">
      <template v-for="(row, y) in rows">
        <div v-for="(block, x) in row" class="block" :class="[block.type]" />
      </template>
    </div>

    <div id="player" :class="direction" />
    <div id="level-indicator">
      x:{{ floorX }}, y:{{ floorY }}
      <template v-if="paused">(PAUSED)</template>
      <template v-else>({{ clock }})</template>
      <div>{{ player.vx }}, {{ player.vy }}</div>
    </div>

    <div id="help" v-if="help">
      <header>
        <h1>How to play</h1>
      </header>

      <section>
        <h2>Walk around: WASD or Arrow Keys</h2>
        <p>A / Left: walk left</p>
        <p>D / Right: walk right</p>
        <p>W / Up: jump or climb up</p>
        <p>S / Down: climb down</p>
        <p>Hold Shift, to run.</p>
      </section>
      <section>
        <h2>Dig Blocks: Left Mouse Key</h2>
        <p>To dig a block, click on it with your left mouse key. Only adjacent blocks can be digged.</p>
        <p><i>(not implemented, yet)</i></p>
      </section>
      <section>
        <h2>Build / Set Blocks: Right Mouse Key</h2>
        <p>To set a block, right click an empty position close to you.</p>
        <p><i>(not implemented, yet)</i></p>
      </section>
      <section>
        <h2>Inventory: I</h2>
        <p>Press I to open the inventory and use the mouse to select an item. This item can then be put into the world with a right click.</p>
        <p><i>(not implemented, yet)</i></p>
      </section>
      <p>&nbsp;</p>
      <p>&nbsp;</p>
      <p>&nbsp;</p>
    </div>
  </div>
</template>
