
local triedLoad = false
local symbols

local function getNextUntitled()
  local nextUntitled = 0
  for k, v in pairs(symbols) do
    local n = tonumber(v.name:sub(10))
    if v.name:sub(1, 9) == "Untitled " and type(n) == "number" and n > nextUntitled then nextUntitled = n end
  end
  return nextUntitled + 1
end

local function checkSymbol(obj)
  local modtime = 1 --tonumber(lfs.getLastModified(obj.fullpath),10)
  if symbols[obj.key] and symbols[obj.key].dir then
    local object = symbols[obj.key]

    if obj.key ~= "_controller" and lfs.exists(LINK_DIR .. object.dir .. obj.name .. "/." .. obj.key) then
      object.animations = object.animations or lfs.dofile(LINK_DIR .. object.dir .. obj.name .. "/." .. obj.key)

      object.size = lfs.getSize(LINK_DIR .. object.dir .. obj.name)

      if lfs.exists(LINK_DIR .. object.dir .. obj.name .. "/.models") then
        object.models = object.models or {}
        for m, model in ipairs(lfs.getDirectoryItems(LINK_DIR .. object.dir .. obj.name .. "/.models/")) do
          if model.name ~= ".DS_Store" then
            print("LOAD MODEL", model)
            local filename = LINK_DIR .. object.dir .. obj.name .. "/.models/" .. model
            local mod = loadstring(lfs.read(filename))()
            object.models[mod.id] = mod
          end
        end
      end
    end

    object.key = obj.key
    object.name = obj.name

    local garbage = collectgarbage("count")
    local chunkedFrames = {}
    local dir = LINK_DIR .. "symbols/" .. object.name .. "/frames/"
    print(obj.name)
    if not object.animations then return false end
    for a, anim in pairs(object.animations) do
      for l, layer in ipairs(anim.layers) do
        layer.type = layer.type or "drawing"
        for f, frame in lume.ripairs(layer.frames) do
          local id = stringFrameID(frame)
          layer.frames[f] = lfs.dofile(dir .. id)
          local frame = layer.frames[f]
          if not frame then
            table.remove(layer.frames, f)
          else
            if layer.format == "vector" then
              for s, stroke in ipairs(frame.strokes) do
                newStroke(stroke)
              end
            end
            if layer.format == "raster" then
            end
            frame.tris = frame.tris or {}
            frame.verts = frame.verts or { {}, {}, {}, {} }
          end
        end
      end
    end
    print("addiff: ", collectgarbage("count") - garbage)
  else
    symbols[obj.key] = {
      fullpath = obj.fullpath,
      folder = obj.folder,
      key = obj.key,
      name = obj.name,
      modtime = modtime,
      dir = obj.dir or "symbols/",
      tags = {},
      default = "default",
      splitFrames = true,
      animations = {
        default = {
          key = "default",
          name = "default",
          fps = 24,
          length = 1,
          shapes = { { -1 / 2, -1 / 2, 1 / 2, -1 / 2, 1 / 2, 1 / 2, -1 / 2, 1 / 2 } },
          color = { r = 1, g = 1, b = 1, a = 1 },
          layers = {
            {
              name = "Layer 1",
              id = uuid(),
              visible = true,
              vector = false,
              alpha = 1,
              format = "vector",
              loop = false,
              resolution = 1,
              length = 1,
              wobble = 0,
              lineWidth = 1,
              clips = {},
              line = { color = { r = 0, g = 0, b = 0, h = 0, s = 0, v = 0, a = 1 }, width = 1 },
              fill = { color = { r = 0, g = 0, b = 0, h = 0, s = 0, v = 0, a = 1 } },
              frames = {
                {
                  symbols = {},
                  strokes = {},
                  fills = {},
                  position = { x = 0, y = 0, z = 0 },
                  orientation = { x = 0, y = 0.001, z = 0, w = 1 },
                  scale = { x = 1, y = 1, z = 1 },
                  blank = false,
                  x = 0,
                  y = 0,
                  w = obj.w or 4,
                  h = obj.h or 4,
                  ox = 0,
                  oy = 0,
                  l = 1,
                },
              },
            },
          },
        },
      },
    }
  end

  load_tagImage(symbols[obj.key])

  return symbols[obj.key]
end

local function cacheThumbnail(img) end

local function getValue(key, val)
  local key = (symbols[key] or engine.symbols[key]) and key or "checkers"
  if engine.symbols[key] then 
    return engine.symbols[key][val]
  else 
    print("key",key)
    return symbols[key][val]
  end
end

local function setValue(key, val, var)
  if key and val and var and symbols[key] ~= nil then symbols[key][val] = var end
end
local function canRelease(thing)
  return type(thing) == "userdata" and (thing:type() == "Image" or thing:type() == "Mesh" or thing:type() == "Canvas")
end
local function releaseImage(image, d)
  local d = d or 1
  if canRelease(image) then
    image:release()
    image = nil
    log("RELEASE")
  elseif type(image) == "table" and d < 16 then
    for k, v in pairs(image) do
      releaseImage(v, d + 1)
    end
  end
  image = nil
end

local function prepareTags()
  ctx.tagged.Game = ctx.tagged.Game or {}
  ctx.tagged.Level = ctx.tagged.Level or {}
  ctx.tagged.Room = ctx.tagged.Room or {}
  for k, v in pairs(ctx.tags) do
    ctx.tagged[k] = ctx.tagged[k] or {}
  end
end

function load_setSymbols(s)
  symbols = s
  print("load_setSymbols",s,symbols)
end

function load_imageTex(key)
  return getValue(key, "image")
end
function load_imagePath(key)
  return getValue(key, "path")
end
function load_imageName(key)
  return getValue(key, "name")
end
function load_imageThumb(key)
  return getValue(key, "thumb")
end
function load_imageData(key)
  return getValue(key, "data")
end
function load_imageType(key)
  return getValue(key, "folder")
end
function load_setImageThumb(key, thumb)
  return setValue(key, "thumb", thumb)
end

function load_loadController()
  symbols["_controller"] = {
    animations = {
      default = {
        bounds = { t = -4.5, ox = 0, r = 4.5, oy = 0, l = -4.5, b = 4.5 },
        size = { h = 9, w = 9 },
        layers = {
          {
            visible = true,
            frames = { { oy = 0, x = 0, ox = 0, w = 9, y = 0, h = 9, l = 1, blank = false } },
            name = "Layer 1",
            alpha = 1,
            loop = false,
          },
        },
        fps = 24,
        name = "default",
        length = 1,
        key = "default",
        shapes = { { -0.5, -0.5, 0.5, -0.5, 0.5, 0.5, -0.5, 0.5 } },
      },
    },
    modtime = 1477547970,
    tags = {},
    key = "_controller",
    name = "_controller",
    default = "default",
  }
end

function load_preLoad()
  ctx.tags = lume.merge(ctx.tags, { Game = "Game", Level = "Level", Room = "Room" })
  ctx.toLoad = {}
  ctx.loaded = true
  lfs.remove("images/temp.png")

  load_checkImages(LINK_DIR .. "symbols", "symbols/")
end

function load_loadGameImages(toLoad)
  for i, key in ipairs(toLoad) do
    if symbols[key] and key ~= "_controller" then load_loadSymbol(key, "Game") end
  end
end

function load_checkImages(folder, path)
  if not lfs.isDir(folder) then return end
  print("check contents of ", folder)
  for i, f in ipairs(lfs.getDirectoryItems(folder)) do
    local file = folder .. "/" .. f.name
    local isThing = lume.match(lfs.getDirectoryItems(file), function(x)
      return x.name:sub(1, 2) == ".u"
    end)

    if isThing then
      local key = isThing.name:sub(2, -1)
      if not symbols[key] then checkSymbol({ key = key, name = f.name }) end
      symbols[key].dir = path
    else
      load_checkImages(file, path .. "/" .. f.name)
    end
  end
end

function load_isLoaded()
  return ctx.loaded
end

function load_isLoading(key)
  local isLoading = false
  for i, image in ipairs(ctx.toLoad) do
    if image == key then return true end
  end
  return isLoading
end

function load_loadLoop()
  while #ctx.toLoad > 0 do
    load_loadSymbol(ctx.toLoad[#ctx.toLoad], "Game")
    print("loadloop", ctx.toLoad[#ctx.toLoad])
    table.remove(ctx.toLoad, #ctx.toLoad)
  end
  ctx.loaded = true
end

function load_newSymbol()
  local name = "Untitled " .. tostring(getNextUntitled())
  local key = uuid()
  local filename = (ctx.zip and LINK_DIR or GAME_DIR) .. "symbols/" .. name .. "/frames/" .. key .. ".png"

  lfs.mkdir((ctx.zip and LINK_DIR or GAME_DIR) .. "symbols", name, "frames")

  return load_loadSymbol(key, "Game", name)
end

function load_loadSymbol(key, folder, name)
  local symbols = folder == "interface" and engine.symbols or symbols
  local ext = ".png"
  local name = symbols[key] and symbols[key].name or name or key
  local fullpath = (folder == "Game" and LINK_DIR or "") .. "symbols/" .. name

  symbols[key] = symbols[key] or {}

  local sym = symbols[key]
  sym.fullpath = fullpath .. ext
  sym.folder = folder
  sym.key = key
  sym.name = name

  if folder == "interface" then

    sym.data = gfx.newImageData(sym.key)
    sym.image = gfx.newImage(sym.data)
  else
    print("\n load symbol ", key, name)

    sym = checkSymbol(sym)
    if not sym then
      gallery_deleteImage(key)
      return false
    end
    sym.image = sym.image or {}
    sym.path = sym.path or {}
    -- print(key,".path =",inspect(sym.path))

    for a, anim in pairs(sym.animations) do
      for l, layer in ipairs(anim.layers) do
        if layer.format == "raster" then
          for f, frame in ipairs(layer.frames) do
            print("texframe")
            sym.path[frame.id] = sym.path[frame.id] or string.format("%s%s%s/frames/%s.png", LINK_DIR, sym.dir, name, frame.id)
            if not ctx.isAnim then
              if not sym.image[frame.id] then print("NEW IMAGE FROM ", sym.path[frame.id]) end
              sym.image[frame.id] = sym.image[frame.id] or gfx.newImage(sym.path[frame.id])
            end
          end
        end
      end
    end

    print(key, ".path =", inspect(sym.path))
    print("")

    print(string.format(ansicolors("%{green} loaded %s %{reset}"), key))
  end

  symbols[key].size = 0 --lfs.getSize( LINK_DIR..symbols[key].dir..symbols[key].name)

  return symbols[key]
end

function load_tagImages()
  prepareTags()
  for k, obj in pairs(symbols) do
    load_tagImage(obj)
  end
end

function load_releaseSymbols()
  for s, symbol in pairs(symbols) do
    load_releaseSymbol(s)
    --if v.data then v.data:release() end
  end
end

function load_releaseSymbol(key)
  if symbols[key] and symbols[key].image then releaseImage(symbols[key].image) end
  symbols[key].image = nil
  if symbols[key].animations then
    for a, anim in pairs(symbols[key].animations) do
      for l, layer in pairs(anim.layers) do
        if layer.strokeMeshes then
          for m, mesh in ipairs(layer.strokeMeshes) do
            mesh:release()
          end
        end
      end
    end
    clearTable(symbols[key].animations)
    symbols[key].animations = nil
    print(ansicolors("%{red}" .. string.format("unloaded %s", key) .. "%{reset}"))
  end
end

function load_removeSymbol(key)
  symbols[key] = nil
end

function load_getSymbol(key)
  return symbols[key]
end

function load_tagImage(obj)
  if obj.folder ~= "Game" then return end
  if lume.count(ctx.tagged) ~= lume.count(ctx.tags) or #ctx.tagged == 0 then prepareTags() end
  obj.tags = obj.tags or {}
  for t, tag in lume.ripairs(obj.tags) do
    if ctx.tags[tag] then
      if not ctx.tagged[tag] then ctx.tagged[tag] = {} end
      table.add(ctx.tagged[tag], obj.key)
    else
      obj.tags[t] = nil
    end
  end
  table.add(ctx.tagged.Game, obj.key)
end

function load_loadConfig()
  triedLoad = true
  print("Checking for config...")

  if lfs.exists("config.lua") then
    print(" Found config file")

    local packed = lfs.read("config.lua")
    if packed and packed:sub(1, 1) == "{" then
      config = loadstring("return "..packed)()
      if lfs.isFused() then print(inspect(config)) end
    else
      print(" Invalid config file: ", packed)
    end
  else
    print(" No config to load")
  end

  print("")

  config = config
    or {
      unlockDrawing = true,
      tempTips = true,
      cropSelect = true,
      limitToStageSize = true,
      antiAlias = true,
      antiAliasing = 2,
      loadMode = "splash",
    }
  config.row = 20

  config.drawingResolution = 1

  config.highlightOutline = config.highlightOutline or { enabled = true, size = 0.5 }
  config.polkaDots = config.polkaDots or { enabled = true, amount = 0.15, speed = 0.15 }
  config.catmull = config.catmull or 2
  config.flatUI = false
  config.projects = config.projects or {}
  config.defaultProjectDirectory = config.defaultProjectDirectory or lfs.getUserDirectory() .. "/Documents"
  if not config.window then
    config.window = { size = { width = 500, height = 500 }, position = { x = 0, y = 0 }, display = 1 }
    config.window.position.x, config.window.position.y, config.window.display = lwindow.getPosition()
  else
    print(inspect(config.window))
  end
  loadedWidth, loadedHeight = loadedWidth or config.window.size.width, loadedHeight or config.window.size.height

  config.launchFullscreen = config.launchFullscreen == true
  config.entered = false

  engine.trace.enabled = config.traceEnabled

  if bench and bench.folded and config.benchFolded then
    for f, fold in pairs(config.benchFolded) do
      bench.folded[f] = true
    end
  end

  bench.on = config.benchOn

  for k,v in pairs(config) do 
    if type(v)=="table" and lume.count(v)==0 then 
      print("ZERO",k)
      config[k] = nil
    end
  end
  if game then
    game.resolution = config.resolution or 1
    game.newRes = game.resolution
  end
  print(inspect(config))
end

function load_stripSymbols()
  local t = {}
  for o, object in pairs(symbols) do
    if object.folder == "Game" then
      t[o] = {}
      for k, v in pairs(object) do
        if not multicheckOR(k, { "animations", "fullpath", "image", "path", "data" }) then t[o][k] = deepcopy(v) end
      end
    end
  end
  return t
end

function load_saveConfig()
  if not triedLoad then return end

  -- config = {
  --  flatUI = config.flatUI,
  --  projects=config.projects,
  --  entered=config.entered,
  --  autoload=config.autoload,
  --  catmull=config.catmull,
  --  tempTips = config.tempTips,
  --  unlockDrawing=config.unlockDrawing,
  --  unlockMouse=config.unlockMouse,
  --  highlightOutline = config.highlightOutline,
  --  polkaDots = config.polkaDots,
  --  hideFrameGaps = config.hideFrameGaps,
  -- }
  if game and game.state == "Edit" then
    config.project = engine.project
    print("save config")
    config.window = config.window or {}

    config.traceEnabled = engine.trace.enabled
    config.helpEnabled = editor.helpEnabled

    config.window.position.x, config.window.position.y, config.window.display = lwindow.getPosition()

    config.window.size = { width = screenW, height = screenH }

    config.sidebar = {
      visible = (sidebar.visible and sidebar.wasOpen),
      width = sidebar.width,
      darkMode = sidebar.darkMode,
    }
    config.timelineTracking = animatePanel and animatePanel.timelineTracking
    config.fontSize = scriptsPanel.fontSize
    config.autoSaveEnabled = editor.autoSaveEnabled
    config.autoSaveTime = editor.autoSaveTime
    config.editingSet = editor.editingPage
    config.renderLines = editor.renderLines
    config.timelineBottom = animatePanel and animatePanel.bottom
    config.galleryOpen = galleryPanel.open
    config.galleryThumb = galleryPanel.thumbSize
    config.gridEnabled = editor.gridEnabled
    config.debug = editor.debug
    config.animDebugView = animatePanel and animatePanel.debugView
    config.cropSelect = animatePanel and animatePanel.cropSelect
    config.recentColors = editor.recentColors
    config.recentArtColors = animatePanel.recentColors

    if bench and bench.folded then
      config.benchFolded = {}
      for f, fold in pairs(bench.folded) do
        if fold then config.benchFolded[f] = true end
      end
    end
    config.benchOn = bench and bench.on

    config.drawingResolution = editor.drawingResolution
    config.resolution = game.resolution

    local proj = config.projects[engine.project]

    if proj then proj.level = ctx.level.key end

    if ctx.isAnim then
      config.currentLayer = animatePanel.layer
      config.debugCompaction = animatePanel.debugCompaction
      config.debugBrush = animatePanel.debugBrush
      config.debugBoolean = animatePanel.debugBoolean

      config.showPasteboard = animatePanel.showPasteboard
      config.limitToStageSize = animatePanel.limitToStageSize
      config.exportFormats = animatePanel.exportFormats
      config.recentColors = animatePanel.recentColors
      config.fillColor = animatePanel.fillColor
      config.roughColor = animatePanel.roughColor
      config.noteColor = animatePanel.noteColor
      config.recentBrushes = animatePanel.recentBrushes
      config.currentBrush = { brush = animatePanel.brush }
      config.selectLayer = animatePanel.selectLayer
      config.eraseMode = animatePanel.eraseMode
      config.brushMode = animatePanel.brushMode
      config.currentPen = animatePanel.pen

      config.fillGap = animatePanel.fillGap
      config.fillRadius = animatePanel.fillRadius
      config.fillDrag = animatePanel.fillDrag
      config.fillAll = animatePanel.fillAll
      config.fillExpand = animatePanel.fillExpand
      config.fillBehind = animatePanel.fillBehind
      config.fillTolerance = animatePanel.fillTolerance
      config.fillDivs = animatePanel.fillDivs

      config.forceRefill = animatePanel.forceRefill
      config.frameShift = animatePanel.frameShift
      config.antiAliasing = animatePanel.antiAliasing
      config.antiAlias = animatePanel.antiAlias
      config.maxUndos = animatePanel.maxUndos
      config.onion = animatePanel.onion
    else
      if proj and ctx.viewport.room then

        proj.page = ctx.page.key
        proj.pageView = ctx.pageView
        proj.room = ctx.viewport.room.key
        proj.view = ctx.viewport.id

      end
    end
  end

  lfs.write(inspect(config),"config.lua")
end
