/*_############################################################################
  _## 
  _##  SNMP4J-AgentX - AgentXRegisterPDU.java  
  _## 
  _##  Copyright (C) 2005-2026  Frank Fock (SNMP4J.org)
  _##  
  _##  This program is free software; you can redistribute it and/or modify
  _##  it under the terms of the GNU General Public License version 2 as 
  _##  published by the Free Software Foundation.
  _##
  _##  This program is distributed in the hope that it will be useful,
  _##  but WITHOUT ANY WARRANTY; without even the implied warranty of
  _##  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  _##  GNU General Public License for more details.
  _##
  _##  You should have received a copy of the GNU General Public License
  _##  along with this program; if not, write to the Free Software
  _##  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 
  _##  MA  02110-1301  USA
  _##  
  _##########################################################################*/

package org.snmp4j.agent.agentx;

import java.io.*;
import java.nio.*;

import org.snmp4j.smi.*;

/**
 * The {@link AgentXRegisterPDU} represents the agentx-Register-PDU defined by RFC 2741.
 */
public class AgentXRegisterPDU extends AgentXContextPDU {

    private static final long serialVersionUID = 2377335776967076409L;

    /**
     * The length of time, in seconds, that the master agent should allow to elapse for requests on
     * the registered region before timing out. A value of zero indicates that the session or master
     * agent default should be used.
     */
    private byte timeout = 0;
    /**
     * The registration priority, where smaller values take precedence over larger ones.
     */
    private byte priority;
    /**
     * The sub-identifier index within the {@link #subtree} at which the {@link #upperBound} applies to
     * define a range registration, or zero if no range is registered.
     */
    private byte rangeSubID;
    /**
     * The object identifier of the subtree (MIB region) to be registered.
     */
    private OID subtree;
    /**
     * The upper bound of the range sub-identifier when {@link #rangeSubID} is non-zero.
     */
    private int upperBound;

    /**
     * Creates a {@link AgentXRegisterPDU}.
     * @param flags
     *    a set of AgentX flags, see {@link AgentXProtocol#FLAG_ALLOCATE_INDEX} for example.
     * @param sessionID
     *    a session ID.
     * @param transactionID
     *    a transaction ID.
     * @param packetID
     *    a packet ID.
     */
    public AgentXRegisterPDU(byte flags,
                             int sessionID, int transactionID, int packetID) {
        super(AGENTX_REGISTER_PDU, flags, sessionID, transactionID, packetID);
    }

    /**
     * Creates a {@link AgentXPingPDU} from a {@link AgentXMessageHeader}.
     * @param header
     *    the message header.
     */
    public AgentXRegisterPDU(AgentXMessageHeader header) {
        super(header);
    }

    /**
     * Creates a {@link AgentXRegisterPDU} from payload values.
     * @param context
     *   the SNMPv3 context.
     * @param subtree
     *   the sub-tree OID.
     * @param priority
     *   the registration priority
     * @param rangeSubID
     *   the index within the {@code subtree} {@link OID} that represents the range of objects ("wildcard").
     * @param upperBound
     *   the upper bound of the region.
     */
    public AgentXRegisterPDU(OctetString context, OID subtree,
                             byte priority,
                             byte rangeSubID,
                             int upperBound) {
        this(AGENTX_REGISTER_PDU, context, subtree, priority, rangeSubID, upperBound);
    }

    /**
     * Creates a {@link AgentXRegisterPDU} from payload values.
     * @param type
     *   the PDU type.
     * @param context
     *   the SNMPv3 context.
     * @param subtree
     *   the sub-tree OID.
     * @param priority
     *   the registration priority
     * @param rangeSubID
     *   the index within the {@code subtree} {@link OID} that represents the range of objects ("wildcard").
     * @param upperBound
     *   the upper bound of the region.
     */
    protected AgentXRegisterPDU(byte type, OctetString context, OID subtree,
                                byte priority,
                                byte rangeSubID,
                                int upperBound) {
        super(type, context);
        this.priority = priority;
        this.subtree = subtree;
        this.rangeSubID = rangeSubID;
        this.upperBound = upperBound;
    }

    @Override
    public void decodeAfterContext(ByteBuffer buf, int length) throws IOException {
        timeout = buf.get();
        priority = buf.get();
        rangeSubID = buf.get();
        buf.get(); // reserved
        subtree = new OID();
        AgentXProtocol.decodeOID(buf, subtree);
        if (rangeSubID != 0) {
            upperBound = buf.getInt();
            if ((rangeSubID < 0) || (rangeSubID > subtree.size())) {
                throw new IOException("Range sub-identifier "+rangeSubID+
                        " is out of range of "+subtree);
            }
        }
    }

    @Override
    public OctetString getContext() {
        return context;
    }

    /**
     * Gets the region registration priority.
     * @return
     *    the priority - smaller values of priority take precedence over larger values.
     */
    public byte getPriority() {
        return priority;
    }

    /**
     * Gets the range sub-identifier.
     * @return
     *    0 if this registration is not a range or the sub-identifier index within lower and upper bound.
     */
    public byte getRangeSubID() {
        return rangeSubID;
    }

    /**
     * Gets the sub-tree {@link OID}.
     * @return
     *    the sub-tree {@link OID}.
     */
    public OID getSubtree() {
        return subtree;
    }

    /**
     * Gets the region timeout.
     * @return
     *    the timeout in seconds.
     */
    public byte getTimeout() {
        return timeout;
    }

    /**
     * Gets the upper bound.
     * @return
     *    the upper bound of the region.
     */
    public int getUpperBound() {
        return upperBound;
    }

    /**
     * Sets the SNMPv3 context.
     * @param context
     *   the context.
     */
    public void setContext(OctetString context) {
        this.context = context;
    }

    /**
     * Sets the priority of the registration.
     * @param priority
     *    the region priority (lower value = precedence)
     */
    public void setPriority(byte priority) {
        this.priority = priority;
    }

    /**
     * Sets the range sub-identifier index (one-based).
     * @param rangeSubID
     *    the range sub-identifier starting from 1 for the first sub-identifier.
     */
    public void setRangeSubID(byte rangeSubID) {
        this.rangeSubID = rangeSubID;
    }

    /**
     * Sets the sub-tree {@link OID}.
     * @param subtree
     *    the sub-tree {@link OID}.
     */
    public void setSubtree(OID subtree) {
        this.subtree = subtree;
    }

    /**
     * Sets the region timeout.
     * @param timeout
     *    the timeout in seconds.
     */
    public void setTimeout(byte timeout) {
        this.timeout = timeout;
    }

    /**
     * Sets the upper bound.
     * @param upperBound
     *    the upper bound of the region.
     */
    public void setUpperBound(int upperBound) {
        this.upperBound = upperBound;
    }

    @Override
    public int getAfterContextLength() {
        return  AgentXProtocol.AGENTX_INT_SIZE +
                AgentXProtocol.getOIDLength(subtree) +
                ((rangeSubID != 0) ? AgentXProtocol.AGENTX_INT_SIZE : 0);
    }

    /**
     * Gets the registered region.
     * @return
     *    the effective registered region.
     */
    public AgentXRegion getRegion() {
        OID lower = new OID(subtree);
        OID upper = new OID(subtree);
        AgentXRegion region = new AgentXRegion(lower, upper);
        if (rangeSubID > 0) {
            if (upper.get(rangeSubID-1) == upperBound) {
                region.setSingleOID(true);
                region.setUpperIncluded(true);
            }
            else {
                upper.set(rangeSubID - 1, upperBound);
                region.setRangeSubID(rangeSubID);
            }
        }
        else if (isFlagSet(AgentXProtocol.FLAG_INSTANCE_REGISTRATION)) {
            region.setSingleOID(true);
            region.setUpperIncluded(true);
        }
        else {
            region.setUpperBound(upper.nextPeer());
        }
        return region;
    }

    @Override
    protected void encodeAfterContext(ByteBuffer buf) {
        buf.put(timeout);
        buf.put(priority);
        buf.put(rangeSubID);
        buf.put((byte)0); // reserved
        AgentXProtocol.encodeOID(buf, subtree, false);
        if (rangeSubID != 0) {
            buf.putInt(upperBound);
        }
    }

    @Override
    protected String toStringExtMembers() {
        return super.toStringExtMembers()+",timeout="+timeout+",priority="+priority+
                ",rangeSubID="+rangeSubID+",subtree="+subtree+",upperBound="+upperBound
                +",single="+isFlagSet(AgentXProtocol.FLAG_INSTANCE_REGISTRATION);
    }
}
