/*----------------------------------------------------------------------------*/
/*                                                                            */
/* Copyright (c) 2002-2022 Rony G. Flatscher. All rights reserved.            */
/* Copyright (c) 2023      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.               */
/*                                                                            */
/*----------------------------------------------------------------------------*/

/***********************************************************************
	program: oleinfo.cls

	purpose: Query the OLE/ActiveX automation interface, create an Object Rexx proxy
				which contains all relevant information in an edited and easy to use form

	usage:   require this file

	returns: an instance of class "rgf.oleinfo"

***********************************************************************/


        -- define invocation types
tmp.="???"
tmp.1="regularMethodCall"
tmp.2="getProperty"
tmp.4="putProperty"
tmp.8="letProperty"		-- a put property (assigning a reference, distinguishment not important for Rexx)
.local~rgf.invKind= tmp.        -- save stem


   -- class to parse and create sorted list of methods
::class rgf.oleinfo 				  public

::method oleObject              attribute       -- OLEobject itself

::method oleString              attribute			-- app_name
::method libname                attribute
::method libdoc                 attribute

::method allMethodDir           attribute			-- contains all methods
::method allMethodRel           attribute
::method allMethodSortedStem    attribute

::method methodDir              attribute

::method getPropertyDir         attribute
::method putPropertyDir         attribute

::method naDir                  attribute       -- directory for storing methods with unknown invocation types

::method getAndPutPropertyDir   attribute
::method getOnlyPropertyDir     attribute
::method putOnlyPropertyDir     attribute

::method eventDir               attribute
::method eventSortedStem        attribute

::method constantSortedStem     attribute

::method init
  expose oleObject oleString libname libdoc allMethodDir allMethodSortedStem methodDir getPropertyDir ,
         putPropertyDir naDir getAndPutPropertyDir getOnlyPropertyDir putOnlyPropertyDir eventDir eventSortedStem,
         constantSortedStem

  use arg oleobject, oleString

  signal on syntax      -- if OLEobject cannot be created, return .nil
  if arg(1)="" | arg(1)=.nil then oleobject=.oleobject~new(oleString, "WITHEVENTS")

  if arg(2, "Omitted") then oleString="n/a"

  methodDir      =.directory~new     -- method directory
  allMethodDir   =.directory~new
  allMethodSortedStem = .stem~new

  getPropertyDir =.directory~new     -- get property directory
  putPropertyDir =.directory~new     -- set property directory

  naDir          =.directory~new

  getAndPutPropertyDir = .directory~new
  getOnlyPropertyDir = .directory~new
  putOnlyPropertyDir = .directory~new

  eventDir       =.directory~new     -- event directory
  eventSortedStem = .stem~new
  constantSortedStem = .stem~new

  m.  = oleobject~getKnownMethods  -- get all known methods
  if m. <> .nil then
  do
     libname=choose( var("m.!LibName"), m.!LibName, "n/a")
     libdoc =choose( var("m.!LibDoc"),  m.!libDoc, "n/a"  )

     -- determine kind of methods, build collection objects
     j=0
     tmpSet=.set~new    -- used to lookup whether name already collected
     mStem.0=m.0
     do i=1 to m.0
        mo=.ole_method~new(m., i )      -- create object
        name=mo~name

        allMethodDir~setentry(name, mo) -- save method-object

        if      mo~invKind=1 then methodDir~setentry(name, mo)      -- a normal method
		  else if mo~invKind=2 then getPropertyDir~setentry(name, mo) -- a get property
        else if mo~invKind=4 | mo~invKind=8 then putPropertyDir~setentry(name, mo)	-- a put property

        uname=translate(name)
        if tmpSet~hasindex(uname) then iterate
        j=j+1
        mStem.j=name
        mStem.0=J
        tmpSet~put(uname)
     end

     call SysStemSort mStem., "Ascending", "Ignore" -- makes problem if used under MSIE (as of: 2002-05-27)
     self~allMethodSortedStem = mStem.  -- save sorted stem
  end

        -- methods which possess an unknown invocation type:
  naDir=allMethodDir~difference(methodDir)~difference(getPropertyDir)~difference(putPropertyDir)

  getAndPutPropertyDir = getPropertyDir~intersection(putPropertyDir)
  getOnlyPropertyDir   = getPropertyDir~difference(putPropertyDir)
  putOnlyPropertyDir   = putPropertyDir~difference(getPropertyDir)

  ev. = oleobject~getKnownEvents           -- get all known events

  if ev. <> .nil then
  do
     evStem.0=ev.0
     do i=1 to ev.0
        ev=.ole_event~new(ev., i )      -- create object
        eventDir~setentry(ev~name, ev)
        evStem.i=ev~name
     end

     call SysStemSort evStem., "Ascending", "Ignore"
     self~eventSortedStem = evStem.     -- save sorted stem
  end

-- call alert "oleobject="oleObject "ev.0="ev.0 "evStem.0="evStem.0 "sel~eventSortedStem="self~eventSortedStem

  c.  = oleobject~getConstant              -- OLE object needs to have been created with the "WITHEVENTS"-option
  if c. <> .nil then
  do
     i=0
     do item over c.
        i=i+1
        constStem.i=substr(item, 2)
     end
     constStem.0=i

     if constStem.0>0 then
	     call SysStemSort constStem., "A", "I" -- get constants and sort ascendingly
     self~constantSortedStem = constStem.
  end

  RETURN

syntax:
   oleObject=.nil
   return


  -- pretty print
::routine pp
  return "[" || arg(1) || "]"


  -- OLE MethodOrEvent
::class methodOrEvent
::method init
  expose name doc params
  use arg stem., idx

  name=stem.idx.!Name

  if var("stem.idx.!doc") then doc=stem.idx.!Doc
                          else doc="n/a" -- .nil

  params =.list~new
  do i=1 to stem.idx.!params.0
     params~insert( .ole_param~new( stem.idx.!params.i.!name, -
                                    stem.idx.!params.i.!type, -
                                    stem.idx.!params.i.!flags ) )
  end

::method name           attribute
::method doc            attribute
::method params         attribute -- list of params, if any

::method makestring     -- create string rendering
  expose name doc params

  tmp=""
  do param over params
     tmp=tmp "," param~makestring
  end
  tmp=substr(tmp,4)
  return name pp( doc ) "(" tmp ")"


  -- OLE Method
::class ole_method subclass MethodOrEvent

::method init
  expose invkind retType
  use arg stem., idx

  forward class (super) continue   -- let super initialize

  invkind=stem.idx.!InvKind
  if invkind=.nil then invkind="??? unknown type #" stem.idx.!InvKind "???"

  -- if invkind="4" then self~name=(self~name || "=")
  retType=stem.idx.!retType

::method invkind        attribute -- 1=method, 2=getProperty, 4=putProperty
::method retType        attribute

::method makestring     -- create string rendering
  expose invKind retType
  return (.rgf.invKind[invKind]~string) (self~makestring:super~string) "retType --->" (retType~string)


  -- OLE Event
::class ole_event subclass MethodOrEvent



  -- OLE Parameter
::class ole_param
::method init
  expose name in out opt type
  parse arg name, type, flags
  in =pos("in",  flags)>0
  out=pos("out", flags)>0
  opt=pos("opt", flags)>0

::method name   attribute
::method in     attribute
::method out    attribute
::method opt    attribute
::method type   attribute

::method makestring             -- render to a string
  expose name in out opt type type

  tmp=name
  if in  then tmp=tmp "in"
         else tmp=tmp "  "

  if out then tmp=tmp"/out"

  tmp=tmp":" type

  if opt then tmp=pp(tmp)       -- indicate optionality by enclosing in brackets
  return tmp


  -- C++/Java like choice ("...?...:...;")
:: routine choose public
  if arg(1)=.true then return arg(2)
  return arg(3)

