/*******************************************************************************
 *
 * Copyright (C) 1984-2005 Micro Focus International Ltd.
 * All rights reserved.
 *
 ******************************************************************************/

/*******************************************************************************
 *  This program demonstrates how to enable C code for CTF tracing using
 *  exported functions from the COBOL run-time system. It is intended to work
 *  unchanged on Windows, Unix and Linux platforms.
 ******************************************************************************/
#include <memory.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "mfctf.h"

#define THIS_COMPONENT      "MF.DEMO"

/******************************************************************************/
/*  Function prototypes                                                       */
/******************************************************************************/
static void trace_event(cobuns32_t  event,
                        cobuns32_t  level,
                        cobuns32_t  data_count,
                        void        *type_info,
                        void        *len_info,
                        void        *data);

static void tracer_callback(cobuns32_t  tracer_handle,
                            cobuns32_t  notif_type,
                            void        *notif_info);

/******************************************************************************/
/*  THIS_COMPONENT's tracer properties                                        */
/******************************************************************************/
#define PROPID_ALL                  0

#define PROPNAME_ALL                "ALL"
#define PROPNAME_APPLES             "APPLES"
#define PROPNAME_BANANAS            "BANANAS"
#define PROPNAME_ORANGES            "ORANGES"

static char *prop_name[] =
{
    PROPNAME_ALL,
    PROPNAME_APPLES,
    PROPNAME_BANANAS,
    PROPNAME_ORANGES,
    (char *)0,
};

/*
**  Trace flags for THIS_COMPONENT. Each bit of these flags corresponds to a
**  property being set (non-zero value) for THIS_EXAMPLE. The actual bit set for
**  a given property corresponds to the position-1 of the property's name in the
**  'prop_name' table above. For example, if bit 0 is set, it indicates that the
**  'APPLES' property was set. If the 'ALL' property is set, then all bits of
**  'trace_flags' will be set.
**
**  N.B. This is just an example of how properties and their values may be
**  processed by a component.How properties are processed is completely
**  transparent to CTF.
*/

static cobuns32_t   trace_flags;
        
#define TRACE_FLAGS_APPLES      0x00000001
#define TRACE_FLAGS_BANANAS     0x00000002
#define TRACE_FLAGS_ORANGES     0x00000004

/******************************************************************************/
/*  Other variables                                                           */
/******************************************************************************/
static cobuns32_t   trace_level = CTF_FLAG_LEVEL_NONE;
                                        /*  Trace level for THIS_COMPONENT    */
static cobuns32_t   tracer_handle;      /*  Tracer handle for THIS_COMPONENT  */

/******************************************************************************/
/*  THIS_COMPONENT events                                                     */
/******************************************************************************/
#define EVENT_PROCESSING_START              0
#define EVENT_PROCESSING_END                1
#define EVENT_APPLE_SIMPLE                  2
#define EVENT_APPLE_TEXT                    3
#define EVENT_BANANA_SIMPLE                 4
#define EVENT_BANANA_BUNCH                  5
#define EVENT_ORANGE_SIMPLE                 6
#define EVENT_ORANGE_JUICY                  7

/******************************************************************************/
/*  Some useful macros                                                        */
/******************************************************************************/

/*
**  Info level event. Output irrespective of trace flags
*/
#define TRACE_INFO_EVENT(event) \
    if(CTF_FLAG_LEVEL_INFO >= trace_level) \
        trace_event(event, CTF_FLAG_LEVEL_INFO, 0, NULL, NULL, NULL);

/*
**  Info level event. Output if specified trace flag set
*/
#define TRACE_INFO_EVENT_FLAGS(event, flags) \
    if((trace_flags & flags) \
    && (CTF_FLAG_LEVEL_INFO >= trace_level)) \
        trace_event(event, CTF_FLAG_LEVEL_INFO, 0, NULL, NULL, NULL);

/*
**  Condition macros used for entering trace blocks
*/
#define TRACE_ENABLED_IF() \
    if(trace_level != CTF_FLAG_LEVEL_NONE)

#define TRACE_ENABLED_LEVEL_IF(level) \
    if(level >= trace_level)

#define TRACE_DEBUG_IF(flag) \
    if((trace_flags & flag) \
    && CTF_FLAG_LEVEL_DEBUG >= trace_level)

#define TRACE_INFO_IF(flag) \
    if((trace_flags & flag) \
    && CTF_FLAG_LEVEL_INFO >= trace_level)

#define TRACE_WARN_IF(flag) \
    if((trace_flags & flag) \
    && CTF_FLAG_LEVEL_WARN >= trace_level)

#define TRACE_ERROR_IF(flag) \
    if((trace_flags & flag) \
    && CTF_FLAG_LEVEL_ERROR >= trace_level)

#define RTS_TRACE_FATAL_IF(flag) \
    if((trace_flags & flag) \
    && CTF_FLAG_FATAL_INFO >= trace_level)

/*
**  Macros to output event data (normally within condition blocks)
*/
#define TRACE_DEBUG_OUTPUT_DO(event, data_count, types, lengths, ptrs) \
    trace_event(event, \
                CTF_FLAG_LEVEL_DEBUG, \
                data_count, \
                types, \
                lengths, \
                ptrs);

#define TRACE_INFO_OUTPUT_DO(event, data_count, types, lengths, ptrs) \
    trace_event(event, \
                CTF_FLAG_LEVEL_INFO, \
                data_count, \
                types, \
                lengths, \
                ptrs);

#define TRACE_WARN_OUTPUT_DO(event, data_count, types, lengths, ptrs) \
    trace_event(event, \
                CTF_FLAG_LEVEL_WARN, \
                data_count, \
                types, \
                lengths, \
                ptrs);

#define TRACE_ERROR_OUTPUT_DO(event, data_count, types, lengths, ptrs) \
    trace_event(event, \
                CTF_FLAG_LEVEL_ERROR, \
                data_count, \
                types, \
                lengths, \
                ptrs);

#define TRACE_FATAL_OUTPUT_DO(event, data_count, types, lengths, ptrs) \
    trace_event(event, \
                CTF_FLAG_LEVEL_FATAL, \
                data_count, \
                types, \
                lengths, \
                ptrs);

/******************************************************************************/
/*  Apply property value for given property                                   */
/*                                                                            */
/*  apply_property_value(cobuns32_t prop_id,                                  */
/*                       cobuns32_t prop_val)                                 */
/*                                                                            */
/*  On input:                                                                 */
/*                                                                            */
/*      prop_id             =   Property identifier                           */
/*                                                                            */
/*      prop_val            =   Value to be set                               */
/*                                                                            */
/*  On output:                                                                */
/*                                                                            */
/*      <nothing>                                                             */
/*                                                                            */
/*  Function returns:                                                         */
/*                                                                            */
/*      <nothing>                                                             */
/******************************************************************************/
static void
apply_property_value(
    cobuns32_t prop_id,
    cobuns32_t prop_val)
{
    if(prop_id == PROPID_ALL)
    {
        trace_flags = (prop_val) ? 0xFFFFFFFF : 0;
    }
    else
    {
        cobuns32_t bit = (1 << (prop_id -1));

        if(prop_val)
        {
            trace_flags |= bit;
        }
        else
        {
            trace_flags &= ~bit;
        }
    }
}

/******************************************************************************/
/*  Do some processing, tracing according to which trace flags have been set  */
/*                                                                            */
/*  do_processing()                                                           */
/*                                                                            */
/*  On input:                                                                 */
/*                                                                            */
/*      <nothing>                                                             */
/*                                                                            */
/*  On output:                                                                */
/*                                                                            */
/*      <nothing>                                                             */
/*                                                                            */
/*  Function returns:                                                         */
/*                                                                            */
/*      <nothing>                                                             */
/******************************************************************************/
static void
do_processing()
{
    /*
    **  This event will be output if the trace level is >= 'info'
    */
    TRACE_INFO_EVENT(EVENT_PROCESSING_START);

    /*
    **  This event will be output if the 'apples' property has been set and
    **  the trace level is >= 'info'
    */
    TRACE_INFO_EVENT_FLAGS(EVENT_APPLE_SIMPLE, TRACE_FLAGS_APPLES)

    /*
    **  This event will be output if the 'apples' property has been set and
    **  the trace level is >= 'info'. The event will have 1 piece of trace
    **  data associated with it
    */
    TRACE_INFO_IF(TRACE_FLAGS_APPLES)
    {
        cobuns32_t  lengths[1];
        void        *ptrs[1];
        char        *some_text = "Some lovely apples";
        cobuns32_t  types[1];

        lengths[0] = strlen(some_text);
        types[0] = TRACE_EVENT_TYPE_TEXT;
        ptrs[0] = some_text;

        TRACE_INFO_OUTPUT_DO(EVENT_APPLE_TEXT, 1, types, lengths, ptrs);
    }

    /*
    **  This event will be output if the 'bananas' property has been set and
    **  the trace level is >= 'info'
    */
    TRACE_INFO_EVENT_FLAGS(EVENT_BANANA_SIMPLE, TRACE_FLAGS_BANANAS)

    /*
    **  This event will be output if the 'bananas' property has been set and
    **  the trace level is >= 'warning'. The event will have 2 pieces of trace
    **  data associated with it
    */
    TRACE_WARN_IF(TRACE_FLAGS_BANANAS)
    {
        cobuns32_t  bunch_size = 12;
        cobuns32_t  lengths[2];
        void        *ptrs[2];
        char        *some_text = "Some ripe bananas";
        cobuns32_t  types[2];

        lengths[0] = strlen(some_text);
        types[0] = TRACE_EVENT_TYPE_TEXT;
        ptrs[0] = some_text;

        lengths[1] = sizeof(bunch_size);
        types[1] = TRACE_EVENT_TYPE_COMP5;
        ptrs[1] = &bunch_size;

        TRACE_WARN_OUTPUT_DO(EVENT_BANANA_BUNCH, 2, types, lengths, ptrs);
    }

    /*
    **  This event will be output if the 'oranges' property has been set and
    **  the trace level is >= 'info'
    */
    TRACE_INFO_EVENT_FLAGS(EVENT_ORANGE_SIMPLE, TRACE_FLAGS_ORANGES)

    /*
    **  This event will be output if the 'oranges' property has been set and
    **  the trace level is >= 'error'. The event will have 3 pieces of trace
    **  data associated with it
    */
    TRACE_ERROR_IF(TRACE_FLAGS_ORANGES)
    {
        cobuns32_t  lengths[3];
        cobuns32_t  net_size = 9;
        void        *ptrs[3];
        char        *some_text = "Some juicy oranges";
        cobuns32_t  types[3];

        lengths[0] = strlen(some_text);
        types[0] = TRACE_EVENT_TYPE_TEXT;
        ptrs[0] = some_text;

        lengths[1] = sizeof(net_size);
        types[1] = TRACE_EVENT_TYPE_COMP5;
        ptrs[1] = &net_size;

        lengths[2] = sizeof(char *);
        types[2] = TRACE_EVENT_TYPE_ADDRESS;
        ptrs[2] = some_text;

        TRACE_ERROR_OUTPUT_DO(EVENT_ORANGE_JUICY, 3, types, lengths, ptrs);
    }

    /*
    **  This event will be output if the trace level is >= 'info'
    */
    TRACE_INFO_EVENT(EVENT_PROCESSING_END);
}

/******************************************************************************/
/*  Initialise tracing for THIS_COMPONENT.                                    */
/*                                                                            */
/*  On exit from this function 'tracer_handle', 'trace_flags' and             */
/*  'trace_level' will be set for THIS_COMPONENT                              */
/*                                                                            */
/*  initialise_tracing()                                                      */
/*                                                                            */
/*  On input:                                                                 */
/*                                                                            */
/*      <nothing>                                                             */
/*                                                                            */
/*  On output:                                                                */
/*                                                                            */
/*      <nothing>                                                             */
/*                                                                            */
/*  Function returns:                                                         */
/*                                                                            */
/*      <nothing>                                                             */
/******************************************************************************/
static void
initialise_tracing()
{
    /*
    **  Pick up THIS_COMPONENT's tracer handle
    */
    if(CBL_CTF_TRACER_GET(CTF_FLAG_COMP_NAME_NULL_TERM,
                          (const cobuns8_t *)THIS_COMPONENT, 
                          (cbl_x4_comp5_t *)&tracer_handle) == 0)
    {
        cobuns32_t      flags;
        cobuns32_t      i;
        NOTIF_INSTALL   notif_install;
        cobuns32_t      prop_val;

        /*
        **  Get THIS_COMPONENT's tracer properties.
        **
        **  For the purpose of this demo, all properties are expected to have
        **  integer values, so if the property exist, but has a non-integer
        **  value it will be ignored.
        */
        flags = (CTF_FLAG_PROP_NAME_NULL_TERM | CTF_FLAG_PROP_INT_VALUE);

        for(i = 0; prop_name[i]; i++)
        {
            if(CBL_CTF_COMP_PROPERTY_GET(flags,
					 (const cobuns8_t *)&tracer_handle,
					 prop_name[i],
					 0,
					 (cobuns8_t *)&prop_val) == 0)
            {
                apply_property_value(i, prop_val);
            }
        }

        /*
        **  Get THIS_COMPONENT's tracer trace level
        */
        CBL_CTF_TRACER_LEVEL_GET(0,
                                 (const cobuns8_t *)&tracer_handle,
                                 (cbl_x4_comp5_t *)&trace_level);

        /*
        **  Install tracer notification callback
        **
        **  (Only required if the component wants to respond to dynamic changes
        **  to its tracer's properties or trace level)
        */
        memset(&notif_install, 0, sizeof(notif_install));

        notif_install.handle = tracer_handle;
        notif_install.callback = tracer_callback;

        cobctf_tracer_notify(TRACER_NOTIFY_FUNC_INSTALL, &notif_install);
    }
}

/******************************************************************************/
/*  Create trace event for given rts event and trace data                     */
/*                                                                            */
/*  mFt_rt_trace_event(mf_uns32 event,                                        */
/*                     mf_uns32 level,                                        */
/*                     mf_uns32 data_count,                                   */
/*                     void     *type_info,                                   */
/*                     void     *len_info,                                    */
/*                     void     *data)                                        */
/*                                                                            */
/*  On input:                                                                 */
/*                                                                            */
/*      event       =   type of event                                         */
/*                                                                            */
/*      level       =   tracing level                                         */
/*                                                                            */
/*      data_count  =   number of trace data items                            */
/*                                                                            */
/*      type_info    =  data type array                                       */
/*                                                                            */
/*      len_info    =   data length array                                     */
/*                                                                            */
/*      data        =   data item array                                       */
/*                                                                            */
/*  On output:                                                                */
/*                                                                            */
/*      <nothing>                                                             */
/*                                                                            */
/*  Function returns:                                                         */
/*                                                                            */
/*      <nothing>                                                             */
/******************************************************************************/
static void
trace_event(
    cobuns32_t  event,
    cobuns32_t  level,
    cobuns32_t  data_count,
    void        *type_info,
    void        *len_info,
    void        *data)
{
    TRACE_EVENT trace_event;

    memset(&trace_event, 0, sizeof(trace_event));

    trace_event.event_id = event;

    trace_event.flags |= (level & CTF_FLAG_TRACE_OUTPUT_FORCE);
    level &= ~CTF_FLAG_TRACE_OUTPUT_FORCE;
    trace_event.level = level;

    trace_event.data_count = data_count;
    trace_event.event_len = (cobuns32_t *)len_info;
    trace_event.event_type = (cobuns32_t *)type_info;
    trace_event.event_data = data;

    cobctf_trace(0, (const cobuns8_t *)&tracer_handle, &trace_event);
}

/******************************************************************************/
/*  THIS_COMPONENT tracer callback                                            */
/*                                                                            */
/*  tracer_callback(cobuns32_t tracer_handle,                                 */
/*                      cobuns32_t notif_type,                                */
/*                      void       *notif_info)                               */
/*                                                                            */
/*  On input:                                                                 */
/*                                                                            */
/*      tracer_handle   =   tracer handle                                     */
/*                                                                            */
/*      notif_type      =   notification type                                 */
/*                                                                            */
/*                          0   =   property change                           */
/*                          1   =   enable state change                       */
/*                          2   =   trace level change                        */
/*                                                                            */
/*      notif_info      =   address of item whose type is dependent upon      */
/*                          notification type                                 */
/*                                                                            */
/*                          type 0  =   cblt_trc_notif_prop_change_t struct   */
/*                          type 1  =   cbl_x4_comp5_t                        */
/*                          type 2  =   cbl_x4_comp5_t                        */
/*                                                                            */
/*  On output:                                                                */
/*                                                                            */
/*      <nothing>                                                             */
/*                                                                            */
/*  Function returns:                                                         */
/*                                                                            */
/*      <nothing>                                                             */
/******************************************************************************/
static void
tracer_callback(
    cobuns32_t  tracer_handle,
    cobuns32_t  notif_type,
    void        *notif_info)
{
    cobuns32_t                      i;
    cobuns8_t                       *name;
    cblt_trc_notif_prop_change_t    *notif_prop;

    switch(notif_type)
    {
        case TRACER_NOTIF_TYPE_PROP_CHANGE:
            notif_prop = (cblt_trc_notif_prop_change_t *)notif_info;

            if(*(cobuns32_t *)(&notif_prop->flags) & 
                                                PROP_CHANGE_FLAGS_VAL_INTEGER)
            {
                name = (*(cobuns8_t **)(&notif_prop->name));

                for(i = 0; prop_name[i]; i++)
                {
                    if(strcmp(prop_name[i], name) == 0)
                    {
                        apply_property_value(
                                        i,
                                        *(cobuns32_t *)(&notif_prop->valint));
                        break;
                    }
                }
            }
            break;

        case TRACER_NOTIF_TYPE_LEVEL_CHANGE:
            trace_level = *(cobuns32_t *)(notif_info);
            break;
    }
}
    
/******************************************************************************/
/*  Main function                                                             */
/******************************************************************************/
int
main(
    int         argc,
    char        **argv)
{
    initialise_tracing();
    do_processing();
    cobtidy();
    return 0;
}
