REBOL [
    Title: "Multi-Category Description Table"
    Purpose: {An object for create a description lookup table
    that can combine several tables into one, with the tables
    identified by a category code.}
]

;; [---------------------------------------------------------------------------]
;; [ This is a module to address a situation that comes up in database work.   ]
;; [ If a database has various codes that represent things, it is nice to      ]
;; [ provide descriptions of those codes in anything produced for human use.   ]
;; [ If the actual code is the description, that can use up space in the       ]
;; [ database, and the description never can change unless one wants to go     ]
;; [ through the entire databse and change any descriptions that already are   ]
;; [ in there.  So it is customary to use codes to represent things, and to    ]
;; [ put the human-readable meaning of the codes in another table.             ]
;; [ If a database has many codes, that can result in many description         ]
;; [ tables, OR, one can find a way to put all such codes and their            ]
;; [ descriptions into one big table.  This module is a way to do that.        ]
;; [                                                                           ]
;; [ The end result of this will be a bunch of description tables, each        ]
;; [ consisting of a bunch of codes with a description for each code.          ]
;; [ Each table will be identified by a category code.                         ]
;; [ So, to find a description for some code, you have to find the table it    ]
;; [ is in by supplying the category code, and also the code for which         ]
;; [ you want the description.  This will be implemented in a big block        ]
;; [ that will look like this:                                                 ]
;; [                                                                           ]
;; [ [                                                                         ]
;; [     category-1 [code-1-1 desc-1-1 code-1-2 desc-1-2 ...]                  ]
;; [     category-2 [code-2-1 desc-2-1 code-2-2 desc-2-2 ...]                  ]
;; [     category-3 [code-3-1 desc-3-1 code-3-2 desc-3-2 ...]                  ]
;; [ ]                                                                         ]
;; [                                                                           ]
;; [ With a structure like this, if we want to find the descripion for a       ]
;; [ certain code in a certain category, we just have to "select" on the       ]
;; [ category to get the table, and then select code to get the description.   ]
;; [ This module provides a function to do that.                               ]
;; [                                                                           ]
;; [ Also, we have to build that multi-category table in the first place.      ]
;; [ A function is provided so you can supply a caegory, a code, and a         ]
;; [ description and they will be put into the table.                          ]
;; [ In case you can get your table data into a block of blocks, perhaps       ]
;; [ as the result of an SQL query, a function is provided to load the         ]
;; [ table from that direction.                                                ]
;; [                                                                           ]
;; [ More specifically:                                                        ]
;; [                                                                           ]
;; [ LOAD-ENTRY category code description                                      ]
;; [ This function is called repeatedly to load codes into the table.          ]
;; [ If you want to save the final table and have it look nice, you could      ]
;; [ provide the items in category-code order, but it is not necessary.        ]
;; [ It should not matter what types of data you use for the category,         ]
;; [ code, and description, but usually these would be strings.                ]
;; [                                                                           ]
;; [ LOAD-RESULTSET resultset                                                  ]
;; [ This function is called with all the table data in a block of blocks,     ]
;; [ where each sub-block contains a category, a code, and a description,      ]
;; [ all of them strings.  The function will call LOAD-ENTRY repeatedly        ]
;; [ for all the sub-blocks in the outer block.                                ]
;; [                                                                           ]
;; [ GET-DESCRIPTION category code                                             ]
;; [ This function returns the description string for the code supplied.       ]
;; [                                                                           ]
;; [ SAVE-TABLE file-id                                                        ]
;; [ This function saves a finished table to a text file.                      ]
;; [                                                                           ]
;; [ LOAD-TABLE file-id                                                        ]
;; [ This function loads a table previously saved with the SAVE-TABLE          ]
;; [ function.                                                                 ]
;; [                                                                           ]
;; [ And finally, all this is packaged into an object so you could have        ]
;; [ several such table in your program, although the purpose of this          ]
;; [ object in the first place is so that you don't need more than one         ]
;; [ table.                                                                    ]
;; [---------------------------------------------------------------------------]

MCDT: make object! [

    DESCRIPTIONS: []

    LOAD-ENTRY: func [
        CATEGORY
        CODE
        DESCRIPTION
        /local LOC BLK
    ] [
        LOC: head DESCRIPTIONS
        LOC: find DESCRIPTIONS CATEGORY
        either LOC [
            BLK: first next LOC ;; a reference, not a copy 
            append BLK CODE
            append BLK DESCRIPTION
        ] [
            BLK: copy []
            append BLK CODE
            append BLK DESCRIPTION
            append DESCRIPTIONS CATEGORY
            append/only DESCRIPTIONS BLK
        ]
    ]

    LOAD-RESULTSET: func [
        RESULTSET
    ] [
        DESCRIPTIONS: copy []
        foreach SUBBLOCK RESULTSET [
            LOAD-ENTRY SUBBLOCK/1 SUBBLOCK/2 SUBBLOCK/3
        ]
    ]

    GET-DESCRIPTION: func [
        CATEGORY
        CODE
    ] [
        either TBL: select DESCRIPTIONS CATEGORY [
            return SELECT TBL CODE
        ] [
            return none
        ]
    ]

    SAVE-TABLE: func [
        FILE-ID
    ] [
;;      save FILE-ID DESCRIPTIONS  ;; Instead, make it look nicer...
        if exists? FILE-ID [
        delete FILE-ID 
        ]
        foreach [CATEGORY TABLE] DESCRIPTIONS [
            write/append FILE-ID rejoin [
                mold CATEGORY
                " ["
                newline
            ]
            foreach [CODE DESC] TABLE [
                write/append FILE-ID rejoin [
                    "    "
                    mold CODE 
                    " "
                    mold DESC
                    newline
                ]
            ]
            write/append FILE-ID rejoin [
                "]"
                newline
            ]
        ]
    ]

    LOAD-TABLE: func [
        FILE-ID
    ] [
        DESCRIPTIONS: copy []
        DESCRIPTIONS: load FILE-ID
    ]
]

;;Uncomment to test
;MCDT/LOAD-ENTRY "WIND-DIR" "1" "North"
;MCDT/LOAD-ENTRY "WIND-DIR" "2" "Northeast"
;MCDT/LOAD-ENTRY "WIND-DIR" "3" "East"
;MCDT/LOAD-ENTRY "WIND-DIR" "4" "Southeast"
;MCDT/LOAD-ENTRY "WIND-DIR" "5" "South"
;MCDT/LOAD-ENTRY "WIND-DIR" "6" "Southwest"
;MCDT/LOAD-ENTRY "WIND-DIR" "7" "West"
;MCDT/LOAD-ENTRY "WIND-DIR" "8" "Northwest"
;MCDT/LOAD-ENTRY "WIND-DIR" "9" "Shifting winds"
;MCDT/LOAD-ENTRY "WIND-DIR" "N" "None/Calm"
;MCDT/LOAD-ENTRY "WIND-DIR" "U" "Undetermined"
;MCDT/LOAD-ENTRY "TAKEN" "0" "Taken to other"
;MCDT/LOAD-ENTRY "TAKEN" "1" "Hospital"
;MCDT/LOAD-ENTRY "TAKEN" "2" "Doctor's office"
;MCDT/LOAD-ENTRY "TAKEN" "3" "Morgue or funeral home"
;MCDT/LOAD-ENTRY "TAKEN" "4" "Residence"
;MCDT/LOAD-ENTRY "TAKEN" "5" "Station or quarters"
;MCDT/LOAD-ENTRY "TAKEN" "6" "Not transported"
;MCDT/LOAD-ENTRY "ACENGINE" "1" "Jet"
;MCDT/LOAD-ENTRY "ACENGINE" "2" "Turbo Prop"
;MCDT/LOAD-ENTRY "ACENGINE" "3" "Propeller"
;MCDT/LOAD-ENTRY "ACENGINE" "4" "None (Glider)"
;foreach [CATEGORY TABLE] MCDT/DESCRIPTIONS [
;    print [CATEGORY mold TABLE]
;]
;print "------------------------------------"
;print ["ACENGINE 2 = " MCDT/GET-DESCRIPTION "ACENGINE" "2"]
;print ["WIND-DIR 6 = " MCDT/GET-DESCRIPTION "WIND0DIR" "6"] ;; error
;print ["WIND-DIR U = " MCDT/GET-DESCRIPTION "WIND-DIR" "U"]
;print ["TAKEN 7 = " MCDT/GET-DESCRIPTION "TAKEN" "7"]
;print "------------------------------------"
;MCDT/SAVE-TABLE %tbltest.txt
;MCDT/LOAD-TABLE %tbltest.txt
;RESULTSET: [
;    ["ACFUEL" "1" "Jet Aviation Fuel"]
;    ["ACFUEL" "2" "Aviation Gasoline"]
;    ["ACFUEL" "3" "Other type of fuel"]
;    ["PAT_STAT" "1" "Improved"]
;    ["PAT_STAT" "2" "Remained Same"]
;    ["PAT_STAT" "3" "Worsened"]
;]
;MCDT/LOAD-RESULTSET RESULTSET
;foreach [CATEGORY TABLE] MCDT/DESCRIPTIONS [
;    print [CATEGORY mold TABLE]
;]
;halt