/*----------------------------------------------------------------------------*/
/*                                                                            */
/* Copyright (c) 2011-2014 Rexx Language Association. All rights reserved.    */
/*                                                                            */
/* This program and the accompanying materials are made available under       */
/* the terms of the Common Public License v1.0 which accompanies this         */
/* distribution. A copy is also available at the following address:           */
/* https://www.oorexx.org/license.html                                        */
/*                                                                            */
/* Redistribution and use in source and binary forms, with or                 */
/* without modification, are permitted provided that the following            */
/* conditions are met:                                                        */
/*                                                                            */
/* Redistributions of source code must retain the above copyright             */
/* notice, this list of conditions and the following disclaimer.              */
/* Redistributions in binary form must reproduce the above copyright          */
/* notice, this list of conditions and the following disclaimer in            */
/* the documentation and/or other materials provided with the distribution.   */
/*                                                                            */
/* Neither the name of Rexx Language Association nor the names                */
/* of its contributors may be used to endorse or promote products             */
/* derived from this software without specific prior written permission.      */
/*                                                                            */
/* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS        */
/* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT          */
/* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS          */
/* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT   */
/* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,      */
/* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED   */
/* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,        */
/* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY     */
/* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING    */
/* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS         */
/* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.               */
/*                                                                            */
/*----------------------------------------------------------------------------*/
/* ooDialog User Guide

   Support - ObjectMgr						 v01-00  21Jan13
   -------------------
   A singleton component that manages model objects.

   Should ensure that only one instance is created. This version doesn't.

   Provides an object reference given a class and an instance name. Stores these
   in an "object bag", which is a table of classes, where each table item is a
   table of instance names, the item being the object reference.

   Interface ObjectMgr {
     bool    init
     cmptId  getComponentId( in string className, in string instanceName )
     bool    addCmptRef( in string className, in string instanceName, in objref cmptRef )
     void    list( )
     bool    showModel( in string modelClass, in string modelInstance )
  }

  Changes:
    v01-00 23Apr12: First version.
           11Jan13: Commented-out 'say' instructions.
           21Jan13: Make 'addView' private and 'removeView' explicitly public.
                    Minor typos in comments corrected.

  = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */


call "RequiresList.rex"


/*//////////////////////////////////////////////////////////////////////////////
  ============================================================================*/
::CLASS 'ObjectMgr' PUBLIC

  ::ATTRIBUTE objectBag PRIVATE-- a bag of objects - i.e. instances of Distributed
                        -- Components or DCs.

  /*----------------------------------------------------------------------------
    init - initialises the ObjectManager
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  ::METHOD init
    expose objectBag viewBag
    --say "ObjectMgr-init. Classes:" .CustomerModel .CustomerData .CustomerView
    .local~my.ObjectMgr = self
    objectBag = .directory~new		-- holds "class-inst" "id-ViewName"
    viewBag = .directory~new	-- holds view class and latest view instance.
    return .true
  /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */


  /*----------------------------------------------------------------------------
    getComponentId - Returns a Component Id if it's in the ObjectBag,
                     else calls doNewInstance to get id, else returns .false.
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  ::METHOD getComponentId PUBLIC
    expose objectBag
    use strict arg className, instanceName
    className = className~upper; instanceName = instanceName~upper
    --say "ObjectMgr-getComponentId-00: className =" className "instanceName =" instanceName
    ObjectName = className||"-"||instanceName
    if objectBag~hasIndex(objectName) then do		-- if class-instance already registered:
      --say "ObjectMgr-getComponentId-01: Class Found:" objectName
      arr = objectBag[objectName]			-- Get info array for this class-instance.
      componentId = arr[1]
      return componentId				-- return component id
    end
    -- If we've got to here, then there's no id stored. So go get one:
    componentId = self~doNewInstance(className,instanceName)
    --say "ObjectMgr-getComponentId-03: componentId =" componentId
    if componentId = .false then return .false		-- Bad object name
    self~addComponentId(className,instanceName,componentId)
    return componentId
  /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/


  /*----------------------------------------------------------------------------
    doNewInstance - Instantiates the requested object and returns the object id
                     (aka component id). If class not found, or class returns
                     .false to ~newInstance, then returns .false.
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  ::METHOD doNewInstance PRIVATE
    use strict arg className, instanceName
    SIGNAL ON NOMETHOD NAME catchIt
    interpret "componentId = ."||className||"~newInstance("||"'"||instanceName||"'"||")"
    --say "ObjectMgr-doNewInstance-01: componentId =" componentId
    -- add to object bag:
    return componentId
    catchIt:
      say "ObjectMgr-doNewInstance-02: component" className instanceName "not found."
      return .false
  /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/


  /*----------------------------------------------------------------------------
    addComponentId - Adds an Instance of a Class to the Object Bag.
                     If instance already exists, return .false
     Format of ObjectBag (note - directory indices are case-sensitive, i.e.
     if entry is created with index "AbC" then 'say dir["ABC")' may return nil.

     +---------------------------------+
     | Index      |  Item (an Array)   |
     +---------------------------------+
     |            |       | View       |
     | Class-Inst | objId | Class-Inst |
     |---------------------------------|
     | PersM-AB12 | x'12' | PersV-1    |
     |- - - - - - |- - - -|- - - - - - |
     | PersM-CD34 | x'5A' | .nil       |
     |- - - - - - |- - - -|- - - - - - |
     | PersV-1    | x'7B' | .nil       |
     |- - - - - - |- - - -|- - - - - - |
     |            |       |            |
     +---------------------------------+

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  ::method addComponentId PRIVATE
    expose objectBag
    use strict arg className, instanceName, componentId
    --say "ObjectMgr-addComponentId-01:" classname instancename componentId
    objectName = className||"-"||instanceName
    arr = .Array~new
    arr[1] = componentId
    arr[2] = .nil		-- Space for a View Name ('class-inst')
    objectBag[objectName] = arr
    --self~list
    return
  /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/


  /*----------------------------------------------------------------------------
    showModel - Shows a view of a model. Lacking a config file that links
                model class to one or more View classes, we heroically assume
                that all models are called "xxxModel" and its View is called
                xxxView. Assume only one View per Model.
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  ::METHOD showModel PUBLIC
    expose objectBag
    use arg modelClass, modelInstance, parentDlg
    --say "ObjectMgr-showModel-01a - modelNames:" modelClass modelInstance
    --say "ObjectMgr-showModel-01b - parentDlg: " parentDlg
    --say "ObjectMgr-showModel-02 - modelClass: '"||modelClass||"';  modelInstance: '"||modelInstance||"'"
    -- If this is an "anonymous" component (instance name "A"|"a"), ask its
    -- class object for an instance name:
    if modelInstance = "A" | modelInstance = "a" then do
      anonModelClass = "."||modelClass
      interpret "modelInstance = "||anonModelClass||"~getInstanceName"
      --say "ObjectMgr-showModel-02 - modelInstance: " modelInstance
    end
    modelId = self~getComponentId(modelClass, modelInstance)
    --say "ObjectMgr-showModel-03 - modelId:" modelId
    if modelId = .false then do
      say "ObjectMgr-showModel-03b: Model" modelClass modelInstance "could not be found."
      return .false
    end

    -- Check if View exists - if so, surface it:
    modelName = modelClass||"-"||modelInstance
    modelName = modelName~upper
    arr = objectBag[modelName]
    viewName = arr[2]
    --say "ObjectMgr-showModel-03c: ViewName =" viewName
    if viewName \= .nil then do		-- if view exists
      arr = objectBag[viewName]
      viewId = arr[1]
      viewId~show("SHOWTOP")		-- if minimized, doesn't work.
      viewId~show("NORMAL")		-- if hidden, doesn't work
      return .true
    end

    -- View does not exist - so construct the view name and send the class a
    --   newInstance(modelId, parentDlg).
    parse var modelClass root "Model"
    viewClass = root||"View"
    viewClassId = "."||viewClass
    --say "ObjectMgr-showModel-04: viewClassId =" viewClassId
    interpret "targetObject =" viewClassId
    --say "ObjectMgr-showModel-05: parentDlg =" parentDlg
    msg = .Message~new(targetObject, "newInstance", "I", modelId, parentDlg)
    --say "ObjectMgr-showModel-06: Class is:" .CustomerView .ObjectMgr
    viewId = msg~send
    --say "ObjectMgr-showModel-07 - viewId:" viewId

    self~addView(modelClass, modelInstance, viewClass, viewId)
    --self~addView(modelClass, modelInstance, "CustomerView", viewId)
    return .true
  /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */


  /*----------------------------------------------------------------------------
    addView - Adds a View to the ObjectBag.
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  ::METHOD addView PRIVATE
    expose objectBag --viewBag
    use strict arg modelClass, modelInstance, viewClass, viewId
    --say "ObjectMgr-addView-01."
    -- Get view's instanceName
    viewInstance = viewId~identityHash

    -- First, add View name to arr[2] of Model's entry in ObjectBag:
    modelName = modelClass||"-"||modelInstance
    modelName = modelName~upper()
    viewClass = viewClass~upper()
    viewName  = viewClass||"-"||viewInstance
    arr = objectBag[modelName]
    arr[2] = viewName
    -- Now add the View to the ObjectBag:
    self~addComponentId(viewClass, viewInstance, viewId)
    --say "ObjectMgr-addView-02: list with new View class:"
    --self~list
  /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */


  /*----------------------------------------------------------------------------
    removeView - Removes a view from the ObjectBag. (Used by RcView & ResView.)
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  ::METHOD removeView PUBLIC
    expose objectBag
    use arg viewClass, viewInstance
    viewClass = viewClass~upper()	-- View class was uppered in addView method.
    viewClassInst = viewClass||"-"||viewInstance
    --say "ObjectMgr-removeView-01: viewClassInst:" "'"||viewClass||"'" "'"||viewClassInst||"'"
    r = objectBag~remove(viewClassInst)
    --say "ObjectMgr-removeView-02: r =" r
    do i over objectBag
      arr = objectBag[i]
      if arr[2] = viewClassInst then arr[2] = .nil
    end
    --say "ObjectMgr-removeView-03: ObjectBag List:"
  /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */


  /*-----------------------------------------------------------------------
    list - Produces a list on stdout of the classes and their Instances
           that are in the Object Bag.
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  ::METHOD list PUBLIC
    expose objectBag viewBag
    say; say "Object Bag List:"
    say "  ----------------------------------------------------------------------------"
    say "  Class-Instance            Model Id                  ViewClass-Inst"
    say "  ------------------------  ------------------------  ------------------------"
    do name over objectBag
      arrItems = objectBag[name]
      viewClass = arrItems[2]
      if viewClass = .nil then viewClass = ".nil"
      modelId = arrItems[1]
      --say "ObjectBag-list-01: ModelID =" modelId ";" modelId~objectName
      say " " name~left(25) modelId~objectName~left(25) viewClass~left(25)
    end
    say "  ----------------------------------------------------------------------------"
    say
    /*say; say "Latest View Instances:"
    say "  View Class                 Instance"
    say "  -------------------------    ---"
    do viewClass over viewBag
      viewNo = viewBag[viewClass]
      say viewClass~left(25) viewNo~right(7)
    end
    say
    */
  /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

/*============================================================================*/

