/** 
 * 
 * Copyright 2004 Protique Ltd
 * Copyright 2004 Hiram Chirino
 * 
 * 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.DataInput;
import java.io.DataInputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.ObjectStreamException;

import org.activeio.PacketData;
import org.activeio.adapter.PacketByteArrayOutputStream;
import org.activeio.adapter.PacketInputStream;
import org.activemq.io.WireFormat;
import org.activemq.message.CachedValue;
import org.activemq.message.Packet;

/**
 * Provides a stateless implementation of AbstractDefaultWireFormat.  Safe for use by multiple threads and incurs no locking overhead.
 * 
 * @version $Revision: 1.1.1.1 $
 */
public class StatelessDefaultWireFormat extends AbstractDefaultWireFormat {

    public Packet writePacket(Packet packet, DataOutput dataOut) throws IOException {
        
        PacketWriter writer = getWriter(packet);
        if (writer != null) {
            
            PacketByteArrayOutputStream internalBytesOut = new PacketByteArrayOutputStream( 50+ (packet.getMemoryUsage()==0 ? 1024 : packet.getMemoryUsage()) );
            DataOutputStream internalDataOut = new DataOutputStream(internalBytesOut);            
            writer.writePacket(packet, internalDataOut);
            internalDataOut.close();
            
            org.activeio.Packet p = internalBytesOut.getPacket();
            int count = p.remaining();

            dataOut.writeByte(packet.getPacketType());
            dataOut.writeInt(count);
            packet.setMemoryUsage(count);
            p.writeTo(dataOut);
            
        }
        return null;
    }

    /**
     * Write a Packet to a PacketByteArrayOutputStream
     * 
     * @param packet
     * @param dataOut
     * @return a response packet - or null
     * @throws IOException
     */
    public org.activeio.Packet writePacket(Packet packet, PacketByteArrayOutputStream paos) throws IOException {
        PacketWriter writer = getWriter(packet);
        if (writer != null) {
            
            // We may not be writing to the start of the PAOS.
            int startPosition = paos.position();        
            // Skip space for the headers.
            paos.skip(5);
            // Stream the data.
            DataOutputStream data = new DataOutputStream(paos);
            writer.writePacket(packet, data);
            data.close();
            org.activeio.Packet rc = paos.getPacket();
            
            int count = rc.remaining()-(startPosition+5);
            packet.setMemoryUsage(count);
            
            // Now write the headers to the packet.
            
            rc.position(startPosition);
            PacketData pd = new PacketData(rc);
            pd.writeByte(packet.getPacketType());
            pd.writeInt(count);
            rc.rewind();
            return rc;
        }
        return null;
    }
 
    /**
     * A helper method which converts a packet into a byte array Overrides the WireFormat to make use of the internal
     * BytesOutputStream
     * 
     * @param packet
     * @return a byte array representing the packet using some wire protocol
     * @throws IOException
     */
    public byte[] toBytes(Packet packet) throws IOException {
        
        byte[] data = null;
        PacketWriter writer = getWriter(packet);
        if (writer != null) {
            
            // Try to guess the right size.            
            ByteArrayOutputStream internalBytesOut = new ByteArrayOutputStream( 50+ (packet.getMemoryUsage()==0 ? 1024 : packet.getMemoryUsage()) );
            DataOutputStream internalDataOut = new DataOutputStream(internalBytesOut);
            
            internalBytesOut.reset();
            internalDataOut.writeByte(packet.getPacketType());
            internalDataOut.writeInt(-1);//the length
            writer.writePacket(packet, internalDataOut);
            internalDataOut.flush();
            data = internalBytesOut.toByteArray();
            // lets subtract the header offset from the length
            int length = data.length - 5;
            packet.setMemoryUsage(length);
            //write in the length to the data
            data[1] = (byte) ((length >>> 24) & 0xFF);
            data[2] = (byte) ((length >>> 16) & 0xFF);
            data[3] = (byte) ((length >>> 8) & 0xFF);
            data[4] = (byte) ((length >>> 0) & 0xFF);
        }
        return data;
    }
    
    protected Packet readPacket(DataInput dataIn, PacketReader reader) throws IOException {
        Packet packet = reader.createPacket();
        int length = dataIn.readInt();
        packet.setMemoryUsage(length);
        reader.buildPacket(packet, dataIn);
        return packet;
    }

    /**
     * @param dataIn
     * @return
     * @throws IOException
     */
    public Packet readPacket(org.activeio.Packet dataIn) throws IOException {
        return readPacket(new DataInputStream(new PacketInputStream(dataIn)));
    }

    protected void handleCachedValue(CachedValue cv) {
        throw new IllegalStateException("Value caching is not supported.");
    }

    public Object getValueFromReadCache(short key) {
        throw new IllegalStateException("Value caching is not supported.");
    }

    short getWriteCachedKey(Object value) {
        throw new IllegalStateException("Value caching is not supported.");
    }
    
    public boolean isCachingEnabled() {
        return false;
    }

    public WireFormat copy() {
        return new StatelessDefaultWireFormat();
    }
    
    private Object readResolve() throws ObjectStreamException {
        return new DefaultWireFormat();
    }

}
