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

import java.io.EOFException;
import java.io.IOException;


/**
 * Used to write and read primitives to and from a Packet.
 */
final public class PacketData {

    final private Packet packet;
    private final boolean bigEndian;

    public PacketData(Packet packet) {
        this(packet, true);
    }

    public PacketData(Packet packet, boolean bigEndian) {
        this.packet = packet;
        this.bigEndian = bigEndian;
    }

    private void spaceNeeded(int space) throws IOException {
        if (packet.remaining() < space)
            throw new EOFException("Not enough space left in the packet.");
    }

    public void readFully(byte[] b) throws IOException {
        readFully(b, 0, b.length);
    }

    public void readFully(byte[] b, int off, int len) throws IOException {
        spaceNeeded(len);
        packet.read(b, off, len);
    }

    public int skipBytes(int n) throws IOException {
        int rc = Math.min(n, packet.remaining());
        packet.position(packet.position() + rc);
        return rc;
    }

    public boolean readBoolean() throws IOException {
        spaceNeeded(1);
        return packet.read() != 0;
    }

    public byte readByte() throws IOException {
        spaceNeeded(1);
        return (byte) packet.read();
    }

    public int readUnsignedByte() throws IOException {
        spaceNeeded(1);
        return packet.read();
    }

    public short readShort() throws IOException {
        spaceNeeded(2);
        if( bigEndian ) {
            return (short) ((packet.read() << 8) + (packet.read() << 0));
        } else {
	        return (short) ((packet.read() << 0) + (packet.read() << 8) );
        }        
    }

    public int readUnsignedShort() throws IOException {
        spaceNeeded(2);
        if( bigEndian ) {
            return ((packet.read() << 8) + (packet.read() << 0));
        } else {
	        return ((packet.read() << 0) + (packet.read() << 8) );
        }        
    }

    public char readChar() throws IOException {
        spaceNeeded(2);
        if( bigEndian ) {
            return (char) ((packet.read() << 8) + (packet.read() << 0));
        } else {
	        return (char) ((packet.read() << 0) + (packet.read() << 8) );
        }        
    }

    public int readInt() throws IOException {
        spaceNeeded(4);
        if( bigEndian ) {
	        return ((packet.read() << 24) + 
	                (packet.read() << 16) + 
	                (packet.read() << 8) + 
	                (packet.read() << 0));
        } else {
	        return ((packet.read() << 0) +
	                (packet.read() << 8) + 
	                (packet.read() << 16) + 
	                (packet.read() << 24));
        }        
    }    
    
    public long readLong() throws IOException {
        spaceNeeded(8);
        if( bigEndian ) {
	        return (((long) packet.read() << 56) + 
	                ((long) packet.read() << 48) + 
	                ((long) packet.read() << 40) + 
	                ((long) packet.read() << 32) + 
	                ((long) packet.read() << 24) + 
	                ((packet.read()) << 16) + 
	                ((packet.read()) << 8) + 
	                ((packet.read()) << 0));
        } else {
	        return ((packet.read() << 0) +
	                (packet.read() << 8) + 
	                (packet.read() << 16) + 
	                ((long) packet.read() << 24) +
	                ((long) packet.read() << 32) + 
	                ((long) packet.read() << 40) + 
	                ((long) packet.read() << 48) + 
	                ((long) packet.read() << 56));	                
        }        
    }
    
    public double readDouble() throws IOException {
        return Double.longBitsToDouble(readLong());
    }

    public float readFloat() throws IOException {
        return Float.intBitsToFloat(readInt());
    }

    public void write(int b) throws IOException {
        spaceNeeded(1);
        packet.write(b);
    }

    public void write(byte[] b) throws IOException {
        write(b, 0, b.length);
    }

    public void write(byte[] b, int off, int len) throws IOException {
        spaceNeeded(len);
        packet.write(b, off, len);
    }

    public void writeBoolean(boolean v) throws IOException {
        spaceNeeded(1);
        packet.write(v ? 1 : 0);
    }

    public void writeByte(int v) throws IOException {
        spaceNeeded(1);
        packet.write(v);
    }

    public void writeShort(int v) throws IOException {
        spaceNeeded(2);
        
        if (bigEndian) {
	        packet.write((v >>> 8) & 0xFF);
	        packet.write((v >>> 0) & 0xFF);
	    } else {
	        packet.write((v >>> 0) & 0xFF);
	        packet.write((v >>> 8) & 0xFF);
	    }
    }

    public void writeChar(int v) throws IOException {
        spaceNeeded(2);
        if (bigEndian) {
	        packet.write((v >>> 8) & 0xFF);
	        packet.write((v >>> 0) & 0xFF);
        } else {
            packet.write((v >>> 0) & 0xFF);
            packet.write((v >>> 8) & 0xFF);
        }
    }

    public void writeInt(int v) throws IOException {
        spaceNeeded(4);
        if (bigEndian) {
            packet.write((v >>> 24) & 0xFF);
            packet.write((v >>> 16) & 0xFF);
            packet.write((v >>> 8) & 0xFF);
            packet.write((v >>> 0) & 0xFF);
        } else {
            packet.write((v >>> 0) & 0xFF);
            packet.write((v >>> 8) & 0xFF);
            packet.write((v >>> 16) & 0xFF);
            packet.write((v >>> 24) & 0xFF);
        }
    }

    public void writeLong(long v) throws IOException {
        spaceNeeded(8);
        if (bigEndian) {
	        packet.write((int) (v >>> 56) & 0xFF);
	        packet.write((int) (v >>> 48) & 0xFF);
	        packet.write((int) (v >>> 40) & 0xFF);
	        packet.write((int) (v >>> 32) & 0xFF);
	        packet.write((int) (v >>> 24) & 0xFF);
	        packet.write((int) (v >>> 16) & 0xFF);
	        packet.write((int) (v >>> 8) & 0xFF);
	        packet.write((int) (v >>> 0) & 0xFF);
        } else {
            packet.write((int) (v >>> 0) & 0xFF);
            packet.write((int) (v >>> 8) & 0xFF);
            packet.write((int) (v >>> 16) & 0xFF);
            packet.write((int) (v >>> 24) & 0xFF);
            packet.write((int) (v >>> 32) & 0xFF);
            packet.write((int) (v >>> 40) & 0xFF);
            packet.write((int) (v >>> 48) & 0xFF);
            packet.write((int) (v >>> 56) & 0xFF);
        }
    }
    
    public void writeDouble(double v) throws IOException {
        writeLong(Double.doubleToLongBits(v));
    }

    public void writeFloat(float v) throws IOException {
        writeInt(Float.floatToIntBits(v));
    }
    
    public void writeRawDouble(double v) throws IOException {
        writeLong(Double.doubleToRawLongBits(v));
    }

    public void writeRawFloat(float v) throws IOException {
        writeInt(Float.floatToRawIntBits(v));
    }

}
