#ifndef _broker_BrokerChannel_h
#define _broker_BrokerChannel_h

/*
 *
 * 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.
 *
 */

#include <list>
#include <memory>

#include <boost/scoped_ptr.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/ptr_container/ptr_map.hpp>

#include "AccumulatedAck.h"
#include "Consumer.h"
#include "DeliveryAdapter.h"
#include "DeliveryRecord.h"
#include "DeliveryToken.h"
#include "Deliverable.h"
#include "DtxBuffer.h"
#include "DtxManager.h"
#include "MessageBuilder.h"
#include "NameGenerator.h"
#include "Prefetch.h"
#include "TxBuffer.h"
#include "qpid/framing/amqp_types.h"
#include "qpid/framing/ChannelAdapter.h"
#include "qpid/framing/ChannelOpenBody.h"
#include "CompletionHandler.h"

namespace qpid {
namespace broker {

class ConnectionToken;
class Connection;
class Queue;
class BrokerAdapter;

using framing::string;

/**
 * Maintains state for an AMQP channel. Handles incoming and
 * outgoing messages for that channel.
 */
class Channel : public CompletionHandler
{
    class ConsumerImpl : public Consumer
    {
        sys::Mutex lock;
        Channel* const parent;
        const DeliveryToken::shared_ptr token;
        const string name;
        const Queue::shared_ptr queue;
        const bool ackExpected;
        const bool nolocal;
        bool blocked;
        bool windowing;
        uint32_t msgCredit;
        uint32_t byteCredit;

      public:
        ConsumerImpl(Channel* parent, DeliveryToken::shared_ptr token, 
                     const string& name, Queue::shared_ptr queue, bool ack, bool nolocal);
        ~ConsumerImpl();
        bool deliver(Message::shared_ptr& msg);            
        void redeliver(Message::shared_ptr& msg, DeliveryId deliveryTag);
        void cancel();
        void requestDispatch();

        void setWindowMode();
        void setCreditMode();
        void addByteCredit(uint32_t value);
        void addMessageCredit(uint32_t value);
        void flush();
        void stop();
        bool checkCredit(Message::shared_ptr& msg);
        void acknowledged(const DeliveryRecord&);    
    };

    typedef boost::ptr_map<string,ConsumerImpl> ConsumerImplMap;

    framing::ChannelId id;
    Connection& connection;
    DeliveryAdapter& out;
    Queue::shared_ptr defaultQueue;
    ConsumerImplMap consumers;
    uint32_t prefetchSize;    
    uint16_t prefetchCount;    
    Prefetch outstanding;
    NameGenerator tagGenerator;
    std::list<DeliveryRecord> unacked;
    sys::Mutex deliveryLock;
    TxBuffer::shared_ptr txBuffer;
    DtxBuffer::shared_ptr dtxBuffer;
    bool dtxSelected;
    AccumulatedAck accumulatedAck;
    MessageStore* const store;
    MessageBuilder messageBuilder;//builder for in-progress message
    bool opened;
    bool flowActive;
    void route(Message::shared_ptr msg, Deliverable& strategy);
    void complete(Message::shared_ptr msg);// completion handler for MessageBuilder
    void record(const DeliveryRecord& delivery);
    bool checkPrefetch(Message::shared_ptr& msg);
    void checkDtxTimeout();
    ConsumerImpl& find(const std::string& destination);
    void ack(DeliveryId deliveryTag, DeliveryId endTag, bool cumulative);
    void acknowledged(const DeliveryRecord&);
        
  public:
    Channel(Connection& parent, DeliveryAdapter& out, framing::ChannelId id, MessageStore* const store = 0);    
    ~Channel();

    bool isOpen() const { return opened; }
    framing::ChannelId getId() const { return id; }
    
    void open() { opened = true; }
    void setDefaultQueue(Queue::shared_ptr queue){ defaultQueue = queue; }
    Queue::shared_ptr getDefaultQueue() const { return defaultQueue; }
    uint32_t setPrefetchSize(uint32_t size){ return prefetchSize = size; }
    uint16_t setPrefetchCount(uint16_t n){ return prefetchCount = n; }

    bool exists(const string& consumerTag);

    /**
     *@param tagInOut - if empty it is updated with the generated token.
     */
    void consume(DeliveryToken::shared_ptr token, string& tagInOut, Queue::shared_ptr queue, 
                 bool nolocal, bool acks, bool exclusive, const framing::FieldTable* = 0);
    void cancel(const string& tag);

    void setWindowMode(const std::string& destination);
    void setCreditMode(const std::string& destination);
    void addByteCredit(const std::string& destination, uint32_t value);
    void addMessageCredit(const std::string& destination, uint32_t value);
    void flush(const std::string& destination);
    void stop(const std::string& destination);

    bool get(DeliveryToken::shared_ptr token, Queue::shared_ptr queue, bool ackExpected);
    void close();
    void startTx();
    void commit();
    void rollback();
    void selectDtx();
    void startDtx(const std::string& xid, DtxManager& mgr, bool join);
    void endDtx(const std::string& xid, bool fail);
    void suspendDtx(const std::string& xid);
    void resumeDtx(const std::string& xid);
    void ackCumulative(DeliveryId deliveryTag);
    void ackRange(DeliveryId deliveryTag, DeliveryId endTag);
    void recover(bool requeue);
    void flow(bool active);
    void deliver(Message::shared_ptr& msg, const string& consumerTag, DeliveryId deliveryTag);            
    void handlePublish(Message* msg);
    void handleHeader(framing::AMQHeaderBody*);
    void handleContent(framing::AMQContentBody*);
    void handleHeartbeat(framing::AMQHeartbeatBody*);
    
    void handleInlineTransfer(Message::shared_ptr msg);
};

}} // namespace broker


#endif  /*!_broker_BrokerChannel_h*/
