require "util"

local function initColors()
  local colorbrewer = require("colorbrewer")
  COLORS = colorbrewer.random(4)
  local c = COLORS[#COLORS]
  BG = { c[1], c[2], c[3], 0.9 }
  BLACK = { 0, 0, 0, 1 }
end

local function initScreen()
  local width, height = love.graphics.getDimensions()
  WIDTH, HEIGHT = round2even(width), round2even(height)
  love.graphics.setBackgroundColor(BG)
end

local function initFont()
  FONT = love.graphics.newFont(16)
  love.graphics.setFont(FONT)
  FONT_HEIGHT = FONT:getHeight()
  PLAYING_WIDTH = FONT:getWidth("playing")
  PAUSED_WIDTH = FONT:getWidth("paused")
end

local function initGrid()
  GRID = {}
  GRID.mt = {}
  setmetatable(GRID, GRID.mt)
  GRID.mt.__index = function(table, key)
    table[key] = {}
    table[key].mt = {}
    setmetatable(table[key], table[key].mt)
    table[key].mt.__index = function(innerTable, innerKey)
      innerTable[innerKey] = 1
      return innerTable[innerKey]
    end
    return table[key]
  end
end

local function cloneGrid()
  local grid = {}
  for x, v in pairs(GRID) do
    grid[x] = {}
    for y, set in pairs(v) do
      grid[x][y] = set
    end
    setmetatable(grid[x], grid[x].mt)
  end
  setmetatable(grid, grid.mt)
  return grid
end

function love.load(args)
  UNITS = args[1] or 16
  STEPS_PER_FRAME = args[2] or 1
  PAUSED = false
  initColors()
  initScreen()
  initFont()
  initGrid()
end

local function neighbors(x, y)
  local lowX = x - UNITS
  local highX = x + UNITS
  local lowY = y - UNITS
  local highY = y + UNITS
  local count = 0
  for i = lowX, highX do
    for j = lowY, highY do
      i = wrap(i, WIDTH)
      j = wrap(j, HEIGHT)
      if (i ~= x or j ~= y) and GRID[i][j] == 2 then
        count = count + 1
      end
    end
  end
  return count
end

local function clear(grid, x, y)
  grid[x][y] = 1
end

local function setHead(grid, x, y)
  grid[x][y] = 2
end

local function setTail(grid, x, y)
  grid[x][y] = 3
end

local function setConductor(grid, x, y)
  grid[x][y] = 4
end

local function setSpark(grid, x, y)
  local n = neighbors(x, y)
  if n == 1 or n == 2 then
    setHead(grid, x, y)
  end
end

local function updater(state)
  local match = {
    -- noop
    [1] = function(x, y) end,
    [2] = setTail,
    [3] = setConductor,
    [4] = setSpark,
  }
  return match[state]
end

local function stepCell(grid, x, y)
  local state = GRID[x][y]
  local update = updater(state)
  update(grid, x, y)
end

function love.keypressed(key, _, isrepeat)
  if key == "space" and not isrepeat then
    PAUSED = not PAUSED
    if PAUSED then
      print("pausing")
    else
      print("unpausing")
    end
  end
end

function love.mousepressed(x, y, button)
  x = (x - (x % UNITS))
  y = (y - (y % UNITS))
  local state = GRID[x][y]
  if button == 1 then
    if state == 1 then
      print("setting conductor")
      setConductor(GRID, x, y)
    else
      print("clearing")
      clear(GRID, x, y)
    end
  elseif button == 2 then
    if state == 4 then
      print("setting head")
      setHead(GRID, x, y)
    elseif state == 2 then
      print("setting conductor")
      setConductor(GRID, x, y)
    end
  end
end

FRAMELOCK_COUNTER = 0
FRAMELOCK_FPS = 10
function love.update(dt)
  pcall(function()
    require("lurker").update()
  end)
  if not PAUSED then
    FRAMELOCK_COUNTER = FRAMELOCK_COUNTER + dt
    if FRAMELOCK_COUNTER < (1 / FRAMELOCK_FPS) then
      return
    end
    FRAMELOCK_COUNTER = 0
    local grid = cloneGrid()
    for x = 0, WIDTH, UNITS do
      for y = 0, HEIGHT, UNITS do
        local state = GRID[x][y]
        if state ~= 1 then
          stepCell(grid, x, y)
        end
      end
    end
    GRID = grid
  end
end

local function paintPlayPause()
  local status, width
  if PAUSED then
    status, width = "paused", PAUSED_WIDTH
  else
    status, width = "playing", PLAYING_WIDTH
  end
  love.graphics.setColor(COLORS[1])
  love.graphics.rectangle("fill", -10, HEIGHT - FONT_HEIGHT - 20, width + 30, FONT_HEIGHT + 10, 5, 5)
  love.graphics.setColor(BLACK)
  love.graphics.print(status, 10, HEIGHT - FONT_HEIGHT - 15)
end

local function paintFPS()
  local fps = tostring(love.timer.getFPS()) .. " FPS"
  local width = FONT:getWidth(fps)
  love.graphics.setColor(COLORS[1])
  love.graphics.rectangle("fill", WIDTH - width - 10, HEIGHT - FONT_HEIGHT - 20, width + 30, FONT_HEIGHT + 10,
    5, 5)
  love.graphics.setColor(BLACK)
  love.graphics.print(fps, WIDTH - width - 5, HEIGHT - FONT_HEIGHT - 15)
end

function love.draw()
  for x, v in pairs(GRID) do
    if x ~= "mt" then
      for y, state in pairs(v) do
        if y ~= "mt" then
          local color = COLORS[#COLORS - state + 1]
          love.graphics.setColor(color)
          love.graphics.rectangle("fill", x, y, UNITS, UNITS)
          if state ~= 1 then
            love.graphics.setColor(BLACK)
            love.graphics.rectangle("line", x, y, UNITS, UNITS)
          end
        end
      end
    end
  end
  paintFPS()
  paintPlayPause()
end
