/** 
 * 
 * Copyright 2004 Protique Ltd
 * 
 * Licensed 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.codehaus.activemq.message;

import java.util.Iterator;
import org.codehaus.activemq.util.BitArray;
import EDU.oswego.cs.dl.util.concurrent.CopyOnWriteArraySet;

/**
 * Abstract class for a transportable Packet
 *
 * @version $Revision: 1.4 $
 */
public abstract class AbstractPacket implements Packet {
    
    /**
     * Message flag indexes (used for writing/reading to/from a Stream
     */
    public static final int RECEIPT_REQUIRED_INDEX = 0;
    public static final int BROKERS_VISITED_INDEX =1;
    private String id;
    protected BitArray bitArray;
    private  boolean receiptRequired;
    private transient int memoryUsage = 2048;
    private transient int memoryUsageReferenceCount;

    private CopyOnWriteArraySet brokersVisited;
   
    protected AbstractPacket(){
        this.bitArray = new BitArray();
    }

    /**
     * @return the unique id for this Packet
     */
    public String getId() {
        return this.id;
    }

    /**
     * Set the unique id for this Packet
     *
     * @param newId
     */
    public void setId(String newId) {
        this.id = newId;
    }

    /**
     * @return true if a Recipt is required
     */
    public boolean isReceiptRequired() {
        return this.receiptRequired;
    }

    /**
     * @return false since most packets are not receipt packets
     */
    public boolean isReceipt() {
        return false;
    }

    /**
     * Set if a Recipt if required on receiving this Packet
     *
     * @param value
     */
    public void setReceiptRequired(boolean value) {
        this.receiptRequired = value;
    }

    /**
     * Retrieve if a JMS Message type or not
     *
     * @return true if it is a JMS Message
     */
    public boolean isJMSMessage() {
        return false;
    }

    /**
     * Tests equality with another instance
     *
     * @param obj - the other instance to test equality with
     * @return Returns true if the objects are equilvant
     */
    public boolean equals(Object obj) {
        boolean result = this == obj;
        if (!result && obj != null && obj instanceof AbstractPacket) {
            AbstractPacket other = (AbstractPacket) obj;
            result = other.getId().equals(this.getId());
        }
        return result;
    }

    /**
     * @return Returns hash code for this instance
     */
    public int hashCode() {
        return this.id != null ? this.id.hashCode() : super.hashCode();
    }

    /**
     * Get a hint about how much memory this Packet is consuming
     *
     * @return an aproximation of the current memory used by this instance
     */
    public int getMemoryUsage() {
        return memoryUsage;
    }

    /**
     * Set a hint about how mujch memory this packet is consuming
     *
     * @param newMemoryUsage
     */
    public void setMemoryUsage(int newMemoryUsage) {
        this.memoryUsage = newMemoryUsage;
    }

    /**
     * Increment reference count for bounded memory collections
     *
     * @return the incremented reference value
     * @see org.codehaus.activemq.message.util.MemoryBoundedQueue
     */
    public synchronized int incrementMemoryReferenceCount() {
        return ++memoryUsageReferenceCount;
    }

    /**
     * Decrement reference count for bounded memory collections
     *
     * @return the decremented reference value
     * @see org.codehaus.activemq.message.util.MemoryBoundedQueue
     */
    public synchronized int decrementMemoryReferenceCount() {
        return --memoryUsageReferenceCount;
    }

    /**
     * @return the current reference count for bounded memory collections
     * @see org.codehaus.activemq.message.util.MemoryBoundedQueue
     */
    public synchronized int getMemoryUsageReferenceCount() {
        return memoryUsageReferenceCount;
    }

    /**
     * As the packet passes through the broker add the broker to the visited list
     *
     * @param brokerName the name of the broker
     */
    public void addBrokerVisited(String brokerName) {
        initializeBrokersVisited();
        brokersVisited.add(brokerName);
    }

    /**
     * test to see if the named broker has already seen this packet
     *
     * @param brokerName the name of the broker
     * @return true if the packet has visited the broker
     */
    public boolean hasVisited(String brokerName) {
        if (brokersVisited == null){
            return false;
        }
        return brokersVisited.contains(brokerName);
    }

    /**
     * @return Returns the brokersVisited.
     */
    public String getBrokersVisitedAsString() {
        String result = "";
        if (brokersVisited != null && !brokersVisited.isEmpty()){
            for (Iterator i = brokersVisited.iterator(); i.hasNext();){
                result += i.next().toString() + ",";
            }
        }
        return result;
    }
    

    /**
     * @return pretty print of this Packet
     */
    public String toString() {
        return getPacketTypeAsString(getPacketType()) + ": id = " + getId();
    }


    public static String getPacketTypeAsString(int type) {
        String packetTypeStr = "";
        switch (type) {
            case ACTIVEMQ_MESSAGE:
                packetTypeStr = "ACTIVEMQ_MESSAGE";
                break;
            case ACTIVEMQ_TEXT_MESSAGE:
                packetTypeStr = "ACTIVEMQ_TEXT_MESSAGE";
                break;
            case ACTIVEMQ_OBJECT_MESSAGE:
                packetTypeStr = "ACTIVEMQ_OBJECT_MESSAGE";
                break;
            case ACTIVEMQ_BYTES_MESSAGE:
                packetTypeStr = "ACTIVEMQ_BYTES_MESSAGE";
                break;
            case ACTIVEMQ_STREAM_MESSAGE:
                packetTypeStr = "ACTIVEMQ_STREAM_MESSAGE";
                break;
            case ACTIVEMQ_MAP_MESSAGE:
                packetTypeStr = "ACTIVEMQ_MAP_MESSAGE";
                break;
            case ACTIVEMQ_MSG_ACK:
                packetTypeStr = "ACTIVEMQ_MSG_ACK";
                break;
            case RECEIPT_INFO:
                packetTypeStr = "RECEIPT_INFO";
                break;
            case CONSUMER_INFO:
                packetTypeStr = "CONSUMER_INFO";
                break;
            case PRODUCER_INFO:
                packetTypeStr = "PRODUCER_INFO";
                break;
            case TRANSACTION_INFO:
                packetTypeStr = "TRANSACTION_INFO";
                break;
            case XA_TRANSACTION_INFO:
                packetTypeStr = "XA_TRANSACTION_INFO";
                break;
            case ACTIVEMQ_BROKER_INFO:
                packetTypeStr = "ACTIVEMQ_BROKER_INFO";
                break;
            case ACTIVEMQ_CONNECTION_INFO:
                packetTypeStr = "ACTIVEMQ_CONNECTION_INFO";
                break;
            case SESSION_INFO:
                packetTypeStr = "SESSION_INFO";
                break;
            case DURABLE_UNSUBSCRIBE:
                packetTypeStr = "DURABLE_UNSUBSCRIBE";
                break;
            case RESPONSE_RECEIPT_INFO:
                packetTypeStr = "RESPONSE_RECEIPT_INFO";
                break;
            case INT_RESPONSE_RECEIPT_INFO:
                packetTypeStr = "INT_RESPONSE_RECEIPT_INFO";
                break;
            case CAPACITY_INFO:
                packetTypeStr = "CAPACITY_INFO";
                break;
            case CAPACITY_INFO_REQUEST:
                packetTypeStr = "CAPACITY_INFO_REQUEST";
                break;
            case WIRE_FORMAT_INFO:
                packetTypeStr = "WIRE_FORMAT_INFO";
                break;
            case KEEP_ALIVE:
                packetTypeStr = "KEEP_ALIVE";
                break;
            default :
                packetTypeStr = "UNKNOWN PACKET TYPE: " + type;
        }
        return packetTypeStr;
    }

    /**
     * A helper method used when implementing equals() which returns true if the objects are identical or equal handling
     * nulls properly
     * @param left
     * @param right
     *
     * @return true if the objects are the same or equal or both null
     */
    protected boolean equals(Object left, Object right) {
        return left == right || (left != null && left.equals(right));
    }
    
    /**
     * Initializes another message with current values from this instance
     *
     * @param other the other ActiveMQMessage to initialize
     */
    protected void initializeOther(AbstractPacket other) {
        initializeBrokersVisited();
        other.id = this.id;
        other.receiptRequired = this.receiptRequired;
        other.memoryUsage = this.memoryUsage;
        CopyOnWriteArraySet set = this.brokersVisited;
        if (set != null && !set.isEmpty()){
            other.brokersVisited = new CopyOnWriteArraySet(set);
        }
    }
    
    synchronized void initializeBrokersVisited(){
        if (this.brokersVisited == null){
            this.brokersVisited = new CopyOnWriteArraySet();
        }
    }

    /**
     * @return Returns the brokersVisited.
     */
    public Object[] getBrokersVisited() {
        if (brokersVisited == null || brokersVisited.isEmpty()){
            return null;
        }
        return brokersVisited.toArray();
    }

    /**
     * @return Returns the bitArray.
     */
    public BitArray getBitArray() {
        return bitArray;
    }
    /**
     * @param bitArray The bitArray to set.
     */
    public void setBitArray(BitArray bitArray) {
        this.bitArray = bitArray;
    }
}