/**
 * 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.
 */
package org.apache.activemq.apollo.openwire.codec;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.nio.ByteBuffer;

public final class BooleanStream {

    byte data[] = new byte[48];
    short arrayLimit;
    short arrayPos;
    byte bytePos;

    public boolean readBoolean() throws IOException {
        assert arrayPos <= arrayLimit;
        byte b = data[arrayPos];
        boolean rc = ((b >> bytePos) & 0x01) != 0;
        bytePos++;
        if (bytePos >= 8) {
            bytePos = 0;
            arrayPos++;
        }
        return rc;
    }

    public void writeBoolean(boolean value) throws IOException {
        if (bytePos == 0) {
            arrayLimit++;
            if (arrayLimit >= data.length) {
                // re-grow the array.
                byte d[] = new byte[data.length * 2];
                System.arraycopy(data, 0, d, 0, data.length);
                data = d;
            }
        }
        if (value) {
            data[arrayPos] |= 0x01 << bytePos;
        }
        bytePos++;
        if (bytePos >= 8) {
            bytePos = 0;
            arrayPos++;
        }
    }

    public void marshal(DataOutput dataOut) throws IOException {
        if (arrayLimit < 64) {
            dataOut.writeByte(arrayLimit);
        } else if (arrayLimit < 256) { // max value of unsigned byte
            dataOut.writeByte(0xC0);
            dataOut.writeByte(arrayLimit);
        } else {
            dataOut.writeByte(0x80);
            dataOut.writeShort(arrayLimit);
        }

        dataOut.write(data, 0, arrayLimit);
        clear();
    }

    public void marshal(ByteBuffer dataOut) {
        if (arrayLimit < 64) {
            dataOut.put((byte)arrayLimit);
        } else if (arrayLimit < 256) { // max value of unsigned byte
            dataOut.put((byte)0xC0);
            dataOut.put((byte)arrayLimit);
        } else {
            dataOut.put((byte)0x80);
            dataOut.putShort(arrayLimit);
        }

        dataOut.put(data, 0, arrayLimit);
    }

    public void unmarshal(DataInput dataIn) throws IOException {

        arrayLimit = (short)(dataIn.readByte() & 0xFF);
        if (arrayLimit == 0xC0) {
            arrayLimit = (short)(dataIn.readByte() & 0xFF);
        } else if (arrayLimit == 0x80) {
            arrayLimit = dataIn.readShort();
        }
        if (data.length < arrayLimit) {
            data = new byte[arrayLimit];
        }
        dataIn.readFully(data, 0, arrayLimit);
        clear();
    }

    public void clear() {
        arrayPos = 0;
        bytePos = 0;
    }

    public int marshalledSize() {
        if (arrayLimit < 64) {
            return 1 + arrayLimit;
        } else if (arrayLimit < 256) {
            return 2 + arrayLimit;
        } else {
            return 3 + arrayLimit;
        }
    }

}
