--[[
  Simply loads some code from traffic planner tool and runs that. If you were to create a new mode with traffic, maybe contact
  me on Discord or Telegram first, so we could plan things in a way that wouldn’t break in the future.
]]

if ac.load('newmode.traffic.notavailable') then
  ac.warn('Not available in this session')
  return
end

local settings = ac.INIConfig.scriptSettings():mapSection('SETTINGS', {
  TRAFFIC_DENSITY = 1500,
  CARNAGE_MODE = false,
})

local function reportError(title, suggestion)
  script.drawUI = function()
    if ac.load('newmode.traffic.notavailable') then
      return
    end
    ui.drawRectFilled(0, ui.windowSize(), rgbm(0, 0, 0, 0.5))
    ui.pushFont(ui.Font.Title)
    ui.drawTextClipped(title, 0, ui.windowSize(), rgbm.colors.white, 0.5)
    ui.popFont()
    ui.drawTextClipped('Use Traffic Planner tool in Objects Inspector to '..suggestion, vec2(0, 30), ui.windowSize() + vec2(0, 30), rgbm.colors.white, 0.5)
    ui.setCursor(ui.windowSize() / 2 + vec2(-120, 60))
    ui.childWindow('w', vec2(240, 40), function ()
      if ui.button('OK', -0.1, 0) then
        ac.store('newmode.traffic.notavailable', 1)
      end
    end)
  end
end

local dataFilename = ac.getTrackDataFilename('traffic.json')
local cachedDataFilename = ac.getFolder(ac.FolderID.ExtCache)..'/traffic/'..ac.getTrackFullID('_')..'.bin'
if not io.fileExists(dataFilename) then
  reportError('Traffic data is missing', 'configure traffic')
  return
end

package.add('../../tools/csp-traffic-tool/lib')
package.add('../../tools/csp-traffic-tool/src/common')
package.add('../../tools/csp-traffic-tool/src/editor')
package.add('../../tools/csp-traffic-tool/src/simulation')

local TrafficConfig = require('TrafficConfig')
TrafficConfig.driversCount = settings.TRAFFIC_DENSITY
TrafficConfig.carnageMode = settings.CARNAGE_MODE
TrafficConfig.debugBehaviour = false
TrafficConfig.debugSpawnAround = false

local sim = ac.getSim()
local simTraffic, simBroken = nil, false
local TrafficSimulation = require('TrafficSimulation')

local data
try(function ()
  do
    if io.lastWriteTime(cachedDataFilename) > io.lastWriteTime(dataFilename) then
      data = stringify.binary.parse(io.load(cachedDataFilename))
    else
      local EditorMain = require('EditorMain')
      data = EditorMain(JSON.parse(io.load(dataFilename, '{}')) or {}):finalizeData()
      io.createFileDir(cachedDataFilename)
      io.save(cachedDataFilename, stringify.binary(data))
    end
  end
end, function (err)
  ac.error(err)
end)
if not data then
  reportError('Failed to load traffic data', 'fix traffic')
  return
end

local toolDir = 'extension/lua/tools/csp-traffic-tool'
local requiresDataInstall = #io.scanDir(toolDir..'/data', '*.json') == 0 ---@type boolean|string

if requiresDataInstall then
  ui.toast(ui.Icons.LoadingSpinner, 'Loading traffic data…###TrafficTool')
  web.get('https://files.acstuff.ru/shared/TCKo/data.zip', function (err, response)
    if response and response.body then
      for _, e in ipairs(io.scanZip(response.body)) do
        if e:startsWith('data/') and not e:find('/.', nil, true) then
          local content = io.loadFromZip(response.body, e)
          if content then
            local fileDestination = toolDir..'/'..e
            io.createFileDir(fileDestination)
            io.save(fileDestination, content)
          end
        end
      end
      if #io.scanDir(toolDir..'/data', '*.json') == 0 then
        requiresDataInstall = 'Data is damaged'
      end
    else
      requiresDataInstall = err and tostring(err) or 'Data is missing'
    end
    if type(requiresDataInstall) == 'string' then
      ui.toast(ui.Icons.Warning, 'Failed to install traffic data: '..requiresDataInstall..'###TrafficTool')
    else
      ac.broadcastSharedEvent('tools.TrafficTool.rescanCars')
      setTimeout(function ()
        requiresDataInstall = false
      end)
    end
  end)
end

ac.store('newmode.traffic.active', 1)

function script.simUpdate(dt)
  if requiresDataInstall or sim.isReplayActive or sim.dt == 0 or sim.isOnlineRace or simBroken then
    return
  end

  if sim.focusedCar ~= -1 then
    local car = ac.getCar(sim.focusedCar)
    if car ~= nil and not car.extrapolatedMovement then
      -- Hacky fix. Let’s hope we’ll be able to use extrapolated movement soon.
      dt = dt + (sim.gameTime - ac.getCar(sim.focusedCar).timestamp) / 1e3
      dt = math.max(dt, 0.002)
    end
  end

  if simTraffic == nil then
    simTraffic = TrafficSimulation(data)
  end

  simBroken = true
  simTraffic:update(dt)
  simBroken = false
end

function script.draw3D()
  if simTraffic then
    simTraffic:drawMain()
  end
end
