export type InputState = {
  key: string,
  shift: boolean,
  ctrl: boolean,
  meta: boolean,
  pressed: Set<string>,
  prevPressed: Set<string>,
  justPressed: Set<string>,
  justReleased: Set<string>
}

export class GameContext {
  constructor(public ctx: CanvasRenderingContext2D) { }

  width = 960
  height = 540

  clear(color?: string) {
    if (color)
      this.rectfill(0, 0, this.width, this.height, color)
    else
      this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height)
  }

  text(msg: string, x: number, y: number, color = "black", size = 16, font = "C64ProMono") {
    const c = this.ctx
    c.save()
    c.fillStyle = color
    c.font = `${size}px ${font}`
    c.textBaseline = "top"
    c.fillText(msg, x, y)
    c.restore()
  }

  centerText(msg: string, color = "black", size = 16, font = "C64ProMono") {
    const c = this.ctx
    c.save()
    c.fillStyle = color
    c.font = `${size}px ${font}`
    c.textBaseline = "middle"
    c.textAlign = "center"
    c.fillText(msg, this.width / 2, this.height / 2)
    c.restore()
  }

  centerTextX(msg: string, y: number, color = "black", size = 16, font = "C64ProMono") {
    const c = this.ctx
    c.save()
    c.fillStyle = color
    c.font = `${size}px ${font}`
    c.textBaseline = "middle"
    c.textAlign = "center"
    c.fillText(msg, this.width / 2, y)
    c.restore()
  }

  centerTextY(msg: string, x: number, color = "black", size = 16, font = "C64ProMono") {
    const c = this.ctx
    c.save()
    c.fillStyle = color
    c.font = `${size}px ${font}`
    c.textBaseline = "middle"
    c.textAlign = "center"
    c.fillText(msg, x, this.height / 2)
    c.restore()
  }

  circ(x: number, y: number, r: number, color = "black") {
    const c = this.ctx
    c.save()
    c.beginPath()
    c.strokeStyle = color
    c.arc(x, y, r, 0, Math.PI * 2)
    c.stroke()
    c.restore()
  }

  circfill(x: number, y: number, r: number, color = "black") {
    const c = this.ctx
    c.save()
    c.beginPath()
    c.fillStyle = color
    c.arc(x, y, r, 0, Math.PI * 2)
    c.fill()
    c.restore()
  }

  line(x0: number, y0: number, x1: number, y1: number, color = "black") {
    const c = this.ctx
    c.save()
    c.beginPath()
    c.strokeStyle = color
    c.moveTo(x0, y0)
    c.lineTo(x1, y1)
    c.stroke()
    c.restore()
  }

  oval(x0: number, y0: number, x1: number, y1: number, color = "black") {
    const c = this.ctx
    const w = x1 - x0
    const h = y1 - y0
    c.save()
    c.beginPath()
    c.strokeStyle = color
    c.ellipse(x0 + w / 2, y0 + h / 2, Math.abs(w) / 2, Math.abs(h) / 2, 0, 0, Math.PI * 2)
    c.stroke()
    c.restore()
  }

  ovalfill(x0: number, y0: number, x1: number, y1: number, color = "black") {
    const c = this.ctx
    const w = x1 - x0
    const h = y1 - y0
    c.save()
    c.beginPath()
    c.fillStyle = color
    c.ellipse(x0 + w / 2, y0 + h / 2, Math.abs(w) / 2, Math.abs(h) / 2, 0, 0, Math.PI * 2)
    c.fill()
    c.restore()
  }

  rect(x0: number, y0: number, x1: number, y1: number, color = "black") {
    const c = this.ctx
    c.save()
    c.beginPath()
    c.strokeStyle = color
    c.rect(x0, y0, x1 - x0, y1 - y0)
    c.stroke()
    c.restore()
  }

  rectfill(x0: number, y0: number, x1: number, y1: number, color = "black") {
    const c = this.ctx
    c.save()
    this.ctx.fillStyle = color
    this.ctx.fillRect(x0, y0, x1 - x0, y1 - y0)
    c.restore()
  }

  rrect(x: number, y: number, w: number, h: number, r: number, color = "black") {
    const c = this.ctx
    c.save()
    c.beginPath()
    c.strokeStyle = color
    this.roundRectPath(x, y, w, h, r)
    c.stroke()
    c.restore()
  }

  rrectfill(x: number, y: number, w: number, h: number, r: number, color = "black") {
    const c = this.ctx
    c.save()
    c.beginPath()
    c.fillStyle = color
    this.roundRectPath(x, y, w, h, r)
    c.fill()
    c.restore()
  }

  trianglefill(x0: number, y0: number, x1: number, y1: number, x2: number, y2: number, color = "black") {
    return this.polygonfill(
      [
        [x0, y0],
        [x1, y1],
        [x2, y2]
      ],
      color
    )
  }

  polygonfill(points: [number, number][], color = "black") {
    if (points.length < 3) return // need at least a triangle
    const c = this.ctx
    c.save()
    c.beginPath()
    c.fillStyle = color
    c.moveTo(points[0]![0], points[0]![1])
    for (let i = 1; i < points.length; i++) {
      c.lineTo(points[i]![0], points[i]![1])
    }
    c.closePath()
    c.fill()
    c.restore()
  }

  private roundRectPath(x: number, y: number, w: number, h: number, r: number) {
    const c = this.ctx
    c.moveTo(x + r, y)
    c.lineTo(x + w - r, y)
    c.quadraticCurveTo(x + w, y, x + w, y + r)
    c.lineTo(x + w, y + h - r)
    c.quadraticCurveTo(x + w, y + h, x + w - r, y + h)
    c.lineTo(x + r, y + h)
    c.quadraticCurveTo(x, y + h, x, y + h - r)
    c.lineTo(x, y + r)
    c.quadraticCurveTo(x, y, x + r, y)
  }
}