/*
 *******************************************************************************
 * Copyright (C) 1996-2012, International Business Machines Corporation and    *
 * others. All Rights Reserved.                                                *
 *******************************************************************************
 */
package com.ibm.icu.dev.util;

import java.io.DataInput;
import java.io.IOException;
import java.io.ObjectInput;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;

import com.ibm.icu.text.UTF16;

/**
 * Simple data input compressor. Nothing fancy, but much smaller footprint for
 * ints and many strings.
 */
public final class DataInputCompressor implements ObjectInput {
    static final boolean SHOW = false;

    private ObjectInput dataInput;

    private transient StringBuffer stringBuffer = new StringBuffer();

    public DataInputCompressor(ObjectInput dataInput) {
        this.dataInput = dataInput;
    }

    public DataInput getDataInput() {
        return dataInput;
    }

    public void setDataInput(ObjectInput dataInput) {
        this.dataInput = dataInput;
    }

    public boolean readBoolean() throws IOException {
        return dataInput.readBoolean();
    }

    public byte readByte() throws IOException {
        return dataInput.readByte();
    }

    public int readUnsignedByte() throws IOException {
        return dataInput.readUnsignedByte();
    }

    public double readDouble() throws IOException {
        return dataInput.readDouble();
    }

    public float readFloat() throws IOException {
        return dataInput.readFloat();
    }

    public void readFully(byte[] b) throws IOException {
        dataInput.readFully(b);
    }

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

    public int skipBytes(int n) throws IOException {
        return dataInput.skipBytes(n);
    }

    public String readLine() throws IOException {
        return dataInput.readLine();
    }

    public int available() throws IOException {
        return dataInput.available();
    }
    public void close() throws IOException {
        dataInput.close();
    }
    public int read() throws IOException {
        return dataInput.read();
    }
    public int read(byte[] b) throws IOException {
        return dataInput.read(b);
    }
    public int read(byte[] b, int off, int len) throws IOException {
        return dataInput.read(b, off, len);
    }
    public Object readObject() throws ClassNotFoundException, IOException {
        return dataInput.readObject();
    }
    public long skip(long n) throws IOException {
        return dataInput.skip(n);
    }
    public String toString() {
        return dataInput.toString();
    }
    // ==== New Routines ====

    public char readChar() throws IOException {
        return (char) readULong();
    }

    public short readShort() throws IOException {
        return (short) readLong();
    }

    public int readUnsignedShort() throws IOException {
        return (int) readULong();
    }

    public int readUShort() throws IOException {
        return (int) readULong();
    }

    public int readInt() throws IOException {
        return (int) readLong();
    }

    public int readUInt() throws IOException {
        return (int) readULong();
    }

    public String readChars(int len) throws IOException {
        stringBuffer.setLength(0);
        for (int i = 0; i < len; ++i) {
            int cp = (int) readULong();
            UTF16.append(stringBuffer, cp);
        }
        return stringBuffer.toString();
    }

    public String readUTF() throws IOException {
        int len = (int) readULong();
        return readChars(len);
    }

    public long readLong() throws IOException {
        long result = 0;
        int offset = 0;
        while (true) {
            long input = readByte();
            result |= (input & 0x7F) << offset;
            if ((input & 0x80) == 0)
                break;
            offset += 7;
        }
        boolean negative = (result & 1) != 0; // get sign bit from the bottom,
                                              // and invert
        result >>>= 1;
        if (negative)
            result = ~result;
        return result;
    }

    public long readULong() throws IOException {
        long result = 0;
        int offset = 0;
        while (true) { // read sequence of 7 bits, with top bit = 1 for
                       // continuation
            int input = readByte();
            result |= (input & 0x7F) << offset;
            if ((input & 0x80) == 0)
                return result;
            offset += 7;
        }
    }

    /**
     *  
     */
    public Object[] readStringSet(Collection availableValues)
            throws IOException {
        int size = readUInt();
        if (SHOW) System.out.println("readStringSet");
        Object[] valuesList = new Object[size + 1];
        // first item is null
        String lastString = "";
        ReadPool trailingPool = new ReadPool();
        for (int i = 0; i < size; ++i) {
            int common = readUInt();
            boolean inPool = (common & 1) != 0;
            common >>>= 1;
            if (SHOW) System.out.println(common);
            String current;
            if (inPool) {
                int poolIndex = readUInt();
                if (SHOW) System.out.println("\t" + poolIndex);
                current = (String) trailingPool.get(poolIndex);
            } else {
                current = readUTF();
                trailingPool.add(current);
            }
            valuesList[i + 1] = lastString = lastString.substring(0, common)
                    + current;
            if (SHOW) System.out.println("\t\t" + lastString);
            if (availableValues != null) availableValues.add(current);
        }
        return valuesList;
    }
    
    public static class ReadPool {
        private List trailingPool = new ArrayList();
        public Object get(int index) {
            return trailingPool.get(index);
        }
        public void add(Object o) {
            trailingPool.add(o);
        }
    }

    /**
     * @throws IOException
     * @throws ClassNotFoundException
     * 
     */
    public Object[] readCollection(LinkedHashSet availableValues) throws ClassNotFoundException, IOException {
        int size = readUInt();
        Object[] valuesList = new Object[size + 1];
        for (int i = 0; i < size; ++i) {
            valuesList[i + 1] = readObject();
        }
       return valuesList;
    }
}
