/* global performance */
import Screen from './screen'
import Input from './input'
import { ShaderManager } from './shader'

let gl

class Engine {
  constructor () {
    this.screen = Screen
    this.input = new Input(this.screen.gl.canvas)
    this.shaders = new ShaderManager()
    this.running = false

    // Queues
    this.rst = []
    this.ust = []

    this.frameTime = 0
    this.frameCount = 0
    this.fps = 0

    gl = this.screen.gl
  }

  static get GL () {
    return gl
  }

  static get gl () {
    return gl
  }

  get gl () {
    return this.screen.gl
  }

  prepare () {
    // Set clear color to black, fully opaque
    gl.clearColor(0.0, 0.0, 0.0, 1.0)

    // Clear the color buffer with specified clear color
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)

    // Enable depth testing
    gl.enable(gl.DEPTH_TEST)

    // Enable back-face culling
    gl.enable(gl.CULL_FACE)
    gl.cullFace(gl.BACK)
  }

  render () {
    // Render functions
    for (let i in this.rst) {
      this.rst[i](gl)
    }
  }

  update (dt) {
    // Updates
    for (let i in this.ust) {
      this.ust[i](dt)
    }

    this.input.update()
  }

  step () {
    this.running && window.requestAnimationFrame(() => this.step())

    this.render(this.gl)

    let ts = performance.now()
    let timeDiff = ts - this.frameTime // time difference in milliseconds
    this.frameCount++

    if (timeDiff > 0) {
      this.fps = Math.floor(this.frameCount / timeDiff * 1000)
      this.frameCount = 0
      this.frameTime = ts
    }

    this.update(timeDiff / 1000)
  }

  addRenderFunction (fn) {
    this.rst.push(fn)
  }

  addUpdateFunction (fn) {
    this.ust.push(fn)
  }

  startGameLoop () {
    if (this.running) throw new Error('Game Loop is already running!')

    this.running = true
    this.step()
  }
}

export default new Engine()
