local MDT = MDT
local slen = string.len

-- The purpose of these functions is to provide a much better way to export the dungeon data to lua format.
-- Using the tShow function from Devpanel.lua does not work well because index and field orders are not
-- preserved. The resulting data would always be in random order and even small changes in the data cause
-- the export to be completely different.
-- We want to instead have a consistent way to export the dungeon data that makes versioning easier.
-- If for example x and y position change then only those values will change in the otherwise identical
-- dungeon data string representation. This will make it easier to compare dungeon data between versions.

--- @param export string
function MDT:ExportString(export)
  if not export then return end
  MDT:Async(function()
    MDT:ShowInterfaceInternal(true)
    local exportFrame = MDT.main_frame.ExportFrame
    local editBox = MDT.main_frame.ExportFrameEditbox
    exportFrame:ClearAllPoints()
    exportFrame:Show()
    exportFrame:SetPoint("CENTER", MDT.main_frame, "CENTER", 0, 50)
    editBox:SetText(export)
    editBox:HighlightText(0, slen(export))
    editBox:SetFocus()
    MDT.copyHelper:SmartShow(MDT.main_frame,0,50)
  end,"exportString")
end

--https://www.lua.org/pil/19.3.html
local function pairsByKeys(t, f)
  local a = {}
  for n in pairs(t) do table.insert(a, n) end
  table.sort(a, f)
  local i = 0 -- iterator variable
  local iter = function() -- iterator function
    i = i + 1
    if a[i] == nil then return nil
    else return a[i], t[a[i]]
    end
  end
  return iter
end

local function getIndent(indentCount)
  local indent = ""
  for i = 1, indentCount do
    indent = indent .. "  " --indent with 2 spaces
  end
  return indent
end

local function recursiveExport(obj, schema, indentCount)
  local res = ""
  local actualObjectType = type(obj)
  if schema.type == "schemaArray" then
    if actualObjectType ~= "table" then
      print("MDT recursiveExport: Error in " ..
        schema.name ..
        ": Expected table, got " .. actualObjectType .. " (field: " .. schema.name .. "; value: " .. tostring(obj) .. ")")
      return "\"Error: Expected table, got " .. actualObjectType .. " (value: " .. tostring(obj) .. ")\";\n"
    end
    res = res .. "{\n"
    for _, field in ipairs(schema.fields) do
      if obj[field.name] then
        res = res .. getIndent(indentCount + 1) .. "[\"" .. field.name .. "\"] = "
        res = res .. recursiveExport(obj[field.name], field, indentCount + 1)
      end
    end
    --handle non schema fields
    for key, value in pairs(obj) do
      local keyExists = false
      for _, field in ipairs(schema.fields) do
        if field.name == key then
          keyExists = true
        end
      end
      if not keyExists then
        local valueType = type(value)
        if valueType == "table" then
          valueType = "array"
        end
        print("MDT recursiveExport: Error: Non schema field " ..
          key ..
          " of type " .. valueType .. " in " .. (schema.name or
              "unnamed schema") .. " (field: " .. key .. "; value: " .. tostring(value) .. ")")
        res = res .. getIndent(indentCount + 1) .. "[\"" .. key .. "\"] = "
        res = res .. recursiveExport(value, { type = valueType }, indentCount + 1)
      end
    end
    return res .. getIndent(indentCount) .. "};\n"
  elseif schema.type == "array" then
    if actualObjectType ~= "table" then
      print("MDT recursiveExport: Error: Expected table, got " .. actualObjectType .. " (value: " .. tostring(obj) .. ")")
      return "\"Error: Expected table, got " .. actualObjectType .. " (value: " .. tostring(obj) .. ")\";\n"
    end
    res = res .. "{\n"
    for fieldName, value in pairsByKeys(obj) do
      local fieldNameType = type(fieldName)
      local maybeEscapedQuote = fieldNameType == "string" and "\"" or ""
      res = res .. getIndent(indentCount + 1) .. "[" .. maybeEscapedQuote .. fieldName .. maybeEscapedQuote .. "] = "
      res = res .. recursiveExport(value, schema.fields, indentCount + 1)
    end
    return res .. getIndent(indentCount) .. "};\n"
  elseif actualObjectType ~= schema.type then
    print("MDT recursiveExport: Error: Expected " ..
      schema.type .. ", got " .. actualObjectType .. " (field: " .. schema.name .. "; value: " .. tostring(obj) .. ")")
    return "\"TYPEERROR: " ..
        schema.type .. " expected, " .. actualObjectType .. " found" .. " (value: " .. tostring(obj) .. ")\";\n"
  elseif schema.type == "string" then
    if obj == "\n" then
      return "\"\\n\";\n"
    end
    return "\"" .. obj .. "\";\n"
  else
    return tostring(obj) .. ";\n"
  end
end

function MDT:ExportLuaTable(obj, schema)
  if not obj then
    print("MDT: ExportLuaTable: obj is nil")
    return
  end
  return (schema.name or "local table") .. " = " .. recursiveExport(obj, schema, 0)
end

--- @param target "enemies" | "pois"
--- @param dungeonIndex number
function MDT:TestExport(target, dungeonIndex)
  local schema = MDT:GetSchema(target)
  local dataToExport = (target == "enemies" and MDT.dungeonEnemies) or (target == "pois" and MDT.mapPOIs) or {}
  for i = dungeonIndex or 1, dungeonIndex or 100 do
    local dungeonName = MDT:GetDungeonName(i)
    if dungeonName and dungeonName ~= "-" then
      print(dungeonName)
      local export = MDT:ExportLuaTable(dataToExport[i], schema)
      if dungeonIndex then
        MDT:ExportString(export)
      end
      -- see results in chat frame, we don't need to show the data in the export frame
      -- TODO: catch and throw errors with error() and pcall() and show all error details in the export frame
      -- https://stackoverflow.com/questions/35735857/how-do-you-throw-lua-error-up
      -- https://www.lua.org/manual/5.3/manual.html#pdf-error
    end
  end
end

do
  -- call 1: export enemies of dungeon 1
  -- call 2: export pois of dungeon 1
  -- call 3: export enemies of dungeon 2
  -- ...
  local dungeonIndex = 0
  local targetIsEnemies = false
  local startSet = false

  --- @param start number optional dungeonIndex to start from
  function MDT:ExportDungeonDataIncrementally(start)
    targetIsEnemies = not targetIsEnemies
    if targetIsEnemies then
      dungeonIndex = dungeonIndex + 1
    end
    if start and not startSet then
      dungeonIndex = start
      startSet = true
    end
    local dungeonName = MDT:GetDungeonName(dungeonIndex)
    if dungeonName and dungeonName ~= "-" then
      MDT:Async(function()
        MDT:ShowInterfaceInternal(true)
        MDT:UpdateToDungeon(dungeonIndex)
        local dropDown = self.main_frame.DungeonSelectionGroup.DungeonDropdown
        if not dropDown.value then
          MDT:FixDungeonDropDownList()
        end
        MDT.main_frame.ExportFrame:Hide()
        local obj = targetIsEnemies and MDT.dungeonEnemies[dungeonIndex] or MDT.mapPOIs[dungeonIndex]
        local schema = MDT:GetSchema(targetIsEnemies and "enemies" or "pois")
        local export = MDT:ExportLuaTable(obj, schema)
        MDT:ExportString(export)
      end,"exportIncrementally")
    end
  end

end
