

function game_update(dt)
  scene_setView(ctx.viewport)
  game_switchSet()
  game_switchRoom()
  game_interact()
end

function game_setPatterns()
  --TODO patterns
  if true then return end
  if ctx.isAnim then return end
  -- grassTexture = grassTexture or  gfx.newImage("symbols/Textures/grass.png")

  -- grassTexture:setFilter("nearest","nearest")

  local dims, imgs, imgs2, size, img = {}, {}, {}, {}, {}
  local x, y, w, h = 0, 0, 0, 0
  local width, tallest = 0, 0
  local n = 32

  for i = 1, n, 1 do
    imgs[i] = {}
    imgs[i].img = gfx.newImage(string.format("symbols/Patterns/pattern_%i.png", i))
    if not imgs[i].img then return end
    imgs[i].w = imgs[i].img:getWidth()
    imgs[i].h = imgs[i].img:getHeight()
    imgs[i].i = i
    width = width + imgs[i].w
  end

  imgs = sortBy(imgs, "h")

  -- print(inspect(imgs))

  width = width / 1.9
  height = 0
  for i = 1, n, 1 do
    img = imgs[i].img
    w = imgs[i].w
    h = imgs[i].h
    y = x + w > width and y + h or y
    x = x + w > width and 0 or x
    dims[imgs[i].i] = { x, y, w, h }
    size[imgs[i].i] = { x, y, w, h, x = x, y = y, w = w, h = h }
    x = x + w
    imgs2[imgs[i].i] = img
    height = y + h
    --width=width+w
    --tallest=math.max(h,tallest)
  end

  gfx.push("all")
  gfx.reset()
  patterns.w, patterns.h = width, height
  patterns.atlas = patterns.atlas or newCanvas(width, height, { format = "rgb565" })
  gfx.setCanvas(patterns.atlas)
  for i = 1, n, 1 do
    gfx.draw(imgs2[i], dims[i][1], dims[i][2])
    dims[i][1] = dims[i][1] / width
    dims[i][2] = dims[i][2] / height
    dims[i][3] = dims[i][3] / width
    dims[i][4] = dims[i][4] / height
    imgs2[i]:release()
  end
  gfx.pop()

  -- patterns.imgs = imgs
  patterns.size = size
  patterns.dims = dims
  patterns.atlas:setFilter("nearest", "nearest", 0)
  -- shaders.forwardRender:send("patterns",unpack(patterns.dims))
  -- shaders.deferredStatic:send("patterns",unpack(patterns.dims))
end

function game_openRoom(key)
  editor_pushUndoState("Change Room")
  game_enterStackRoom(0)
  engine_waitFor(function()
    if #ctx.roomStack == 0 then
      game_enterRoom(ctx.rooms[key], nil, nil, nil, true)
      return true
    end
  end)
end

function game_openSet(key)
  editor_pushUndoState("Change Set")
  game_enterStackRoom(-1)
  ctx.roomZoom = 1
  editor_loadSet(key)
end

function game_exit()
  scene_destroy()

  ctx.editing = false
  pauseFade = 0
  time = 0

  game_unPause()
  namer_close()

  game.state = "Load"
  mouse_setVisible(true)
  engine.project = nil

  print("release")

  load_releaseSymbols()
end

function game_setResolution(n)
  game.newRes = n
end

function game_addRoom(room)
  if type(game_getRoom(room)) ~= "string" or ctx.rooms[room] then
    local room = type(game_getRoom(room)) ~= "string" and game_getRoom(room) or ctx.rooms[room]
    room.cameras = room.cameras or { camera_new() }
    table.insert(ctx.views, {
      key = room.key,
      room = room,
      viewportID = 1,
      cameraID = 1,
      camera = room.cameras[1],
      viewport = room.viewports[1],
      depth = 1,
    })
  else
    table.insert(ctx.views, room)
  end
  return ctx.views[#ctx.views]
end

function game_duplicateRoom(room, props)
  local dupe = game_addRoom(room.room.name)
  dupe.viewportID = #room.room.viewports + 1
  dupe.viewport = deepcopy(dupe.viewport)
  if props.offset then
    for i, p in ipairs(dupe.viewport.polys) do
      for j, q in ipairs(p) do
        dupe.viewport.polys[i][j] = dupe.viewport.polys[i][j] + ((j % 2 == 0) and props.offset.x / renderW or props.offset.y / renderH)
      end
    end
  end
end

function game_removeRoom(room)
  lume.remove(ctx.views, room)
end

function game_removeRooms(rooms)
  for i, r in rooms do
    lume.remove(ctx.views, game_getRoom(r))
  end
end
function game_getRooms(...)
  return lume.map(ctx.rooms, function(x)
    return game_getRoom(x)
  end)
end

function game_getRoom(room)
  return type(room) == "string" and (lume.match(ctx.rooms, function(x)
    return (x.name == room)
  end) or ctx.rooms[room]) or room
end

function game_getSet(set)
  return type(set) == "string" and lume.match(ctx.level.pages, function(x)
    return (x.name == set)
  end) or set
end

function game_findSet(room)
  local rset = ctx.page.key
  for s, set in pairs(ctx.level.pages) do
    local match = lume.match(game_getSet(set).rooms, function(x)
      return x.key == room
    end)
    if match then rset = s end
  end
  if rset == ctx.page.key then log("found same set") end
  return rset
end

function game_loadSet(key)
  if not ctx.level.pages[key] then
    _, key = lume.match(ctx.level.pages, function(x)
      return x.name == key
    end)
    if not key then
      log("set not found")
      return false
    end
  end

  if not ctx.level.pages[key] then
    log("set not found 2")
    return false
  end

  log(key .. " -> " .. ctx.page.key)

  if key == ctx.page.key then
    log("you are already in this set")
    return false
  end

  scene_loadSet(key)

  return true
end

function game_reloadSet() end

function game_setContains(set, r)
  local r = game_getRoom(r) or room
  return lume.match(game_getSet(set).views, function(x)
    return x.key == r.key
  end)
end

function game_enterRoom(room, cam, link, linkID, nostack)
  print("enterroom")
  -- print("room",room,room.isRoom)

  local room = room 

  -- if not ctx.isAnim and not room then 
  --   room = ctx.nextView.room
  -- end
  
  if room and room.isRoom then
    print("room: check")
    if not cam then
      engine_waitFor(function()
        editor.enteringRoom = true
        if ctx.viewport.key == room.key then
          -- plan_cacheRoomPreview(room)
          editor.enteringRoom = false
          return true
        end
      end)
    end
    print("postwait")
    if ctx.editing and pathsPanel then paths_closePanel() end
    print("close pp ")
    if nostack then
      print("ctx.pageView = false")
      ctx.pageView = false
      ctx.roomZoom = 0
    else
      if #ctx.roomStack == 0 then
        print("cachestatrt ") -- plan_cacheStackStart()
      end
      print("1")
      local _, i = lume.match(ctx.views, function(x)
        return x == editor.viewport
      end)
      print("2")
      if #ctx.roomStack > 1 and ctx.roomStack[#ctx.roomStack - 1][3] == link then
        -- fix
        -- ctx.roomStack = lume.slice(ctx.roomStack, 1, #ctx.roomStack - 1)
      else
        -- fix
        -- table.insert(ctx.roomStack, { ctx.page.key, ctx.viewport, link })
      end
    end

    print("pre next ")
    print(room and room.cameras and room.cameras[1])
    ctx.nextView = {
      key = room.key,
      cameraID = 1,
      camera = lume.merge(room.cameras[1] or camera_new(), nostack and {} or (cam or {})),
      room = room,
      viewportID = 1,
    }
    print("hello?")
  end

  if ctx.pageView then
    print("cache ") -- cacheSetPreview()
  end

  print("ctx.pageView = false")
  ctx.pageView = false
  editor_closeContextMenu()
  print("enterrred")
end

function game_exitRoom()
  mouse_setRelativeMode(false)
  game_enterStackRoom(#ctx.roomStack - 1)
  paths_closePanel()
  editor_closeContextMenu()
end

function game_exitAllRooms()
  for i = 1, #ctx.roomStack + 1, 1 do
    game_exitRoom()
  end
end

function game_enterStackRoom(stackPos)
  print("game_enterStackRoom", stackPos)
  -- print_info(3)
  if #ctx.roomStack > 0 then
    --nextSet   = roomStack[math.max(1,stackPos+1)][1]
    ctx.nextView = ctx.roomStack[math.max(1, stackPos + 1)][2]
    ctx.roomStack = lume.slice(ctx.roomStack, 1, math.max(1, stackPos))
  end
  if stackPos < 1 then
    ctx.roomStack = {}
    editor.stackStart = nil
  end
  if stackPos < 0 then
    editor.setPreview = nil
    ctx.pageView = true
  end
end

function game_switchSet()
  if nextSet ~= nil then
    if ctx.editing then
      editor_loadSet(nextSet)
    else
      game_loadSet(nextSet)
    end
    nextSet = nil
  end
end

function game_switchRoom()
  ctx.nextView = type(ctx.nextView) == "number" and ctx.views[ctx.nextView] or ctx.nextView
  if ctx.nextView ~= nil then
    local sameRoom = editor.viewport and editor.viewport.room.key == ctx.nextView.key
    local sameView = editor.viewport and editor.viewport.viewportID == ctx.nextView.viewportID
    if not sameRoom or (sameRoom and not sameView) then
      local reopen = false

      paths_closePanel()

      ctx.rooms[ctx.nextView.key].dateModified = os.time()

      if ctx.editing then
        editor.viewport = ctx.nextView
        scene_setView(ctx.nextView)
      end

      editor.selectedEntities = lume.clear(editor.selectedEntities)
    end
  end
end

function game_interact()
  if controls.aButtonPressed then game_talk() end
  if controls.bButtonPressed then game_touch() end
end

function game_unPause()
  game.paused = false
end

function game_pause()
  game.paused = true
end

function game_touch()
  --Fire off whatever function is queued next
end

function game_talk()
  --Fire off whatever text is queued next
end

function game_flashRoom()
  --Temporarily show a different room , either over the top of the current ones or instead of
  --Can either be for a set amount of time or until something ends the flash
end
