package lsr.paxos.messages;

import java.io.DataInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;

import lsr.paxos.storage.ConsensusInstance;

/**
 * Represents the response to <code>Prepare</code> message. It contains view
 * number and list of consensus instances. If recovery with epoch vector is
 * used, then epoch vector is also sent.
 */
public class PrepareOK extends Message {
    private static final long serialVersionUID = 1L;
    private final ConsensusInstance[] prepared;
    private final long[] epoch;

    /**
     * Creates new <code>PrepareOK</code> message without epoch vector.
     * 
     * @param view - sender view number
     * @param prepared - list of prepared consensus instances
     */
    public PrepareOK(int view, ConsensusInstance[] prepared) {
        this(view, prepared, new long[0]);
    }

    /**
     * Creates new <code>PrepareOK</code> message with epoch vector.
     * 
     * @param view - sender view number
     * @param prepared - list of prepared consensus instances
     * @param epoch - the epoch vector
     */
    public PrepareOK(int view, ConsensusInstance[] prepared, long[] epoch) {
        super(view);
        assert epoch != null;
        this.prepared = prepared;
        this.epoch = epoch;
    }

    /**
     * Creates new <cod>PrepareOK</code> message from serialized stream.
     * 
     * @param input - the input stream with serialized message
     * @throws IOException if I/O error occurs
     */
    public PrepareOK(DataInputStream input) throws IOException {
        super(input);
        prepared = new ConsensusInstance[input.readInt()];
        for (int i = 0; i < prepared.length; ++i) {
            prepared[i] = new ConsensusInstance(input);
        }

        int epochSize = input.readInt();
        epoch = new long[epochSize];
        for (int i = 0; i < epoch.length; ++i) {
            epoch[i] = input.readLong();
        }
    }

    /**
     * Returns prepared list of consensus instances.
     * 
     * @return prepared list of consensus instances.
     */
    public ConsensusInstance[] getPrepared() {
        return prepared;
    }

    /**
     * Returns epoch vector. This value should never be equal to
     * <code>null</code>. If this message doesn't contain epoch vector, then it
     * will be represented as empty array.
     * 
     * @return epoch vector
     */
    public long[] getEpoch() {
        return epoch;
    }

    public MessageType getType() {
        return MessageType.PrepareOK;
    }

    public int byteSize() {
        int size = super.byteSize() + 4;
        for (ConsensusInstance ci : prepared) {
            size += ci.byteSize();
        }

        size += epoch.length * 8 + 4;

        return size;
    }

    public String toString() {
        return "PrepareOK(" + super.toString() + ", values: " + Arrays.toString(getPrepared()) +
               ")";
    }

    protected void write(ByteBuffer bb) {
        bb.putInt(prepared.length);
        for (ConsensusInstance ci : prepared) {
            ci.write(bb);
        }

        bb.putInt(epoch.length);
        for (int i = 0; i < epoch.length; ++i) {
            bb.putLong(epoch[i]);
        }
    }
}
