-- factoryos.shell — desktop host: canvas → wm → desktop → core.ui service.
-- Two display modes:
--   local  : canvas flushes straight to a monitor/terminal
--   wall   : virtual canvas + compositor pushes tiles to displaytile nodes
--            (cfg.tiles = { id = {x,y,w,h} }, cfg.screenW/screenH)

local sched     = require("factoryos.core.sched")
local canvasLib = require("factoryos.ui.canvas")
local WM        = require("factoryos.ui.wm")
local Desktop   = require("factoryos.ui.desktop")
local uiSvc     = require("factoryos.services.ui")
local compositorLib = require("factoryos.ui.compositor")

local M = {}

local DEFAULT_APPS = {
  "factoryos.apps.nodes",
  "factoryos.apps.power",
  "factoryos.apps.switches",
  "factoryos.apps.config",
  "factoryos.apps.guides",
}

function M.start(node, cfg)
  cfg = cfg or {}
  local cv, comp
  if cfg.tiles then
    local Wc, Hc = cfg.screenW or 100, cfg.screenH or 40
    cv = canvasLib.Canvas.new(canvasLib.virtual(Wc, Hc))
    comp = compositorLib.new(node, cv, cfg.tiles, cfg.displayChannel)
  else
    local target = (cfg.display and peripheral.wrap(cfg.display))
      or peripheral.find("monitor")
    if target and target.setTextScale then
      pcall(target.setTextScale, cfg.textScale or 0.5)
    end
    cv = canvasLib.Canvas.new(target or term)
  end

  local wm = WM.new(cv)
  local desktop = Desktop.new(wm, node)
  desktop.registryNodes = cfg.registryNodes

  for _, path in ipairs(cfg.apps or DEFAULT_APPS) do
    local ok, app = pcall(require, path)
    if ok then desktop:registerApp(app) end
  end

  uiSvc.attach(node, {
    wm = wm,
    openApp = function(id) return desktop:openApp(id) end,
    goHome = function() return desktop:goHome() end,
  })

  desktop:open()

  -- dev fallback: keyboard on the host terminal. Focused widgets get
  -- char/key first; unhandled keys become nav events.
  local KEYMAP = keys and {
    [keys.up] = "NAV_UP", [keys.down] = "NAV_DOWN",
    [keys.left] = "NAV_LEFT", [keys.right] = "NAV_RIGHT",
    [keys.enter] = "NAV_ENTER", [keys.backspace] = "NAV_BACK",
  } or {}
  node:on("key", function(k)
    if not wm:inputKey(k) and KEYMAP[k] then wm:nav(KEYMAP[k]) end
  end)
  node:on("char", function(c) wm:inputChar(c) end)

  node:spawn(function()
    while true do
      sched.sleep(0.5)
      desktop:tick()
      wm:draw()
      if comp then comp:flush() end
    end
  end, "render")

  return { wm = wm, desktop = desktop, canvas = cv, compositor = comp }
end

return M
