<script>
const Fifo = require('fifo')
const SCREEN_WIDTH = 256;
const SCREEN_HEIGHT = 240;
const BUFFER_FRAMES = 120;
const SAMPLE_REDUCTION = 4;
const MAX_BUFFER = 0; 1200;
const WORKING_MEMORY = 0x800;
const { cartesianToPolar:c2p, polarToCartesian:p2c, findMemory } = require('../common/util')
window.findMemory = findMemory
export default {
  data: () => ({
    gameState: {},
    gameStateString: '',
    memory: new Uint8Array(WORKING_MEMORY),
    watch: [
      0x0086
    ]
  }),
  beforeDestroy() {
    console.log('beforeDestroy()')
    this.stop = true
    this.ws.close()
  },
  mounted() {
    console.log('mounted()')
    const buffer = new Fifo()
    const url = new URL('game', location.href)
    url.protocol = url.protocol === 'https:' ? 'wss:' : 'ws:'
    const connect = () => {
      this.ws = new WebSocket(url)
      this.ws.binaryType = 'arraybuffer'
      this.ws.addEventListener('message', msg => {
        if (typeof msg.data === 'string') {
          this.gameState = JSON.parse(msg.data)
          this.gameStateString = JSON.stringify(this.gameState.enemyVision, null, 2)
            .replace(/\{[^\{\}\[\]]*\}/g, x => x.replace(/\s+/g, ' '))
            .replace(/\[[^\{\}\[\]]*\]/g, x => x.replace(/\s+/g, ' '))

        } else {
          //buffer.push(msg.data)
          //if (buffer.length === MAX_BUFFER) buffer.shift()
          // if (!timer) {
          //   timer = true
            this.renderFrame(msg.data)
            this.renderDebug(this.gameState)
          //}
        }
      })
      this.ws.addEventListener('close', () => {
        if (!this.stop) setTimeout(connect, 1000)
      })
    }
    connect()
    this.canvas = this.$refs.canvas
    this.context = this.canvas.getContext('2d')
    this.imageData = this.context.getImageData(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)
    this.memoryCanvas = this.$refs.memoryCanvas
    this.memoryContext = this.memoryCanvas.getContext('2d')
    this.memoryImageData = this.memoryContext.getImageData(0, 0, this.memoryCanvas.getAttribute('width'), this.memoryCanvas.getAttribute('height'))
    this.buf = new ArrayBuffer(this.imageData.data.length);
    // Get the canvas buffer in 8bit and 32bit
    this.buf8 = new Uint8ClampedArray(this.buf);
    this.buf32 = new Uint32Array(this.buf);

    this.timer = null

    this.keyMap = {
      a: /*BUTTON_B*/ 1,
      s: /*BUTTON_A*/ 0,
      '\\': /*BUTTON_SELECT*/ 2,
      Enter: /*BUTTON_START*/ 3,
      ArrowUp: /*BUTTON_UP*/ 4,
      ArrowDown: /*BUTTON_DOWN*/ 5,
      ArrowLeft: /*BUTTON_LEFT*/ 6,
      ArrowRight: /*BUTTON_RIGHT*/ 7
    }

    this.key = (ev, pressed) => {
      if (this.keyMap.hasOwnProperty(ev.key)) {
        const button = this.keyMap[ev.key]
        if (this.ws && this.ws.readyState === WebSocket.OPEN) {
          this.ws.send(JSON.stringify({
            type: 'button',
            button,
            pressed
          }))
        } else {
          console.warn('Not connected')
        }
      }
    }

    document.addEventListener('keydown', (ev) => this.key(ev, true))
    document.addEventListener('keyup', (ev) => this.key(ev, false))
  },
  methods: {
    renderDebug(data) {
      window.data = data
      this.context.save()
      // this.context.translate(8, 8)
      this.context.translate(data.x, data.y + 32)
      // this.context.beginPath()
      // this.context.fillStyle = '#080'
      // //this.context.fillRect(data.hitbox.x1, data.hitbox.y1, data.hitbox.x2 - data.hitbox.x1, data.hitbox.y2 - data.hitbox.y1)
      // this.context.arc(0, 0, 8, 0, 2 * Math.PI)
      // this.context.fill()

      // data.enemies.forEach(enemy => {
      //   if (enemy && enemy.hitbox.y2 > enemy.hitbox.y1) {
      //     this.context.beginPath()
      //     this.context.fillStyle = '#800'
      //     this.context.arc(enemy.x, enemy.y, 8, 0, 2 * Math.PI)
      //     //this.context.fillRect(enemy.hitbox.x1, enemy.hitbox.y1, enemy.hitbox.x2 - enemy.hitbox.x1, enemy.hitbox.y2 - enemy.hitbox.y1)
      //     this.context.fill()
      //   }
      // })

      const renderVision = (collection, color) => {
        const arc = (Math.PI * 2 / data.slices)
        this.context.save()
        this.context.translate((data.hitbox.x2 - data.hitbox.x1) / 2, (data.hitbox.y2 - data.hitbox.y1) / -2)
        for (let i = 0; i < data.slices; i++) {
          const r = collection[i]
          const {x, y} = p2c({
            r: (1 - r) * 30,
            t: arc * i + arc / 2 - Math.PI
          })
          this.context.beginPath()
          this.context.fillStyle = color
          this.context.arc(x, y, 2, 0, 2 * Math.PI)
          //this.context.fillRect(enemy.hitbox.x1, enemy.hitbox.y1, enemy.hitbox.x2 - enemy.hitbox.x1, enemy.hitbox.y2 - enemy.hitbox.y1)
          this.context.fill()

        }
        this.context.restore()
      }
      renderVision(data.enemyVision, 'red')
      renderVision(data.solidVision, 'blue')
      renderVision(data.breakableVision, 'yellow')
      renderVision(data.magicVision, 'green')

      const renderHitboxes = (collection, color) => {
        this.context.strokeStyle = color
        this.context.beginPath()
        collection.forEach(entity => {
          if (entity) this.context.rect(entity.hitbox.x1, entity.hitbox.y1, entity.hitbox.x2 - entity.hitbox.x1, entity.hitbox.y2 - entity.hitbox.y1)
        })
        this.context.stroke()
      }

      renderHitboxes(this.gameState.blocks, 'blue')      
      renderHitboxes(data.enemies, 'red')
      renderHitboxes([data], 'yellow')
      // this.gameState.blocks.forEach(({x, y, v}) => {

      //   // v = v.toString(16).padStart(2)
      //   this.context.rect(x, y, 16, 16)
//        for (let xo = -512; xo <= 512; xo += 512) {
        // const xo = 0
        //   this.context.rect(x * 16 + 0.5 - this.gameState.levelX % 512 + xo, y * 16 + 0.5 - data.y, 16, 16)
        //   this.context.fillStyle = 'black'
        //   this.context.fillText(v, x * 16 + 0.5 - this.gameState.levelX % 512 + xo + 1, y * 16 + 0.5 - data.y + 10)
        //   this.context.fillStyle = 'black'
        //   this.context.fillText(v, x * 16 + 0.5 - this.gameState.levelX % 512 + xo - 1, y * 16 + 0.5 - data.y + 7)
        //   this.context.fillStyle = 'black'
        //   this.context.fillText(v, x * 16 + 0.5 - this.gameState.levelX % 512 + xo + 1, y * 16 + 0.5 - data.y + 7)
        //   this.context.fillStyle = 'black'
        //   this.context.fillText(v, x * 16 + 0.5 - this.gameState.levelX % 512 + xo - 1, y * 16 + 0.5 - data.y + 10)
        //   this.context.fillStyle = '#aaaa00'
        //   this.context.fillText(v, x * 16 + 0.5 - this.gameState.levelX % 512 + xo, y * 16 + 0.5 - data.y + 9)
//        }
      // })
      // this.context.stroke();
      // this.context.strokeStyle = null
      this.context.restore()
    },
    renderFrame(data) {
      //const data = buffer.shift()
      if (data) {
        const palette = new Uint32Array(data, 0, 256 * 4)
        const pixels = new Uint8Array(data, 256 * 4, SCREEN_WIDTH * SCREEN_HEIGHT)
        const memory = new Uint8Array(data, 256 * 4 + SCREEN_WIDTH * SCREEN_HEIGHT, WORKING_MEMORY)
        Object.assign(this.memory, memory)
        window.memory = this.memory
        for (let i = 0; i < pixels.length; i++) {
          this.buf32[i] = 0xff000000 | palette[pixels[i]]
        }
        this.imageData.data.set(this.buf8)
        for (let i = 32 * 40; i < memory.length; i++) {
          const m = memory[i]
          for (let x = 0; x < 4; x++) {
            this.memoryImageData.data[i * 4 + x - 32 * 40] = m
          }
        }
        this.context.putImageData(this.imageData, 0, 0)
        this.memoryContext.putImageData(this.memoryImageData, 0, 0)
      }


      //const ms = Math.max(-0.8 * buffer.length + 160, 0)
      //timer = setTimeout(renderFrame, ms)
      //requestAnimationFrame(renderFrame)
      /* https://www.calculator.net/slope-calculator.html?type=1&x11=0&y11=500&x12=100&y12=16&x=12&y=7
        l = buffer.length
        l = 0, sleep 500
        l = 100, sleep 1000/60
        l = 200, sleep more
      */
    }
  },
  destroyed() {
    console.log('destroyed()')
    if (this.ws) this.ws.close()
  }
}
</script>

<template>
    <v-app>
      <v-content>
        <v-toolbar color="primary">
          <v-toolbar-side-icon />
          <v-toolbar-title>Super Mario Bros</v-toolbar-title>
        </v-toolbar>
        <v-container>
          <canvas style="zoom: 2;" ref="canvas" width="256" height="240" />
          <canvas style="zoom: 4;" ref="memoryCanvas" width="16" height="128" />
          <pre>{{gameStateString}}</pre>
          <ul>
            <!-- "0x0069", "0x0075", "0x0093", "0x0109", "0x0113", "0x0114", "0x0115", "0x0118", "0x0119", "0x0130", "0x0163", "0x0164", "0x0168", "0x0184", "0x0185", "0x0186", "0x0187", "0x0190", "0x0191", "0x0202", "0x0215", "0x0216", "0x0239", "0x0246", "0x0269", "0x0281", "0x0282", "0x0283", "0x0289", "0x0637", "0x0641", "0x0642", "0x0645", "0x0646", "0x0791", "0x0987", "0x0988", "0x0989", "0x0996", "0x0997", "0x1002", "0x1003", "0x1008", "0x1050", "0x1052", "0x1084", "0x1178", "0x1180", "0x1181", "0x1182", "0x1183", "0x1212", "0x1213", "0x1214", "0x1215", "0x1220", "0x1221", "0x1222", "0x1223", "0x1286", "0x1302", "0x1318", "0x1334", "0x1350", "0x1366", "0x1382", "0x1398", "0x1563", "0x1564", "0x1565", "0x1578", "0x1579", "0x1580", "0x1581", "0x1593", "0x1594", "0x1595", "0x1596", "0x1597", "0x1611", "0x1612", "0x1613", "0x1623", "0x1624", "0x1627", "0x1628", "0x1629", "0x1636", "0x1638", "0x1639", "0x1640", "0x1643", "0x1653", "0x1654", "0x1655", "0x1656", "0x1659", "0x1675", "0x1676", "0x1677" -->
            <li> x, y: {{gameState.x}}, {{gameState.y}}</li>
            <li v-for="(key, index) in watch" :key="index">
              {{key.toString(16)}}: {{memory[key]}}
            </li>
            <li>0x006d (Level x): {{gameState.levelX}}</li>
            <li>0x0491 (Enemy collision): {{memory[0x0491]}}</li>
            <li>0x04AC: {{memory[0x04AC]}}</li>
            <li>0x04AC: {{memory[0x04AC]}}</li>
            <li>0x0750: {{memory[0x0750]}}</li>
          </ul>
          <!-- <div style="position: relative;">
            <div v-for="block in gameState.blocks"
              :key="gameState.blocks.indexOf(block)"
              :style="{
                display: 'block',
                width: '4px',
                height: '4px',
                left: (block.x * 4) + 'px',
                top: (block.y * 4) + 'px',
                position: 'absolute',
                backgroundColor: 'brown'
              }"
            >
            </div>
          </div> -->
        </v-container>
      </v-content>
    </v-app>
</template>

<style scoped>
canvas {
  image-rendering: pixelated;
}
</style>
