var core = use('core')
var camera = use('camera')
var compositor = use('compositor')
var input = use('input')
var shape = use('shape2d')
var text2d = use('text2d')
var tw = use('tween')
var ease = use('ease')
var random = use('random')

var GW = 600, GH = 600
var cellSize = 20
var gridW = GW / cellSize
var gridH = GH / cellSize

var game_cam = camera.make({width: GW, height: GH, pos: {x: GW / 2, y: GH / 2}})
var hud_cam = camera.make({width: GW, height: GH, pos: {x: GW / 2, y: GH / 2}})

// Action mapping with possession
input.configure({
  action_map: {
    up: ['w', 'up'],
    down: ['s', 'down'],
    left: ['a', 'left'],
    right: ['d', 'right'],
    restart: ['space']
  }
})

// Game state
var snake_shapes = []
var snake_pos = []
var dir = {x: 1, y: 0}
var next_dir = {x: 1, y: 0}
var move_timer = 0
var move_interval = 0.12
var state = 'playing'
var gameover_label = null
var score = 0
var score_label = null

// Input handler entity — possessed by player1
var game_input = {
  on_input: function(action, data) {
    if (!data.pressed) return
    if (state == 'playing') {
      if (action == 'up' && dir.y != -1) next_dir = {x: 0, y: 1}
      if (action == 'down' && dir.y != 1) next_dir = {x: 0, y: -1}
      if (action == 'left' && dir.x != 1) next_dir = {x: -1, y: 0}
      if (action == 'right' && dir.x != -1) next_dir = {x: 1, y: 0}
    }
    if (action == 'restart' && state == 'gameover') reset_game()
  }
}
input.player1().possess(game_input)

function grid_to_world(gx, gy) {
  return {x: gx * cellSize + cellSize / 2, y: gy * cellSize + cellSize / 2}
}

// Apple
var apple_gx = 0, apple_gy = 0
var apple_shape = shape.rect({
  pos: {x: 0, y: 0}, width: cellSize - 2, height: cellSize - 2,
  fill: {r: 1, g: 0.2, b: 0.2, a: 1}, plane: 'game'
})

function spawn_apple() {
  apple_gx = floor(random.random() * gridW)
  apple_gy = floor(random.random() * gridH)
  var i = 0
  for (i = 0; i < length(snake_pos); i++) {
    if (snake_pos[i].x == apple_gx && snake_pos[i].y == apple_gy) {
      spawn_apple()
      return
    }
  }
  var wp = grid_to_world(apple_gx, apple_gy)
  apple_shape.pos.x = wp.x
  apple_shape.pos.y = wp.y
}

function reset_game() {
  var i = 0
  for (i = 0; i < length(snake_shapes); i++)
    snake_shapes[i].destroy()
  snake_shapes = []
  snake_pos = []

  var cx = floor(gridW / 2)
  var cy = floor(gridH / 2)
  var wp = null
  for (i = 0; i < 3; i++) {
    push(snake_pos, {x: cx - i, y: cy})
    wp = grid_to_world(cx - i, cy)
    push(snake_shapes, shape.rect({
      pos: {x: wp.x, y: wp.y}, width: cellSize - 2, height: cellSize - 2,
      fill: {r: 0, g: 1, b: 0.3, a: 1}, plane: 'game'
    }))
  }

  dir = {x: 1, y: 0}
  next_dir = {x: 1, y: 0}
  state = 'playing'
  move_timer = 0
  score = 0
  if (score_label) score_label.text = "Score: 0"
  if (gameover_label) { gameover_label.destroy(); gameover_label = null }
  spawn_apple()
}

// HUD
score_label = text2d({
  text: "Score: 0", pos: {x: 10, y: GH - 30},
  plane: 'hud', size: 16, color: {r: 1, g: 1, b: 1, a: 1}
})

reset_game()

var comp_config = {
  clear: {r: 0.08, g: 0.08, b: 0.1, a: 1},
  planes: [
    {name: 'game', camera: game_cam, resolution: {width: GW, height: GH}, presentation: 'letterbox'},
    {name: 'hud', camera: hud_cam, resolution: {width: GW, height: GH}, presentation: 'stretch'}
  ]
}

core.start({
  width: 600, height: 600, title: "Snake",

  update: function(dt) {
    if (state != 'playing') return
    move_timer += dt
    if (move_timer < move_interval) return
    move_timer -= move_interval

    dir.x = next_dir.x
    dir.y = next_dir.y

    // New head position
    var hx = snake_pos[0].x + dir.x
    var hy = snake_pos[0].y + dir.y

    // Wrap
    if (hx < 0) hx = gridW - 1
    if (hx >= gridW) hx = 0
    if (hy < 0) hy = gridH - 1
    if (hy >= gridH) hy = 0

    // Self collision
    var i = 0
    for (i = 0; i < length(snake_pos); i++) {
      if (snake_pos[i].x == hx && snake_pos[i].y == hy) {
        state = 'gameover'
        gameover_label = text2d({
          text: "GAME OVER — SPACE to restart",
          pos: {x: GW / 2 - 170, y: GH / 2},
          plane: 'hud', size: 20, color: {r: 1, g: 0.3, b: 0.3, a: 1}
        })
        return
      }
    }

    // Add head with tween from old head position
    var old_wp = grid_to_world(snake_pos[0].x, snake_pos[0].y)
    var new_wp = grid_to_world(hx, hy)
    snake_pos.unshift({x: hx, y: hy})
    var head = shape.rect({
      pos: {x: old_wp.x, y: old_wp.y}, width: cellSize - 2, height: cellSize - 2,
      fill: {r: 0, g: 1, b: 0.3, a: 1}, plane: 'game'
    })
    // Smooth tween from old position to new position
    tw.tween(head.pos).to({x: new_wp.x, y: new_wp.y}, move_interval).ease(ease.linear)
    snake_shapes.unshift(head)

    // Eat apple?
    if (hx == apple_gx && hy == apple_gy) {
      score++
      score_label.text = "Score: " + score
      spawn_apple()
    } else {
      // Remove tail
      var tail = pop(snake_shapes)
      tail.destroy()
      pop(snake_pos)
    }
  },

  render: function() {
    return compositor.execute(compositor.compile(comp_config))
  }
})
