/*
 * call-seq:
 *   execute(...)
 *
 * Execute an Administration command against the local queue manager
 *
 * Parameters:
 * * Since the number of parameters can vary dramatically, all parameters are passed by name in a hash
 * * The entire MQ Administration interface has been implemented.
 *   Rather than re-documentation the hundreds of options, a standard
 *   convention has been used to map the MQ constants to Symbols in Ruby.
 *
 * For all MQ Admin commands, just drop the MQAI_ off the front and
 * convert the command name to lower case.
 * * E.g. MQAI_INQUIRE_Q becomes inquire_q
 *
 * For the hundreds of parameters, a similiar technique is followed.
 * Remove the prefixes: MQCA_, MQIA_, etc.. and convert to lowercase
 * * E.g. MQCA_Q_NAME becomes :q_name
 *
 * Example
 *   WMQ::QueueManager.connect do |qmgr|
 *     result = qmgr.execute(
 *                :command         => :inquire_q,
 *                :q_name          => 'MY.LOCAL.QUEUE',
 *                :q_type          => WMQ::MQQT_LOCAL,
 *                :current_q_depth => nil
 *                )
 *     # OR, we can replace the method name execute with the MQAI command:
 *     result = qmgr.inquire_q(
 *                :q_name          => 'MY.LOCAL.QUEUE',
 *                :q_type          => WMQ::MQQT_LOCAL,
 *                :current_q_depth => nil
 *                )
 *
 * Complete Example:
 *   require 'wmq/wmq'
 *   require 'wmq/wmq_const_admin'
 *   WMQ::QueueManager.connect(:q_mgr_name=>'REID', :connection_name=>'localhost(1414)') do |qmgr|
 *     qmgr.reset_q_stats(:q_name=>'*').each {|item| p item }
 *   end
 *
 * Some one line examples
 *   qmgr.inquire_q(:q_name=>'TEST*').each {|item| p item }
 *
 *   qmgr.inquire_q(:q_name=>'TEST*', :q_type=>WMQ::MQQT_LOCAL, :current_q_depth=>nil).each {|item| p item }
 *
 *   qmgr.inquire_process(:process_name=>'*').each {|item| p item }
 *
 *   qmgr.ping_q_mgr.each {|item| p item }
 *
 *   qmgr.refresh_security.each {|item| p item }
 *
 *   qmgr.inquire_q_status(:q_name=>'TEST*', :q_status_type=>:q_status, :q_status_attrs=>:process_id).each {|item| p item }
 *
 *   qmgr.start_channel_listener.each {|item| p item }
 *
 *   qmgr.inquire_channel_status(:channel_name=>'*').each {|item| p item }
 */
VALUE QueueManager_execute(VALUE self, VALUE hash)
{
#ifdef MQHB_UNUSABLE_HBAG
    VALUE          val, str;
    PQUEUE_MANAGER pqm;
    Data_Get_Struct(self, QUEUE_MANAGER, pqm);

    Check_Type(hash, T_HASH);

    if (pqm->admin_bag == MQHB_UNUSABLE_HBAG)         /* Lazy create admin bag */
    {
        pqm->mqCreateBag(MQCBO_ADMIN_BAG, &pqm->admin_bag, &pqm->comp_code, &pqm->reason_code);
        CHECK_COMPLETION_CODE("Creating the admin bag")
    }
    else
    {
        pqm->mqClearBag(pqm->admin_bag, &pqm->comp_code, &pqm->reason_code);
        CHECK_COMPLETION_CODE("Clearing the admin bag")
    }

    if (pqm->reply_bag == MQHB_UNUSABLE_HBAG)         /* Lazy create reply bag */
    {
        pqm->mqCreateBag(MQCBO_ADMIN_BAG, &pqm->reply_bag, &pqm->comp_code, &pqm->reason_code);
        CHECK_COMPLETION_CODE("Creating the reply bag")
    }
    else
    {
        pqm->mqClearBag(pqm->reply_bag, &pqm->comp_code, &pqm->reason_code);
        CHECK_COMPLETION_CODE("Clearing the reply bag")
    }

    val = rb_hash_aref(hash, ID2SYM(ID_command));     /* :command */
    if (NIL_P(val))
    {
        rb_raise(rb_eArgError, "WMQ::QueueManager#execute Mandatory parameter :command missing");
    }
#if RUBY_VERSION_CODE > 183
    rb_hash_foreach(hash, QueueManager_execute_each, (VALUE)pqm);
#else
    rb_iterate (rb_each, hash, QueueManager_execute_each, (VALUE)pqm);
#endif
    if(pqm->trace_level) printf ("WMQ::QueueManager#execute() Queue Manager Handle:%ld\n", pqm->hcon);

    pqm->mqExecute(
              pqm->hcon,                              /* MQ connection handle                 */
              wmq_command_lookup(rb_to_id(val)),      /* Command to be executed               */
              MQHB_NONE,                              /* No options bag                       */
              pqm->admin_bag,                         /* Handle to bag containing commands    */
              pqm->reply_bag,                         /* Handle to bag to receive the response*/
              MQHO_NONE,                              /* Put msg on SYSTEM.ADMIN.COMMAND.QUEUE*/
              MQHO_NONE,                              /* Create a dynamic q for the response  */
              &pqm->comp_code,                        /* Completion code from the mqexecute   */
              &pqm->reason_code);                     /* Reason code from mqexecute call      */

    if(pqm->trace_level) printf("WMQ::QueueManager#execute() completed with reason:%s\n", wmq_reason(pqm->reason_code));

    if (pqm->comp_code == MQCC_OK)
    {
        MQLONG numberOfBags;                          /* number of bags in response bag  */
        MQHBAG qAttrsBag;                             /* bag containing q attributes     */
        VALUE  array;
        MQLONG size;
        MQLONG length;
        MQCHAR inquiry_buffer[WMQ_EXEC_STRING_INQ_BUFFER_SIZE];
        PMQCHAR pChar;

        MQLONG qDepth;                          /* depth of queue                  */
        MQLONG item_type;
        MQLONG selector;
        MQLONG number_of_items;
        int    bag_index, items, k;

        pqm->mqCountItems(pqm->reply_bag, MQHA_BAG_HANDLE, &numberOfBags, &pqm->comp_code, &pqm->reason_code);
        CHECK_COMPLETION_CODE("Counting number of bags returned from the command server")

        if(pqm->trace_level > 1) printf("WMQ::QueueManager#execute() %ld bags returned\n", numberOfBags);
        array = rb_ary_new2(numberOfBags);

        for ( bag_index=0; bag_index<numberOfBags; bag_index++)               /* For each bag, extract the queue depth */
        {
            hash = rb_hash_new();

            pqm->mqInquireBag(pqm->reply_bag, MQHA_BAG_HANDLE, bag_index, &qAttrsBag, &pqm->comp_code, &pqm->reason_code);
            CHECK_COMPLETION_CODE("Inquiring for the attribute bag handle")

            pqm->mqCountItems(qAttrsBag, MQSEL_ALL_SELECTORS, &number_of_items, &pqm->comp_code, &pqm->reason_code);
            CHECK_COMPLETION_CODE("Counting number of items in this bag")

            if(pqm->trace_level > 1) printf("WMQ::QueueManager#execute() Bag %ld contains %ld items\n", bag_index, number_of_items);

            for (items=0; items<number_of_items; items++) /* For each item, extract it's value */
            {
                pqm->mqInquireItemInfo(
                                  qAttrsBag,               /* I: Bag handle */
                                  MQSEL_ANY_SELECTOR,      /* I: Item selector */
                                  items,                   /* I: Item index */
                                  &selector,               /* O: Selector of item */
                                  &item_type,              /* O: Data type of item */
                                  &pqm->comp_code,
                                  &pqm->reason_code);
                CHECK_COMPLETION_CODE("Inquiring Item details")

                if (selector > 0)                     /* Skip system selectors */
                {
                    switch (item_type)
                    {
                        case MQIT_INTEGER:
                            pqm->mqInquireInteger(qAttrsBag, MQSEL_ALL_SELECTORS, items, &qDepth, &pqm->comp_code, &pqm->reason_code);
                            CHECK_COMPLETION_CODE("Inquiring Integer item")

                            if(pqm->trace_level > 1)
                                printf("WMQ::QueueManager#execute() Item %ld: Integer:%ld, selector:%ld\n", items, qDepth, selector);

                            rb_hash_aset(hash, ID2SYM(wmq_selector_id(selector)), LONG2NUM(qDepth));
                        break;

                        case MQIT_STRING:
                            pqm->mqInquireString(qAttrsBag, MQSEL_ALL_SELECTORS, items, WMQ_EXEC_STRING_INQ_BUFFER_SIZE-1, inquiry_buffer,
                                            &size, NULL, &pqm->comp_code, &pqm->reason_code);
                            if(pqm->trace_level > 2)
                                printf("WMQ::QueueManager#execute() mqInquireString buffer size: %ld, string size:%ld\n",
                                    WMQ_EXEC_STRING_INQ_BUFFER_SIZE,size);
                            CHECK_COMPLETION_CODE("Inquiring String item")

                            length = 0;
                            pChar = inquiry_buffer + size-1;
                            for (k = size; k > 0; k--)
                            {
                                if (*pChar != ' ' && *pChar != 0)
                                {
                                    length = k;
                                    break;
                                }
                                pChar--;
                            }
                            rb_hash_aset(hash, ID2SYM(wmq_selector_id(selector)), rb_str_new(inquiry_buffer, length));

                            if(pqm->trace_level > 1)
                            {
                                inquiry_buffer[length] = '\0';
                                printf("WMQ::QueueManager#execute() Item %ld: String:'%s', selector:%ld\n",
                                        items, inquiry_buffer, selector);
                            }
                        break;

                        case MQIT_BAG:
                            printf("Ignoring Bag at this level\n");
                        break;

                        default:
                            printf("Ignoring Unknown type:%ld\n", item_type);
                        break;
                    }
                }
            }
            rb_ary_push(array, hash);
        }
        return array;
    }
    else
    {
        VALUE name = rb_iv_get(self,"@name");
        name = StringValue(name);

        if (pqm->reason_code == MQRCCF_COMMAND_FAILED)
        {
            /* Find out why admin command failed */
            MQLONG result_comp_code, result_reason_code;
            MQHBAG result_bag;

            pqm->mqInquireBag(pqm->reply_bag, MQHA_BAG_HANDLE, 0, &result_bag, &pqm->comp_code, &pqm->reason_code);
            CHECK_COMPLETION_CODE("Getting the result bag handle")

            pqm->mqInquireInteger(result_bag, MQIASY_COMP_CODE, MQIND_NONE, &result_comp_code, &pqm->comp_code, &pqm->reason_code);
            CHECK_COMPLETION_CODE("Getting the completion code from the result bag")

            pqm->mqInquireInteger(result_bag, MQIASY_REASON, MQIND_NONE, &result_reason_code, &pqm->comp_code, &pqm->reason_code);
            CHECK_COMPLETION_CODE("Getting the reason code from the result bag")

            pqm->comp_code   = result_comp_code;
            pqm->reason_code = result_reason_code;

            if(pqm->trace_level)
                printf("WMQ::QueueManager#execute() Error returned by command server:%s\n", wmq_reason(pqm->reason_code));
        }

        if (pqm->exception_on_error)
        {
            if (pqm->reason_code == MQRC_CMD_SERVER_NOT_AVAILABLE)
            {
                rb_raise(wmq_exception,
                        "WMQ::QueueManager#execute(). Please start the WebSphere MQ Command Server : 'strmqcsv %s', reason:%s",
                        RSTRING(name)->ptr,
                        wmq_reason(pqm->reason_code));
            }
            else
            {
                rb_raise(wmq_exception,
                        "WMQ::QueueManager#execute(). Error executing admin command on Queue Manager:%s, reason:%s",
                        RSTRING(name)->ptr,
                        wmq_reason(pqm->reason_code));
            }
        }
        return Qfalse;
    }
    return Qnil;
#else
    rb_notimplement();
    return Qfalse;
#endif
}