
package org.jgroups.protocols.pbcast;


import org.jgroups.Address;
import org.jgroups.Global;
import org.jgroups.Header;
import org.jgroups.util.Range;
import org.jgroups.util.Util;

import java.io.*;


/**
 * @author Bela Ban
 */
public class NakAckHeader extends Header {
    public static final byte MSG=1;       // regular msg
    public static final byte XMIT_REQ=2;  // retransmit request
    public static final byte XMIT_RSP=3;  // retransmit response (contains one or more messages)

    byte  type=0;
    long  seqno=-1;        // seqno of regular message (MSG)
    Range range=null;      // range of msgs to be retransmitted (XMIT_REQ)
    Address sender;        // the original sender of the message (for XMIT_REQ)


    public NakAckHeader() {
    }


    public static NakAckHeader createMessageHeader(long seqno) {
        return new NakAckHeader(MSG, seqno);
    }

    public static NakAckHeader createXmitRequestHeader(long low, long high, Address orginal_sender) {
        return new NakAckHeader(XMIT_REQ, low, high, orginal_sender);
    }

    public static NakAckHeader createXmitResponseHeader() {
        return new NakAckHeader(XMIT_RSP, -1);
    }


    /**
     * Constructor for regular messages or XMIT responses
     */
    private NakAckHeader(byte type, long seqno) {
        this.type=type;
        this.seqno=seqno;
    }


    /**
     * Constructor for retransmit requests (XMIT_REQs) (low and high define the range of msgs)
     */
    private NakAckHeader(byte type, long low, long high, Address sender) {
        this.type=type;
        range=new Range(low, high);
        this.sender=sender;
    }

    public byte getType() {
        return type;
    }

    public long getSeqno() {
        return seqno;
    }

    public Range getRange() {
        return range;
    }

    public Address getSender() {
        return sender;
    }


    public void writeTo(DataOutput out) throws Exception {
        out.writeByte(type);
        switch(type) {
            case MSG:
            case XMIT_RSP:
                Util.writeLong(seqno, out);
                break;
            case XMIT_REQ:
                Util.writeStreamable(range, out);
                Util.writeAddress(sender, out);
                break;
        }
    }

    public void readFrom(DataInput in) throws Exception {
        type=in.readByte();
        switch(type) {
            case MSG:
            case XMIT_RSP:
                seqno=Util.readLong(in);
                break;
            case XMIT_REQ:
                range=(Range)Util.readStreamable(Range.class, in);
                sender=Util.readAddress(in);
                break;
        }
    }
    

    public int size() {
        int retval=Global.BYTE_SIZE; // type
        switch(type) {
            case MSG:
            case XMIT_RSP:
                return retval + Util.size(seqno);

            case XMIT_REQ:
                retval+=Global.BYTE_SIZE; // presence for range
                if(range != null)
                    retval+=range.serializedSize();
                retval+=Util.size(sender);
                return retval;
        }
        return retval;
    }


    public NakAckHeader copy() {
        NakAckHeader ret=new NakAckHeader();
        ret.type=type;
        ret.seqno=seqno;
        ret.range=range;
        ret.sender=sender;
        return ret;
    }


    public static String type2Str(byte t) {
        switch(t) {
            case MSG:
                return "MSG";
            case XMIT_REQ:
                return "XMIT_REQ";
            case XMIT_RSP:
                return "XMIT_RSP";
            default:
                return "<undefined>";
        }
    }


    public String toString() {
        StringBuilder ret=new StringBuilder();
        ret.append("[").append(type2Str(type));
        switch(type) {
            case MSG:
            case XMIT_RSP: // seqno and sender
                ret.append(", seqno=").append(seqno);
                break;
            case XMIT_REQ:  // range and sender
                if(range != null)
                    ret.append(", range=" + range);
                break;
        }

        if(sender != null) ret.append(", sender=").append(sender);
        ret.append(']');
        return ret.toString();
    }


}
