/*_############################################################################
  _## 
  _##  SNMP4J-AgentX - AgentXContextPDU.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.IOException;
import java.io.Serial;
import java.nio.ByteBuffer;

import org.snmp4j.smi.OctetString;

/**
 * A {@link AgentXContextPDU} is a {@link AgentXPDU} with context support.
 */
public abstract class AgentXContextPDU extends AgentXPDU {

    @Serial
    private static final long serialVersionUID = 4649210580838933182L;

    /**
     * The SNMPv43 context.
     */
    protected OctetString context;

    /**
     *
     * Create a {@link AgentXPDU} from its type.
     * @param type
     *    a AgentX PDU type from {@link #AGENTX_OPEN_PDU} to {@link AGENTX_RESPONSE_PDU}.
     * @param context
     *    the context.
     */
    protected AgentXContextPDU(byte type, OctetString context) {
        super(type);
        this.context = context;
    }

    /**
     * Creates a {@link AgentXContextPDU} from a {@link AgentXMessageHeader}.
     * @param header
     *    the message header.
     */
    protected AgentXContextPDU(AgentXMessageHeader header) {
        super(header);
    }

    /**
     * Creates a {@link AgentXContextPDU}.
     * @param type
     *    a AgentX PDU type from {@link #AGENTX_OPEN_PDU} to {@link AgentXPDU#AGENTX_RESPONSE_PDU}.
     * @param flags
     *    a set of flags (bit-or), see also {@link AgentXProtocol#FLAG_NETWORK_BYTE_ORDER}.
     * @param sessionID
     *    a session ID.
     * @param transactionID
     *    a transaction ID.
     * @param packetID
     *    a packet ID.
     */
    protected AgentXContextPDU(byte type, byte flags, int sessionID, int transactionID, int packetID) {
        super(type, flags, sessionID, transactionID, packetID);
    }

    /**
     * Decode this PDU after the end of the encoded context.
     * @param buf
     *    the buffer positioned at the end of the encoded context.
     * @param length
     *    the length of the PDU to be decoded.
     * @throws IOException
     *    if the buffer contains fewer data than expected.
     */
    protected abstract void decodeAfterContext(ByteBuffer buf, int length)
            throws IOException;

    /**
     * Encode this PDU after already encoded context (thus context will not be encoded by this operation).
     * @param buf
     *    the target buffer.
     */
    protected abstract void encodeAfterContext(ByteBuffer buf);

    /**
     * Gets the encoded length after an already encoded context.
     * @return
     *    the BER encoded length (without context).
     */
    protected abstract int getAfterContextLength();

    /**
     * Decode the PDU payload from the {@link ByteBuffer}.
     * @param buf
     *    the byte buffer from which to decode the payload.
     * @param length
     *    the length of the payload to decode.
     * @throws IOException
     *    if the byte bufferdoes not contain the expected data.
     */
    public final void decodePayload(ByteBuffer buf, int length)
            throws IOException
    {
        if (isFlagSet(AgentXProtocol.FLAG_NON_DEFAULT_CONTEXT)) {
            context = AgentXProtocol.decodeOctetString(buf);
        }
        else {
            context = new OctetString();
        }
        decodeAfterContext(buf, length);
    }

    /**
     * Encode this PDU payload to the given {@link ByteBuffer}.
     * @param buf
     *    the byte buffer into which to encode the payload.
     */
    public final void encodePayload(ByteBuffer buf) {
        if (isFlagSet(AgentXProtocol.FLAG_NON_DEFAULT_CONTEXT)) {
            AgentXProtocol.encodeOctetString(buf, context);
        }
        encodeAfterContext(buf);
    }

    /**
     * Gets the payload length.
     * @return
     *    the number of bytes needed to encode the payload.
     */
    public final int getPayloadLength() {
        int length = 0;
        if ((context != null) && (context.length() > 0) &&
                (AgentXProtocol.isNonDefaultContextsEnabled())) {
            length = AgentXProtocol.getOctetStringLength(context.length());
        }
        length += getAfterContextLength();
        return length;
    }

    /**
     * Gets the context.
     * @return
     *    an {@link OctetString} representing the context.
     */
    public OctetString getContext() {
        return context;
    }

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

    protected String toStringExtMembers() {
        return super.toStringExtMembers()+",context="+context;
    }

    /**
     * Initialize flags and other things before a PDU is encoded.
     */
    protected void beforeEncode() {
        if ((context != null) && (context.length() > 0) &&
                (AgentXProtocol.isNonDefaultContextsEnabled())) {
            addFlag(AgentXProtocol.FLAG_NON_DEFAULT_CONTEXT);
        }
    }

}
