//
// Copyright (c) 2008, Brian Frank and Andy Frank
// Licensed under the Academic Free License version 3.0
//
// History:
//   21 Jul 08  Brian Frank  Creation
//

using gfx
using fwt

**
** Resource represents the objects a user navigates, views, and
** edits in a flux application.  Resources are mapped to objects
** via the indexed prop 'flux.resource.{target}={resource}', where
** both "target" and "resource" are qualied type names.  Subclasses
** must define a 'make(Uri, Obj)' constructor.
**
** See [pod doc]`pod-doc#resources` for details.
**
abstract class Resource
{

  **
  ** Get the root resources.
  **
  static Resource[] roots()
  {
    acc := Resource[,]
    File.osRoots.each |f| { if (f.exists) acc.add(resolve(f.uri)) }
    return acc
  }

  **
  ** Resolve a uri into a resource:
  **   1.  Resolve uri to obj via `sys::Uri.get`
  **   2.  If obj is Resource, return it
  **   3.  Resolve to obj type to resource type via 'flux.resource.{qname}'
  **       indexed property for type hierarchy
  **
  ** Throw UnresolvedErr if the uri can't be resolved, and UnsupportedErr
  ** if resource can't be mapped to a resource.
  **
  static Resource resolve(Uri uri)
  {
    // 1. resolve uri
    if (uri.toStr.startsWith("/")) uri = "file:$uri".toUri
    obj := uri.get

    // 2. if already resource return it
    if (obj is Resource) return obj

    // 3. map via fluxResource indexed props
    rtypes := Flux.indexForInheritance("flux.resource.", obj.typeof)
    if (rtypes.isEmpty) throw UnsupportedErr("No resource mapping for $obj.typeof")
    if (rtypes.size > 1)
    {
      // if we have multiple matches, take the non-flux version
      nonFlux := rtypes.exclude |t| { t.pod === Resource#.pod }
      if (!nonFlux.isEmpty) rtypes = nonFlux
    }
    return rtypes.first.make([uri, obj])
  }

  **
  ** Get the absolute Uri of this resource.
  **
  abstract Uri uri()

  **
  ** Get the display name of the resource.
  **
  abstract Str name()

  **
  ** Get a 16x16 icon for the resource.
  **
  virtual Image icon() { return Flux.icon(`/x16/file.png`) }

  **
  ** Return if this resource has or might have children.  This
  ** is an optimization to display the expansion control in a tree
  ** without loading all the children.  The default calls 'children'.
  **
  virtual Bool hasChildren()
  {
    c := children
    return c != null ? !c.isEmpty : false
  }

  **
  ** Get the navigation children of the resource.  Return an
  ** empty list or null to indicate no children.  Default
  ** returns null.
  **
  virtual Resource[]? children() { return null}

  **
  ** Get the list of available `View` types for the resource.
  ** The first view should be the default view.  The default
  ** implementation searches the type database index props formatted
  ** as "flux.view.{target}={view}", where "target" is this type (and
  ** its inherited classes, and "view" is view type qname.
  **
  virtual Type[] views()
  {
    Flux.indexForInheritance("flux.view.", typeof)
  }

  **
  ** Make a popup menu for this resource or return null.
  ** The default popup menu returns the `viewsMenu`.
  **
  virtual Menu? popup(Frame? frame, Event? event)
  {
    return Menu { add(viewsMenu(frame, event)) }
  }

  **
  ** Return a menu to hyperlink to the views supported
  ** by this resource.
  **
  virtual Menu? viewsMenu(Frame? frame, Event? event)
  {
    menu := Menu { text = Flux.locale("views.name") }
    views.each |Type v, Int i|
    {
      viewUri := i == 0 ? uri : uri.plusQuery(["view":v.qname])
      c := Command(v.name, null) { frame.load(viewUri, LoadMode(event)) }
      menu.add(MenuItem { command = c })
    }
    return menu
  }

  **
  ** Return `uri`.
  **
  override Str toStr() { return uri.toStr }
}

**************************************************************************
** ErrResource
**************************************************************************

**
** ErrResource models a resource that cannot be resolved.
**
class ErrResource : Resource
{

  new make(Uri uri) { this.uri = uri }

  override Uri uri

  override Str name() { n := uri.name; return n.isEmpty ? uri.toStr : n }

  override Image icon() { return Flux.icon(`/x16/err.png`) }

  override Type[] views() { return Type[,] }

}