/** 
 * 
 * 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.activemq.io.impl;
import java.io.ByteArrayOutputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import org.activemq.io.WireFormat;
import org.activemq.message.AbstractPacket;
import org.activemq.message.Packet;
import org.activemq.util.BitArray;
import org.activemq.util.SerializationHelper;

/**
 * Allows instances implementing Packet interface to be serailized/deserailized
 */
public abstract class AbstractPacketWriter implements PacketWriter {
    protected int wireFormatVersion = DefaultWireFormat.WIRE_FORMAT_VERSION;
    
    /**
     * simple helper method to ensure null strings are catered for
     *
     * @param str
     * @param dataOut
     * @throws IOException
     */
    protected void writeUTF(String str, DataOutput dataOut) throws IOException {
        if (str == null) {
            str = "";
        }
        dataOut.writeUTF(str);
    }

    /**
     * @param packet
     * @return true if this PacketWriter can write this type of Packet
     */
    public boolean canWrite(Packet packet) {
        return packet.getPacketType() == this.getPacketType();
    }

    /**
     * Simple (but inefficent) utility method to write an object on to a stream
     *
     * @param object
     * @param dataOut
     * @throws IOException
     */
    protected void writeObject(Object object, DataOutput dataOut) throws IOException {
        if (object != null) {            
            byte[] data = SerializationHelper.writeObject(object);
            dataOut.writeInt(data.length);
            dataOut.write(data);
        }
        else {
            dataOut.writeInt(0);
        }
    }

    /**
     * Serializes a Packet int a byte array
     *
     * @param packet
     * @return the byte[]
     * @throws IOException
     */
    public byte[] writePacketToByteArray(Packet packet) throws IOException {
        ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
        DataOutputStream dataOut = new DataOutputStream(bytesOut);
        writePacket(packet, dataOut);
        dataOut.flush();
        return bytesOut.toByteArray();
    }

    /**
     * Write a Packet instance to data output stream
     *
     * @param p  the instance to be seralized
     * @param dataOut the output stream
     * @throws IOException thrown if an error occurs
     */
    public void writePacket(Packet p, DataOutput dataOut) throws IOException {
        AbstractPacket packet = (AbstractPacket)p;
        dataOut.writeShort(packet.getId());
        BitArray ba = packet.getBitArray();
        ba.set(AbstractPacket.RECEIPT_REQUIRED_INDEX, packet.isReceiptRequired());
        Object[] visited = packet.getBrokersVisited();
        boolean writeVisited = visited != null && visited.length > 0;
        ba.set(AbstractPacket.BROKERS_VISITED_INDEX,writeVisited);
        ba.writeToStream(dataOut);
        if (writeVisited){
            dataOut.writeShort(visited.length);
            for(int i =0; i < visited.length; i++){
                dataOut.writeUTF(visited[i].toString());
            }
        }
    }
    
    /**
     * Set the wire format version
     * @param version
     */
    public void setWireFormatVersion(int version){
        this.wireFormatVersion = version;
    }
    
    /**
     * @return the wire format version
     */
    public int getWireFormatVersion(){
        return wireFormatVersion;
    }

}