package org.jgroups.util;

import java.io.*;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;

/**
 * Class using {@link ByteBuffer} and implementing {@link DataInput}.
 * @author Bela Ban
 * @since  3.5
 */
public class ByteBufferInputStream implements DataInput {
    protected final ByteBuffer buf;

    public ByteBufferInputStream(ByteBuffer buf) {
        this.buf=buf;
    }

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

    public void readFully(byte[] b, int off, int len) throws IOException {
        try {
            buf.get(b, off, len);
        } catch (BufferUnderflowException e) {
            throw new EOFException();
        }
    }


    public int skipBytes(int n) throws IOException {
        int skip=Math.min(n, buf.limit() - buf.position());
        buf.position(buf.position() + skip);
        return skip;
    }

    public boolean readBoolean() throws IOException {
        return readByte() == 1;
    }

    public byte readByte() throws IOException {
        try {
            return buf.get();
        } catch (BufferUnderflowException e) {
            throw new EOFException();
        }
    }

    public int readUnsignedByte() throws IOException {
        int ch=readByte() & 0xff;
        if(ch < 0)
            throw new EOFException();
        return ch;
    }

    public short readShort() throws IOException {
        try {
            return buf.getShort();
        } catch (BufferUnderflowException e) {
            throw new EOFException();
        }
    }

    public int readUnsignedShort() throws IOException {
        int retval=readShort() & 0xffff;
        if(retval < 0)
            throw new EOFException();
        return retval;
    }

    public char readChar() throws IOException {
        try {
            return buf.getChar();
        } catch (BufferUnderflowException e) {
            throw new EOFException();
        }
    }

    public int readInt() throws IOException {
        try {
            return buf.getInt();
        } catch (BufferUnderflowException e) {
            throw new EOFException();
        }
    }

    public long readLong() throws IOException {
        try {
            return buf.getLong();
        } catch (BufferUnderflowException e) {
            throw new EOFException();
        }
    }

    public float readFloat() throws IOException {
        try {
            return buf.getFloat();
        } catch (BufferUnderflowException e) {
            throw new EOFException();
        }
    }

    public double readDouble() throws IOException {
        try {
            return buf.getDouble();
        } catch (BufferUnderflowException e) {
            throw new EOFException();
        }
    }

    public String readLine() throws IOException {
        char[] lineBuffer=new char[128];
        char buffer[] = lineBuffer;

        if (buffer == null) {
            buffer = lineBuffer = new char[128];
        }

        int room = buffer.length;
        int offset = 0;
        int c;

        loop:	while (true) {
            switch (c = readByte()) {
                case -1:
                case '\n':
                    break loop;

                case '\r':
                    int c2 = readByte();
                    if ((c2 != '\n') && (c2 != -1))
                        ;
                    break loop;

                default:
                    if (--room < 0) {
                        buffer = new char[offset + 128];
                        room = buffer.length - offset - 1;
                        System.arraycopy(lineBuffer, 0, buffer, 0, offset);
                        lineBuffer = buffer;
                    }
                    buffer[offset++] = (char) c;
                    break;
            }
        }
        if ((c == -1) && (offset == 0)) {
            return null;
        }
        return String.copyValueOf(buffer, 0, offset);
    }

    public String readUTF() throws IOException {
        int utflen = readUnsignedShort();
        byte[] bytearr = new byte[utflen];
        char[] chararr = new char[utflen];

        int c, char2, char3;
        int count = 0;
        int chararr_count=0;

        readFully(bytearr,0,utflen);

        while (count < utflen) {
            c = (int) bytearr[count] & 0xff;
            if (c > 127) break;
            count++;
            chararr[chararr_count++]=(char)c;
        }

        while (count < utflen) {
            c = (int) bytearr[count] & 0xff;
            switch (c >> 4) {
                case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
                    /* 0xxxxxxx*/
                    count++;
                    chararr[chararr_count++]=(char)c;
                    break;
                case 12: case 13:
                    /* 110x xxxx   10xx xxxx*/
                    count += 2;
                    if (count > utflen)
                        throw new UTFDataFormatException(
                          "malformed input: partial character at end");
                    char2 = (int) bytearr[count-1];
                    if ((char2 & 0xC0) != 0x80)
                        throw new UTFDataFormatException(
                          "malformed input around byte " + count);
                    chararr[chararr_count++]=(char)(((c & 0x1F) << 6) |
                      (char2 & 0x3F));
                    break;
                case 14:
                    /* 1110 xxxx  10xx xxxx  10xx xxxx */
                    count += 3;
                    if (count > utflen)
                        throw new UTFDataFormatException(
                          "malformed input: partial character at end");
                    char2 = (int) bytearr[count-2];
                    char3 = (int) bytearr[count-1];
                    if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80))
                        throw new UTFDataFormatException(
                          "malformed input around byte " + (count-1));
                    chararr[chararr_count++]=(char)(((c     & 0x0F) << 12) |
                      ((char2 & 0x3F) << 6)  |
                      ((char3 & 0x3F) << 0));
                    break;
                default:
                    /* 10xx xxxx,  1111 xxxx */
                    throw new UTFDataFormatException(
                      "malformed input around byte " + count);
            }
        }
        // The number of chars produced may be less than utflen
        return new String(chararr, 0, chararr_count);
    }

    public String toString() {
        return buf.toString();
    }
}
