/*_############################################################################
  _## 
  _##  SNMP4J-AgentX - AgentXPendingSet.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.master;

import org.snmp4j.agent.agentx.AgentXPDU;

import java.util.LinkedList;

import org.snmp4j.agent.request.*;
import org.snmp4j.smi.Address;
import org.snmp4j.smi.VariableBinding;

import java.util.List;

import org.snmp4j.agent.agentx.AgentXTestSetPDU;
import org.snmp4j.agent.agentx.AgentXCommitSetPDU;
import org.snmp4j.agent.agentx.AgentXCleanupSetPDU;
import org.snmp4j.agent.agentx.AgentXUndoSetPDU;

/**
 * {@link AgentXPendingSet} implements an AgentX SET request.
 * @param <A>
 *    the address type.
 */
public class AgentXPendingSet<A extends Address> extends AbstractAgentXPending<A> {

    private static final long serialVersionUID = 6075492117112399140L;

    /**
     * The AgentX master session over which the SET request is processed.
     */
    private final AgentXMasterSession<A> session;
    /**
     * The variable bindings of the SET request to be sent to the sub-agent.
     */
    private final List<AgentXSetVB> vbs = new LinkedList<>();
    /**
     * The AgentX request PDU (TestSet, CommitSet, UndoSet, or CleanupSet) currently being processed.
     */
    private AgentXPDU requestPDU;

    /**
     * Creates a new {@link AgentXPendingSet} request.
     * @param regEntry
     *    a registration entry to process the request.
     * @param request
     *    the SNMP request.
     */
    public AgentXPendingSet(AgentXRegEntry<A> regEntry, SnmpRequest request) {
        super(regEntry, request);
        this.session = regEntry.getSession();
    }

    /**
     * Adds a variable binding to the SET request with a reference to the SNMP request it belongs to.
     * @param reference
     *    the corresponding SNMP sub-request.
     * @param vb
     *    the variable binding.
     */
    public synchronized void add(SnmpRequest.SnmpSubRequest reference, VariableBinding vb) {
        vbs.add(new AgentXSetVB(vb, reference));
        // invalidate pdu
        requestPDU = null;
    }

    @Override
    public AgentXPDU getAgentXPDU() {
        if (requestPDU == null) {
            createRequestPDU();
        }
        return requestPDU;
    }

    private void createRequestPDU() {
        AgentXPDU reqPDU = null;
        SnmpRequest request = vbs.get(0).getReferenceSubRequest().getRequest();
        switch (request.getPhase()) {
            case Request.PHASE_INIT:
            case Request.PHASE_1PC:
            case Request.PHASE_2PC_PREPARE: {
                reqPDU =
                        new AgentXTestSetPDU(request.getContext(), vbs.toArray(new VariableBinding[0]));
                break;
            }
            case Request.PHASE_2PC_COMMIT: {
                reqPDU = new AgentXCommitSetPDU();
                break;
            }
            case Request.PHASE_2PC_CLEANUP: {
                reqPDU = new AgentXCleanupSetPDU();
                break;
            }
            default: {
                reqPDU = new AgentXUndoSetPDU();
                break;
            }
        }
        reqPDU.setSessionID(session.getSessionID());
        reqPDU.setTransactionID(request.getTransactionID());
        this.requestPDU = reqPDU;
    }

    public AgentXMasterSession<A> getSession() {
        return session;
    }

    public SubRequestIterator<SnmpRequest.SnmpSubRequest> getReferences() {
        return new SetSubRequestIterator();
    }

    public String toString() {
        return getClass().getName() + "[" + super.toStringMembers() + ",vbs=" + vbs + "]";
    }

    static class AgentXSetVB extends VariableBinding {
        private static final long serialVersionUID = 9058499911536689081L;

        private final transient SnmpRequest.SnmpSubRequest referenceSubRequest;

        public AgentXSetVB(VariableBinding vb, SnmpRequest.SnmpSubRequest reference) {
            super(vb.getOid(), vb.getVariable());
            this.referenceSubRequest = reference;
        }

        public SnmpRequest.SnmpSubRequest getReferenceSubRequest() {
            return referenceSubRequest;
        }
    }

    class SetSubRequestIterator extends SubRequestIteratorSupport<SnmpRequest.SnmpSubRequest> {
        protected SetSubRequestIterator() {
            super(AgentXPendingSet.this.vbs.iterator());
        }

        @SuppressWarnings("unchecked")
        protected SnmpRequest.SnmpSubRequest mapToSubRequest(Object element) {
            return ((AgentXSetVB) element).getReferenceSubRequest();
        }
    }


}
