/*
 * call-seq:
 *   put(...)
 *
 * Put a message to the WebSphere MQ queue
 *
 * Parameters:
 * * A Hash consisting of one or more of the named parameters
 * * Summary of parameters and their WebSphere MQ equivalents
 *  queue.put(                                             # WebSphere MQ Equivalents:
 *   :message            => my_message,                    # n/a : Instance of Message
 *   :data               => "Hello World",                 # n/a : Data to send
 *   :sync               => false,                         # MQGMO_SYNCPOINT
 *   :new_id             => true,                          # MQPMO_NEW_MSG_ID & MQPMO_NEW_CORREL_ID
 *   :new_msg_id         => true,                          # MQPMO_NEW_MSG_ID
 *   :new_correl_id      => true,                          # MQPMO_NEW_CORREL_ID
 *   :fail_if_quiescing  => true,                          # MQOO_FAIL_IF_QUIESCING
 *   :options            => WMQ::MQPMO_FAIL_IF_QUIESCING   # MQPMO_*
 *   )
 *
 * Mandatory Parameters:
 *
 * * Either :message or :data must be supplied
 *   * If both are supplied, then :data will be written to the queue. The data in :message
 *     will be ignored
 *
 * Optional Parameters:
 * * :data => String
 *   * Data to be written to the queue. Can be binary or text data
 *   * Takes precendence over the data in :message
 *
 * * :message => Message
 *   * An instance of the WMQ::Message
 *   * The Message descriptor, headers and data is retrieved from :message
 *     * message.data is ignored if :data is supplied
 *
 * * :sync => true or false
 *   * Determines whether the get is performed under synchpoint.
 *     I.e. Under the current unit of work
 *      Default: false
 *
 * * :new_id => true or false
 *   * Generate a new message id and correlation id for this
 *     message. :new_msg_id and :new_correl_id will be ignored
 *     if this parameter is true
 *      Default: false
 *
 * * :new_msg_id => true or false
 *   * Generate a new message id for this message
 *   * Note: A blank message id will result in a new message id anyway.
 *     However, for subsequent puts using the same message descriptor, the same
 *     message id will be used.
 *      Default: false
 *
 * * :new_correl_id => true or false
 *   * Generate a new correlation id for this message
 *      Default: false
 *
 * * :fail_if_quiescing => true or false
 *   * Determines whether the WMQ::Queue#put call will fail if the queue manager is
 *     in the process of being quiesced.
 *   * Note: This interface differs from other WebSphere MQ interfaces,
 *     they do not default to true.
 *      Default: true
 *      Equivalent to: MQGMO_FAIL_IF_QUIESCING
 *
 *   * Note: As part of the application design, carefull consideration
 *     should be given as to when to allow a transaction or
 *     unit of work to complete or fail under this condition.
 *     As such it is important to include this option where
 *     appropriate so that MQ Administrators can shutdown the
 *     queue managers without having to resort to the 'immediate'
 *     shutdown option.
 *
 * * :options => Fixnum (Advanced MQ Use only)
 *   * Numeric field containing any of the MQ Put message options or'd together
 *     * E.g. :options => WMQ::MQPMO_PASS_IDENTITY_CONTEXT | WMQ::MQPMO_ALTERNATE_USER_AUTHORITY
 *   * Note: If :options is supplied, it is applied first, then the above parameters are
 *     applied afterwards.
 *   * One or more of the following values:
 *       WMQ::MQPMO_NO_SYNCPOINT
 *       WMQ::MQPMO_LOGICAL_ORDER
 *       WMQ::MQPMO_NO_CONTEXT
 *       WMQ::MQPMO_DEFAULT_CONTEXT
 *       WMQ::MQPMO_PASS_IDENTITY_CONTEXT
 *       WMQ::MQPMO_PASS_ALL_CONTEXT
 *       WMQ::MQPMO_SET_IDENTITY_CONTEXT
 *       WMQ::MQPMO_SET_ALL_CONTEXT
 *       WMQ::MQPMO_ALTERNATE_USER_AUTHORITY
 *       WMQ::MQPMO_RESOLVE_LOCAL_Q
 *       WMQ::MQPMO_NONE
 *   * Please see the WebSphere MQ documentation for more details on the above options
 *      Default: WMQ::MQPMO_NONE
 *
 * Returns:
 * * true : On Success
 * * false: On Failure
 *
 *   comp_code and reason_code are also updated.
 *   reason will return a text description of the reason_code
 *
 * Throws:
 * * WMQ::WMQException if comp_code == MQCC_FAILED
 * * Except if :exception_on_error => false was supplied as a parameter
 *   to QueueManager.new
 *
 * Example:
 *   require 'wmq/wmq_client'
 *
 *   WMQ::QueueManager.connect(:q_mgr_name=>'REID', :connection_name=>'localhost(1414)') do |qmgr|
 *     qmgr.open_queue(:q_name=>'TEST.QUEUE', :mode=>:output) do |queue|
 *
 *       # First message
 *       queue.put(:data => 'Hello World')
 *
 *       # Set Format of message to string
 *       message = WMQ::Message.new
 *       message.descriptor[:format] = WMQ::MQFMT_STRING
 *       queue.put(:message=>message, :data => 'Hello Again')
 *
 *       # Or, pass the data in the message
 *       message = WMQ::Message.new
 *       message.descriptor[:format] = WMQ::MQFMT_STRING
 *       message.data = 'Hello Again'
 *       queue.put(:message=>message)
 *     end
 *   end
 */
VALUE Queue_put(VALUE self, VALUE hash)
{
    MQPMO    pmo = {MQPMO_DEFAULT};  /* put message options           */
    MQMD     md = {MQMD_DEFAULT};    /* Message Descriptor            */
    PQUEUE   pq;
    MQLONG   BufferLength = 0;       /* Length of the message in Buffer */
    PMQVOID  pBuffer = 0;            /* Message data                  */

    Check_Type(hash, T_HASH);

    Data_Get_Struct(self, QUEUE, pq);

    /* Automatically open the queue if not already open */
    if (!pq->hcon && (Queue_open(self) == Qfalse))
    {
        return Qfalse;
    }

    Queue_extract_put_message_options(hash, &pmo);
    Message_build(&pq->p_buffer,  &pq->buffer_size, pq->trace_level,
                  hash, &pBuffer, &BufferLength,    &md);

    if(pq->trace_level) printf("WMQ::Queue#put() Queue Handle:%ld, Queue Manager Handle:%ld\n", pq->hobj, pq->hcon);

    pq->MQPUT(
          pq->hcon,            /* connection handle               */
          pq->hobj,            /* object handle                   */
          &md,                 /* message descriptor              */
          &pmo,                /* put message options             */
          BufferLength,        /* message length                  */
          pBuffer,             /* message buffer                  */
          &pq->comp_code,      /* completion code                 */
          &pq->reason_code);   /* reason code                     */

    if(pq->trace_level) printf("WMQ::Queue#put() MQPUT ended with reason:%s\n", wmq_reason(pq->reason_code));

    if (pq->reason_code != MQRC_NONE)
    {
        if (pq->exception_on_error)
        {
            VALUE name = Queue_name(self);

            rb_raise(wmq_exception,
                     "WMQ::Queue#put(). Error writing a message to Queue:%s, reason:%s",
                     RSTRING(name)->ptr,
                     wmq_reason(pq->reason_code));
        }
        return Qfalse;
    }
    else
    {
        VALUE message = rb_hash_aref(hash, ID2SYM(ID_message));
        if(!NIL_P(message))
        {
            VALUE descriptor = rb_funcall(message, ID_descriptor, 0);
            Message_from_mqmd(descriptor, &md);                /* This should be optimized to output only fields */
        }
    }

    return Qtrue;
}