/*
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
 */
package org.wso2.andes.client;

import java.util.UUID;

import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Topic;
import javax.jms.Queue;

import org.apache.mina.common.ByteBuffer;
import org.wso2.andes.AMQException;
import org.wso2.andes.client.message.AbstractJMSMessage;
import org.wso2.andes.client.message.AMQMessageDelegate_0_8;
import org.wso2.andes.client.protocol.AMQProtocolHandler;
import org.wso2.andes.framing.AMQFrame;
import org.wso2.andes.framing.BasicContentHeaderProperties;
import org.wso2.andes.framing.BasicPublishBody;
import org.wso2.andes.framing.CompositeAMQDataBlock;
import org.wso2.andes.framing.ContentBody;
import org.wso2.andes.framing.ContentHeaderBody;
import org.wso2.andes.framing.ExchangeDeclareBody;

public class BasicMessageProducer_0_8 extends BasicMessageProducer
{

    BasicMessageProducer_0_8(AMQConnection connection, AMQDestination destination, boolean transacted, int channelId,
            AMQSession session, AMQProtocolHandler protocolHandler, long producerId, boolean immediate, boolean mandatory,
            boolean waitUntilSent) throws AMQException
    {
        super(connection, destination,transacted,channelId,session, protocolHandler, producerId, immediate, mandatory,waitUntilSent);
    }

    void declareDestination(AMQDestination destination)
    {

        ExchangeDeclareBody body = getSession().getMethodRegistry().createExchangeDeclareBody(_session.getTicket(),
                                                                                              destination.getExchangeName(),
                                                                                              destination.getExchangeClass(),
                                                                                              false,
                                                                                              false,
                                                                                              false,
                                                                                              false,
                                                                                              true,
                                                                                              null);
        // Declare the exchange
        // Note that the durable and internal arguments are ignored since passive is set to false

        AMQFrame declare = body.generateFrame(_channelId);

        _protocolHandler.writeFrame(declare);
    }

    void sendMessage(AMQDestination destination, Message origMessage, AbstractJMSMessage message,
                     UUID messageId, int deliveryMode,int priority, long timeToLive, boolean mandatory,
                     boolean immediate, boolean wait) throws JMSException
    {
        BasicPublishBody body = getSession().getMethodRegistry().createBasicPublishBody(_session.getTicket(),
                                                                                        destination.getExchangeName(),
                                                                                        destination.getRoutingKey(),
                                                                                        mandatory,
                                                                                        immediate);

        AMQFrame publishFrame = body.generateFrame(_channelId);

        message.prepareForSending();
        ByteBuffer payload = message.getData();
        AMQMessageDelegate_0_8 delegate = (AMQMessageDelegate_0_8) message.getDelegate();
        BasicContentHeaderProperties contentHeaderProperties = delegate.getContentHeaderProperties();

        contentHeaderProperties.setUserId(_userID);

        //Set the JMS_QPID_DESTTYPE for 0-8/9 messages
        int type;
        if (destination instanceof Topic)
        {
            type = AMQDestination.TOPIC_TYPE;
        }
        else if (destination instanceof Queue)
        {
            type = AMQDestination.QUEUE_TYPE;
        }
        else
        {
            type = AMQDestination.UNKNOWN_TYPE;
        }

        //Set JMS_QPID_DESTTYPE
        delegate.getContentHeaderProperties().getHeaders().setInteger(CustomJMSXProperty.JMS_QPID_DESTTYPE.getShortStringName(), type);
        //Set JMS_ANDES_ROUTING_KEY
        if (null != System.getProperty("AndesSetRoutingKey")) {
            delegate.getContentHeaderProperties().getHeaders().setString(
                CustomJMSXProperty.JMS_ANDES_ROUTING_KEY.getShortStringName(), destination.getRoutingKey().asString());
        }

        if (!_disableTimestamps)
        {
            final long currentTime = System.currentTimeMillis();
            contentHeaderProperties.setTimestamp(currentTime);

            if (timeToLive > 0)
            {
                contentHeaderProperties.setExpiration(currentTime + timeToLive);
            }
            else
            {
                contentHeaderProperties.setExpiration(0);
            }
        }

        contentHeaderProperties.setDeliveryMode((byte) deliveryMode);
        contentHeaderProperties.setPriority((byte) priority);

        final int size = (payload != null) ? payload.limit() : 0;
        final int contentBodyFrameCount = calculateContentBodyFrameCount(payload);
        final AMQFrame[] frames = new AMQFrame[2 + contentBodyFrameCount];

        if (payload != null)
        {
            createContentBodies(payload, frames, 2, _channelId);
        }

        if ((contentBodyFrameCount != 0) && _logger.isDebugEnabled())
        {
            _logger.debug("Sending content body frames to " + destination);
        }


        // TODO: This is a hacky way of getting the AMQP class-id for the Basic class
        int classIfForBasic = getSession().getMethodRegistry().createBasicQosOkBody().getClazz();

        AMQFrame contentHeaderFrame =
            ContentHeaderBody.createAMQFrame(_channelId,
                                             classIfForBasic, 0, contentHeaderProperties, size);
        if (_logger.isDebugEnabled())
        {
            _logger.debug("Sending content header frame to " + destination);
        }

        frames[0] = publishFrame;
        frames[1] = contentHeaderFrame;
        CompositeAMQDataBlock compositeFrame = new CompositeAMQDataBlock(frames);

        try
        {
            _session.checkFlowControl();
        }
        catch (InterruptedException e)
        {
            JMSException jmse = new JMSException("Interrupted while waiting for flow control to be removed");
            jmse.setLinkedException(e);
            jmse.initCause(e);
            throw jmse;
        }

        _protocolHandler.writeFrame(compositeFrame, wait);
    }

    /**
     * Create content bodies. This will split a large message into numerous bodies depending on the negotiated
     * maximum frame size.
     *
     * @param payload
     * @param frames
     * @param offset
     * @param channelId @return the array of content bodies
     */
    private void createContentBodies(ByteBuffer payload, AMQFrame[] frames, int offset, int channelId)
    {

        if (frames.length == (offset + 1))
        {
            frames[offset] = ContentBody.createAMQFrame(channelId, new ContentBody(payload));
        }
        else
        {

            final long framePayloadMax = _session.getAMQConnection().getMaximumFrameSize() - 1;
            long remaining = payload.remaining();
            for (int i = offset; i < frames.length; i++)
            {
                payload.position((int) framePayloadMax * (i - offset));
                int length = (remaining >= framePayloadMax) ? (int) framePayloadMax : (int) remaining;
                payload.limit(payload.position() + length);
                frames[i] = ContentBody.createAMQFrame(channelId, new ContentBody(payload.slice()));

                remaining -= length;
            }
        }

    }

    private int calculateContentBodyFrameCount(ByteBuffer payload)
    {
        // we substract one from the total frame maximum size to account for the end of frame marker in a body frame
        // (0xCE byte).
        int frameCount;
        if ((payload == null) || (payload.remaining() == 0))
        {
            frameCount = 0;
        }
        else
        {
            int dataLength = payload.remaining();
            final long framePayloadMax = _session.getAMQConnection().getMaximumFrameSize() - 1;
            int lastFrame = ((dataLength % framePayloadMax) > 0) ? 1 : 0;
            frameCount = (int) (dataLength / framePayloadMax) + lastFrame;
        }

        return frameCount;
    }

}

@@ -2135,32 +2135,103 @@
 tination)%0A    %7B%0A
+        String exchangeName = destination.getExchangeName().toString();
 %0A        Exchang
@@ -2668,37 +2668,63 @@
                 
-false
+exchangeName.startsWith(%22amq.%22)
 ,%0A              

Fix AMQP exchange declare invocation