local format = string.format
local strip_floats = require'luametalatex-pdf-utils'.strip_floats
local pdfe = pdfe
local l = lpeg
local regularchar = 1-l.S'\0\t\n\r\f ()<>[]{}/%#'
local escapednamechar = l.P(1)/function(s)
  return format("#%02X")
end
local nameescape = l.Cs(l.Cc'/' * (regularchar + escapednamechar)^0)
local deepcopy_lookup deepcopy_lookup = {
  function(_, pdf) -- 1: null
    return 'null'
  end,
  function(_, pdf, b) -- 2: boolean
    return b == 1 and 'true' or 'false'
  end,
  function(_, pdf, i) -- 3: integer
    return format("%d", i)
  end,
  function(_, pdf, f) -- 4: number
    return strip_floats(format("%f", f), "%.?0+[ %]]", "")
  end,
  function(_, pdf, name) -- 5: name
    return nameescape:match(name)
  end,
  function(_, pdf, string, hex) -- 6: string
    return hex and format("<%s>", string) or format("(%s)", string)
  end,
  function(references, pdf, array, size) -- 7: array
    local a = {}
    for i=1,size do
      local type, value, detail = pdfe.getfromarray(array, i)
      a[i] = deepcopy_lookup[type](references, pdf, value, detail)
    end
    return '[' .. table.concat(a, ' ') .. ']'
  end,
  function(references, pdf, dict, size) -- 8: dict
    local a = {}
    for i=1,size do
      local key, type, value, detail = pdfe.getfromdictionary(dict, i)
      a[2*i-1] = nameescape:match(key)
      a[2*i] = deepcopy_lookup[type](references, pdf, value, detail)
    end
    return '<<' .. table.concat(a, ' ') .. '>>'
  end,
  nil, -- 9: stream (can only appear as a reference
  function(references, pdf, ref, num)
    local new = references[-num]
    if not new then
      new = pdf:getobj()
      references[-num] = new
      references[#references+1] = {ref, num}
    end
    return format("%i 0 R", new)
  end,
}

local references = setmetatable({}, {__index = function(t, n)
  local v = {}
  t[n] = v
  return v
end})

return function(file, id, pdf, type, value, detail)
  local references = references[id]
  local res = deepcopy_lookup[type](references, pdf, value, detail)
  local i, r = 1, references[1]
  while r do
    local type, value, detail, more = pdfe.getfromreference(r[1])
    if type == 9 then
      local a,j = {}, 0
      for i=1,more do
        local key, type, value, detail = pdfe.getfromdictionary(detail, i)
        if key == 'Length' then
          j=2
        else
          a[2*i-1-j] = nameescape:match(key)
          a[2*i-j] = deepcopy_lookup[type](references, pdf, value, detail)
        end
      end
      pdf:stream(references[-r[2]], table.concat(a, ' '), value(false), false, true)
    else
      pdf:indirect(references[-r[2]], deepcopy_lookup[type](references, pdf, value, detail))
    end
    i = i+1
    r = references[i]
  end
  for i=1,#references do references[i] = nil end
  return res
end
