#!/usr/bin/env rexx
/** 
 * The prolog code performs some simple tests for the different possibilities
 * to create an Enumeration
 *
 *  a.  Only symbols as key/index, where the ascending integer values/items will
 *      be done by the Enumeration  creation, starting at zero.
 *
 *  b.  Combination of keys with values, where the key/ndex can be an OORexx 
 *      string and the values/items can be any OORexx object, inclusive numbers. 
 *
 *  c.  An array containing 2-item arrays where the 2-item arrays contain
 *      the possible combination of the b. case, i.e array index 1 is a String,
 *      array index 2 can be anything.
 *  
 **/
level = .Enumeration~of(LOW,MEDIUM,HIGH,LEVELS)
say '----  Automatic sequencing from 0 -----'
say low '=' level[low]
say 'There are' level['LEVELS'] 'levels'
say medium '=' level[medium]
say '----- Bitlevels -----'
options = .Enumeration~of((BOLD,1),(ITALIC,2),(UNDERSCORE,4))
say bold'+'italic '=' options[bold]+options[italic]
say '----- Strings -----'
colours = .Enumeration~of((RED,'800000'),(GREEN, '008000'),(BLUE,'000080'))
say blue '=' colours[blue]
say red '=' colours~red
say '----- Objects -----'
objects = .Enumeration~of((OBJ1, .object~new), (OBJ2, .array~new), ('obj3', .table~new))
--say 'enum type =' objects~type
say obj2 'is a' objects['oBj2']~class~id 'instance'
say obj3 'is an instance of' objects~obj3 'class'
say '----- 2-item Array -----'
fonts = .array~of((ULTRALIGHT,100),(LIGHT,200),(MEDIUM, 500),(HEAVY,800), (ULTRAHEAVY,1000))
font = .Enumeration~of(fonts)
say 'The font''s MEDIUM weight is:' font~medium 

exit

::OPTIONS NOPROLOG

/**
 *  Class: Enumeration
 *
 *  An OORexx implementation of the well known ENUM's in other languages like C(++),C#,
 *  Java etc..
 *
 *  ENUMs are normally compile time defined and unmutable and function as symbols
 *  for constant values. For simple cases those values are ascending integers
 *  starting at 0, that don't need to be specified.
 *  For more complex situations both the key (in OORex terms: index) and the value
 *  (in OORexx: items) are provided as a 2-item array in the argument array.
 *
 *  One could think of this as a dynamic inplementation if the ::CONSTANT 
 *  directive, as it seems there is no provision for that in the built-in classes
 *
 *  @version  0.8    
 *
 *  @author   Ruurd Idenburg (ruurd@idenburg.net)
 *  
 *  2024-10-27 Changed '~class~id' to 'isA'
 *
 **/
::CLASS Enumeration PUBLIC

::METHOD of PUBLIC CLASS
-- get the enum arguments as an array
  if arg(1)~isA(.String)
-- case a. above 
    then do
      args = arg(1,'A')
-- creation will generate the ascending integer sequence
      simple = .true
    end
    else do 
      if arg(1)~isA(.Array) & arg(1)[1]~isA(.Array) then do
-- case c. above
        args = arg(1)
      end
      else do
-- case b. above
        args = arg(1,'A')
      end
-- creation does not need the provide the values
      simple = .false
    end
-- create the supporting table, caseless, as keys/indexes should be
-- unique and case differences would make it error prone   
  table = .caselessStringTable~new
-- create an instance of this class
  me = self~new
-- give access to the table for the instance
  me~enum = table
-- preset the simpleness (one or two things per argument)
--  simple = .false
-- do we have a 2-item array per argument
--  if args[1]~class~id='String' then simple=.true
  loop arg=1 to args~last
-- if sparse item iterate the loop
  if \args~hasIndex(arg) then iterate
  if simple then do
-- get key, set index of the key minus 1 as value
      key = args[arg]
      value = args~index(key)-1
    end
-- get key and value from the argument array
    else do
      key = args[arg][1]
      value = args[arg][2]
    end
-- and store the map member  
    table[key]= value
  end
return me

::ATTRIBUTE enum PRIVATE

::METHOD '[]'
  use strict arg key
return self~enum[key]

/**
 * The next 3 methods forward the message to the embedded
 * caselessStringtable, that will return it's result'
 **/
::METHOD items
return self~enum~items

::METHOD allindexes
return self~enum~allindexes

::METHOD allitems
return self~enum~allitems

/**
 * Method: unknown
 *
 * Allows to use the enum symbol to be used as a message
 *
 * @param string - the method name of the message
 *
 * @param array - an array with the original arguments
 *
 * @returns result  - the result of the forwarded method
 *                    if valid method for the enum otherwise
 *                    raises a syntax error.
 **/
::METHOD unknown PRIVATE
  use strict arg methodName, rest
-- Make sure there is an index with the methodName
  if self~enum~hasIndex(methodName) 
    then return self~enum[methodName~upper]
-- if not raise a syntax error
    else raise syntax 97.001 array(self,methodName)
exit  

::REQUIRES 'caselessStringTable.cls'
