package anastore.util;

import java.io.*;
import java.util.ArrayList;
import java.util.List;

import static anastore.util.StreamExpectations.*;

/**
 * A specialized object input stream for use with streams encoded by a
 * {@link FastObjectOutputStream}.
 */
public class FastObjectInputStream extends ObjectInputStream
{
    private final static boolean LOG = false;
    private final static Log log = new Log(LOG);

    public FastObjectInputStream(InputStream in)
        throws IOException
    {
        super(in);
    }

    private StreamExpectations _exp = new StreamExpectations();

    private final List<ObjectStreamClass> _refs =
        new ArrayList<ObjectStreamClass>();

    private ObjectStreamClass readDescriptor(boolean isRef)
        throws IOException, ClassNotFoundException
    {
        if (isRef) {
            int id = readInt();
            return _refs.get(id);
        } else {
            String name = readUTF();
            Class<?> cl = Class.forName(name);
            ObjectStreamClass desc = ObjectStreamClass.lookup(cl);
            _refs.add(desc);
            return desc;
        }
    }

    protected ObjectStreamClass readClassDescriptor()
        throws IOException, ClassNotFoundException
    {
        byte code = readByte();
        ObjectStreamClass res;

        switch ((int)code) {
        case CODE_EXPECTED:
            res = _exp.takeExpectation();
            if (LOG)
                log.println("Read expected descriptor " + res.getName());
            break;
        case CODE_SUBCLASS:
        case CODE_SUBCLASS_REF:
            {
                ObjectStreamClass supercl = _exp.takeExpectation();
                res = readDescriptor(code == CODE_SUBCLASS_REF);
                if (LOG)
                    log.println("Read subclass descriptor " + res.getName());
                if (!supercl.forClass().isAssignableFrom(res.forClass()))
                    throw new StreamCorruptedException
                        (res.getName() + " is not a subclass of " +
                         supercl.getName());
            }
            break;
        case CODE_SKIP:
            {
                int count = readInt();
                res = _exp.skipBy(count);
                if (LOG)
                    log.println("Read skip " + count +
                                " descriptors to " + res.getName());
            }
            break;
        case CODE_UNEXPECTED:
        case CODE_UNEXPECTED_REF:
            {
                _exp.clear();
                res = readDescriptor(code == CODE_UNEXPECTED_REF);
                if (LOG)
                    log.println("Read unexpected descriptor " + res.getName());
            }
            break;
        default:
            throw new StreamCorruptedException("Got illegal descriptor code " + code);
        }
        _exp.addExpectations(res);
        return res;
    }

    /**
     * Only for testing.
     */
    StreamExpectations getExpectations()
    {
        return _exp;
    }
}
