//
// Copyright (c) 2012, Brian Frank
// Licensed under the Academic Free License version 3.0
//
// History:
//   22 Apr 12  Brian Frank  Creation
//
using gfx
using concurrent
using wisp
using netColarUtils
**
** Sys manages references to system services
**
const class Sys : Service
{
  ** Logger
  static const Log log := Log.get("camembert")
  const ProjectRegistry prjReg
  ** The main options file
  const File optionsFile
  ** Shortcuts config
  const Shortcuts shortcuts
  ** Configuration options
  const Options options
  ** Theme
  const AtomicRef _theme := AtomicRef()
  const Template[] templates
  const LicenseTpl[] licenses
  ** Application level commands
  const Commands commands
  const WispService docServer
  const Unsafe pm
  ** Top-level frame (only in UI thread)
  Frame frame() { Actor.locals["frame"] ?: throw Err("Not on UI thread") }
  ProcessManager processManager() { (ProcessManager) pm.val }
  new make(|This|? f)
  {
    if(f!=null) f(this)
    options = Options.load(optionsFile)
    shortcuts =  Shortcuts.load(optionsFile.parent)
    _theme.val = Theme.load(`${optionsFile.parent}/themes/${options.theme}.props`.toFile)
    commands = Commands(this)
    prjReg = ProjectRegistry(options.srcDirs, optionsFile.parent)
    wPort := NetUtils.findAvailPort(8787)
    docServer = WispService { port = wPort; root = DocWebMod() }.start
    pm = Unsafe(ProcessManager())
    // read the templates
    tpl := Template[,]
    (optionsFile.parent + `templates/`).listFiles.each
    {
      tpl.add(JsonUtils.load(it.in, Template#))
    }
    templates = tpl.sort |a, b| {a.name <=> b.name}
    // read the licenses
    lic := LicenseTpl[,]
    (optionsFile.parent + `licenses/`).listFiles.each
    {
      lic.add(JsonUtils.load(it.in, LicenseTpl#))
    }
    licenses = lic.sort |a, b| {a.name <=> b.name}
  }
  Theme theme()
  {
    return _theme.val
  }
  override Void onStart()
  {
    PluginManager.cur.onConfigLoaded(this)
  }
  override Void onStop()
  {
    PluginManager.cur.onShutdown()
    // TODO: gotta be generalized or moved to Fantom pugin too
    if(docServer.isRunning)
    {
      docServer.stop
      docServer.uninstall
    }
    prjReg.pool.stop
    Actor.sleep(1sec)
    PluginManager.cur.onShutdown(true)
    prjReg.pool.kill
    Sys.log.info("Sys.onStop completed.")
  }
  ** Reload the *whole* config including all plugins
  static Void reloadConfig()
  {
    frame := Sys.cur.frame
    frame.spaces.each {Sys.cur.frame.closeSpace(it)}
    optionsFile := Sys.cur.optionsFile
    // Note : calling manually onStop to make sure it fully stops before we restart
    // because sys.stop calls it asynchronously
    Sys.cur.onStop
    Sys.cur.uninstall
    Sys sys := Sys
    {
      it.optionsFile = optionsFile
    }
    sys.start
    // Update theme menu
    m := (frame.menuBar as MenuBar)
    m.buildThemesMenu
    m.relayout; m.repaint
    // rescan projects after a sys chnage
    ProjectRegistry.scan
    // also update menus
    PluginManager.cur.onFrameReady(Sys.cur.frame, false)
  }
  ** All known plugins
  Str:Plugin plugins() {PluginManager.cur.plugins}
  ** Retrieve a given plugin instance by it's name
  Plugin? plugin(Str name)
  {
    return PluginManager.cur.plugins[name]
  }
  static Sys cur()
  {
    return (Sys) Service.find(Sys#)
  }
  Uri[] srcRoots() {options.srcDirs}
  static File confDir()
  {
    File.os((Env.cur.workDir + `etc/camembert/camembert.props`).readProps["configDir"])
  }
}