/***************************************************************************
 *                         ArraySeq_mod -  description
 *                            -------------------
 *   begin                : Mon 19 Dec 2005 04:20:14 PM EST
 *   copyright            : (C) 2005 by Terry D. Boldt
 *   email                : fastsnip-wm1@yahoo.com
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
/*
 *
 *   QTAwk plugin function to deomonstrate accessing and sequencing arrays
 */

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>

/* Note QTAwk_plugins.h header file depeneds on "string.h" header file.
 * ALWAYS include "string.h" before QTAwk_plugins
 */
#include "QTAwk_plugins.h"

/* define some useful constants
 */
#define TRUE  1
#define FALSE 0

/* define End Of String marker
 */
#define EOS '\x000'

/* define initialization function
 */
unsigned QTAwk_init_load_module(
void)
{
    /* array of pointers to string with argument names for loaded functions
     * Here both functions take a single string function
     */
    unsigned char *arguments[] = {
                    "seq_array",
                    NULL
                    };

    /* declare error return variable
     */
    unsigned char *reg_err;

    /* call function to initialize for installing dynamic function(s)
     */
    QTAwk_register_function_count(2);

    /* now call function to register each dynamic function to load
     *
     * Note that 'QTAwk_register_function' returns a pointer to a string or NULL
     * if no error in registering function. Here we are ignoring the return value
     * until returning from the call.
     * We "should" examine the return value for every call to 'QTAwk_register_function'
     * and report any errors.
     */
    reg_err = QTAwk_register_function("ArraySeq_mod_version",NULL,FALSE);
    reg_err = QTAwk_register_function("sequence_array",arguments,FALSE);

    QTAwk_set_module_exit_function("ArraySeq_mod_exit");

    return reg_err ? -1 : 0;
} /* QTAwk_init_load_module */

/* function registered to accomplish actual work.
 *
 * All dynamically loaded functions have the following prototype form:
 *
 *      Variable_ptr loaded_function(unsigned argument_count);
 *
 * Where 'Variable_ptr' is a QTAwk variable defined in the QTAwk plugin
 *  header file "QTAwk_plugins.h"
 *  Do NOT manipulate the fields in the structure directly. Use the function
 *  calls defined in "QTAwk_plugins.h" for that purpose.
 *
 *
 *
 */

/* version string
 */
static unsigned char ArraySeq_mod_version_string[] = "Array Sequence Demo. Version 1.00";

/* function to return module version string
 */
Variable_ptr  ArraySeq_mod_version(
void)
{
    Variable_ptr   retval;

    retval = QTAwk_get_variable_set((void *)ArraySeq_mod_version_string,STRING_VAL);

    return retval;
} /* ArraySeq_mod_version */

/* function to scan variable indices and match index.
 *
 * Call with arguments:
 *  1. Variable with indices to scan
 *  2. pointer to string index to match, NULL if matching integer index
 *  3. integer to match if not matching string index
 *
 * Returns: TRUE if index matched, FALSE otherwise
 *
 * NOTE: case ignored based on value of global variable, 'IGNORECASE'.
 *
 */
unsigned       match_index(
Variable_ptr   var,
unsigned char *string_index,
unsigned long  integer_index)
{
    Variable_ptr    array_element;
    QTAwk_index     index;
    QTAwk_index_ptr indexp = &index;
    unsigned        seqstate;
    unsigned        bool = FALSE;
    unsigned        ignore_case = QTAwk_true_false(QTAwk_get_named_variable("IGNORECASE"));
    int            (*cmpstr)(const char *string1, const char *string2) = ignore_case ? strcasecmp : strcmp;

    for ( array_element = QTAwk_first_array_element(var,&seqstate,indexp) ;
          array_element;
          array_element = QTAwk_next_array_element(seqstate,indexp) ) {
        if ( string_index ) {
            if ( cmpstr((const char *)(string_index),(const char *)(indexp->index_rep.char_index)) == 0 ) {
                bool = TRUE;
                QTAwk_array_finish(seqstate);
                break;
            } /* endif */
          } else {
            if ( integer_index = indexp->index_rep.numeric_index ) {
                bool = TRUE;
                QTAwk_array_finish(seqstate);
                break;
            }/* endif*/
        } /* endif */
    } /* endfor */

    return bool;
} /* match_index */

static unsigned char   *vname = NULL;

static void  print_variable_value(
Variable_ptr var)
{
    void            *retn_val;
    unsigned         vtype;
    unsigned char   *strg_ret;
    unsigned char    char_ret;
    qta_long         int_ret;
    double           dbl_ret;

    /* now get element value and print
     */
    vtype = NO_VAL;
    retn_val = QTAwk_get_value(var,&vtype);
    switch ( vtype ) {
        case NO_VAL:
            printf("%llu",0LL);
            break;
        case REGEXP_VAL:
        case STRING_VAL:
        case STRING_INT:
        case STRING_FLT:
            strg_ret = (unsigned char *)retn_val;
            printf("%s",strg_ret);
            break;
        case CHAR_VAL:
            char_ret = *(unsigned char *)retn_val;
            break;
        case LONG_VAL:
            int_ret = *(qta_long *)retn_val;
            printf("%lld",int_ret);
            break;
        case DOUBLE_VAL:
            dbl_ret = *(double *)retn_val;
            printf("%f",dbl_ret);
            break;
    } /* endswitch */
} /* print_variable_value */

static unsigned  seq_array_elements(
Variable_ptr     arg_array)
{
    Variable_ptr     array_element;
    Variable_ptr     sarray;
    QTAwk_index_ptr  index_chn;
    unsigned         this_lvl = FALSE;
    unsigned         seqstate;
    unsigned         index_lvls;
    unsigned         lc;
    unsigned         index_this_level;
    unsigned         index_all_levels;
    unsigned         blnk_output = FALSE;

    /* loop through elements at this level
     */
    for ( array_element = QTAwk_first_array_element(arg_array,&seqstate,NULL) ;
          array_element ;
          array_element = QTAwk_next_array_element(seqstate,NULL) ) {
        this_lvl = TRUE;

        /* check if a next level of index for this element
         * If so then go to next level
         * This produces output by row, e.g., for a 3x3 matrix
         * output would be in the following index order
         *      [1][1]
         *      [1][2]
         *      [1][3]
         *      [2][1]
         *      [2][2]
         *      [2][3]
         *      [3][1]
         *      [3][2]
         *      [3][3]
         *
         * This is like the statements:
         *      for ( i in array ) {
         *          for ( j in array[i] ) {
         *              print("Element: [" >< i >< "][" >< j >< "]: " >< array[i][j]);
         *          }
         *      }
         *
         *  If a sparse array then only output elements that actually exist.
         */

         /* first check and output next index level if next level exists, skip this level
          * otherwise output this level
          *  index_this_level == indices specified for call variable
          *  index_all_levels == total indices needed to fully specify variable
          */
        index_this_level = QTAwk_index_var(array_element,&index_all_levels);
        /* if more indices needed, go to next index level
         * else print all variables at this index level.
         */
        if ( (index_this_level < index_all_levels) ) {
            (void)seq_array_elements(array_element);
          } else {
            if ( index_lvls == 1 ) {
                printf("%s",vname);
              } else if ( !blnk_output ) {
                printf("%s",vname);
                blnk_output = TRUE;
            } else printf(" ");

            /* print indices and values
             */
            /* first get indices for this element and print
             */
            index_chn = QTAwk_get_indices(array_element,&index_lvls);
            for ( lc = 0 ; lc < index_lvls ; lc++ ) {
                switch ( index_chn[lc].index_type ) {
                    case STRING_INDEX_TYPE:
                        printf("[%s]",index_chn[lc].index_rep.char_index);
                        break;
                    case NUM_INDEX_TYPE:
                        printf("[%lu]",index_chn[lc].index_rep.numeric_index);
                        break;
                } /* endswitch */
            } /* endfor */
            /* must free memory used by stack of index structures
             */
            free(index_chn);
            printf(" == ");

            /* now print element value
             */
            print_variable_value(array_element);
            if ( index_all_levels == 1 ) printf("\n");
        } /* endif */
    } /* endfor */

    if ( this_lvl && (index_all_levels > 1) ) printf("\n");

    return this_lvl;
} /* seq_array_elements */

/* test sequencing through array elements
 */
Variable_ptr sequence_array(
unsigned     arg_cnt)
{
    Variable_ptr     arg_array;

    /* get argument - retrieving by name - use same name as specified when function
     * registered.
     */
    arg_array = QTAwk_get_argument(1,"seq_array");

    vname = QTAwk_get_name(arg_array);

    printf("Sequencing Array: %s\n",vname);

    if ( !seq_array_elements(arg_array) ) printf("    Not Indexed\n");

    free(vname);

    return arg_array;
} /* sequence_array */

/* exit function - executed before unloading module
 * Using default function name here.
 */
unsigned ArraySeq_mod_exit(
void)
{
    printf("Unloading Array Sequencing module.\n");
} /* ArraySeq_mod_exit */