/*
 * Copyright (c) 1997, 2007, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code 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
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */


package com.sun.jmx.snmp.agent;



// java imports
//
import java.io.Serializable;
import java.util.Vector;
import java.util.Hashtable;
import java.util.Enumeration;

// jmx imports
//
import com.sun.jmx.snmp.SnmpOid;
import com.sun.jmx.snmp.SnmpValue;
import com.sun.jmx.snmp.SnmpVarBind;
import com.sun.jmx.snmp.SnmpDefinitions;
import com.sun.jmx.snmp.SnmpStatusException;

/**
 * The <CODE>SnmpMibNode</CODE> class represents a node in an SNMP MIB.
 * <P>
 * This class is used internally and by the class generated by
 * <CODE>mibgen</CODE>.
 * You should not need to use this class directly.
 *
 * <p><b>This API is a Sun Microsystems internal API  and is subject
 * to change without notice.</b></p>
 */

public abstract class SnmpMibNode implements Serializable {

    // ---------------------------------------------------------------------
    // PUBLIC METHODS
    //----------------------------------------------------------------------

    /**
     * Get the next OID arc corresponding to a readable scalar variable,
     * a branch leading to a subgroub, or a table.
     *
     * @param id Id we start from looking for the next.
     * @param userData A contextual object containing user-data.
     *        This object is allocated through the <code>
     *        {@link com.sun.jmx.snmp.agent.SnmpUserDataFactory}</code>
     *        for each incoming SNMP request.
     *
     * @return The next id in this group.
     *
     * @exception SnmpStatusException If no id is found after the given id.
     */
    public long getNextVarId(long id, Object userData)
        throws SnmpStatusException {
        return getNextIdentifier(varList,id);
    }

    /**
     * Get the next OID arc corresponding to a readable scalar variable,
     * a branch leading to a subgroub, or a table, possibly skipping over
     * those arcs that must not or cannot be returned.
     *
     * Calls {@link #getNextVarId(long,java.lang.Object)} until
     * {@link #skipVariable(long,java.lang.Object,int)} returns false.
     *
     * @param id Id we start from looking for the next.
     * @param userData A contextual object containing user-data.
     *        This object is allocated through the <code>
     *        {@link com.sun.jmx.snmp.agent.SnmpUserDataFactory}</code>
     *        for each incoming SNMP request.
     * @param pduVersion Protocol version of the original request PDU.
     *
     * @return The next id in this group which can be returned using
     *         the given PDU's protocol version.
     *
     * @exception SnmpStatusException If no id is found after the given id.
     */
    public long getNextVarId(long id, Object userData, int pduVersion)
        throws SnmpStatusException {
        long varid=id;
        do {
            varid = getNextVarId(varid,userData);
        } while (skipVariable(varid,userData,pduVersion));

        return varid;
    }

    /**
     * Hook for subclasses.
     * The default implementation of this method is to always return
     * false. Subclasses should redefine this method so that it returns
     * true when:
     * <ul><li>the variable is a leaf that is not instantiated,</li>
     * <li>or the variable is a leaf whose type cannot be returned by that
     *     version of the protocol (e.g. an Counter64 with SNMPv1).</li>
     * </ul>
     *
     * @param id Id we start from looking for the next.
     * @param userData A contextual object containing user-data.
     *        This object is allocated through the <code>
     *        {@link com.sun.jmx.snmp.agent.SnmpUserDataFactory}</code>
     *        for each incoming SNMP request.
     * @param pduVersion Protocol version of the original request PDU.
     *
     * @return true if the variable must be skipped by the get-next
     *         algorithm.
     */
    protected boolean skipVariable(long id, Object userData, int pduVersion) {
        return false;
    }

    /**
     * Find the node which handles a varbind, and register it in the
     * SnmpRequestTree. This method is a pure internal method. You should
     * never try to call it directly.
     *
     * @param varbind  The varbind to be handled
     *
     * @param oid      The OID array extracted from the varbind
     *
     * @param depth    The depth reached in the OID at this step of the
     *                 processing.
     *
     * @param handlers The Hashtable in which the varbind will be registered
     *                 with its handling node. This hashtable contains
     *                 <CODE>SnmpRequestTree.Handler</CODE> items.
     *
     * @exception SnmpStatusException No handling node was found.
     **/
    void findHandlingNode(SnmpVarBind varbind,
                          long[] oid, int depth,
                          SnmpRequestTree handlers)
        throws SnmpStatusException {
        throw new SnmpStatusException(SnmpStatusException.noSuchObject);
    }

    /**
     * Find the node which handles the leaf that immediately follows the
     * given varbind OID, and register the it in the SnmpRequestTree.
     * This method is a pure internal method. You should never try to call
     * it directly.
     *
     * @param varbind  The varbind to be handled
     *
     * @param oid      The OID array extracted from the varbind
     *
     * @param depth    The depth reached in the OID at this step of the
     *                 processing.
     *
     * @param handlers The Hashtable in which the varbind will be registered
     *                 with its handling node. This hashtable contains
     *                 SnmpRequestTree.Handler items.
     *
     * @return The SnmpOid of the next leaf.
     *
     * @exception SnmpStatusException No handling node was found.
     **/
    long[] findNextHandlingNode(SnmpVarBind varbind,
                                 long[] oid, int pos, int depth,
                                 SnmpRequestTree handlers, AcmChecker checker)
        throws SnmpStatusException {
        throw new SnmpStatusException(SnmpStatusException.noSuchObject);
    }

    /**
     * Generic handling of the <CODE>get</CODE> operation.
     *
     * <p> You can override this method if you need to implement some
     * specific policies for minimizing the accesses made to some remote
     * underlying resources.
     * <p>
     *
     * @param req   The sub-request that must be handled by this node.
     *
     * @param depth The depth reached in the OID tree.
     *
     * @exception SnmpStatusException An error occurred while accessing
     *  the MIB node.
     */
    public abstract void get(SnmpMibSubRequest req, int depth)
        throws SnmpStatusException;

    /**
     * Generic handling of the <CODE>set</CODE> operation.
     * <p> You can override this method if you need to implement some
     * specific policies for minimizing the accesses made to some remote
     * underlying resources.
     * <p>
     *
     * @param req   The sub-request that must be handled by this node.
     *
     * @param depth The depth reached in the OID tree.
     *
     * @exception SnmpStatusException An error occurred while accessing
     *  the MIB node.
     */
    public abstract void set(SnmpMibSubRequest req, int depth)
        throws SnmpStatusException;

    /**
     * Generic handling of the <CODE>check</CODE> operation.
     * <p> You can override this method if you need to implement some
     * specific policies for minimizing the accesses made to some remote
     * underlying resources, or if you need to implement some consistency
     * checks between the different values provided in the varbind list.
     * <p>
     *
     * @param req   The sub-request that must be handled by this node.
     *
     * @param depth The depth reached in the OID tree.
     *
     * @exception SnmpStatusException An error occurred while accessing
     *  the MIB node.
     */
    public abstract void check(SnmpMibSubRequest req, int depth)
        throws SnmpStatusException;

    /**
     * Sorts the specified integer array.
     *
     * @param array An integer array.
     */
    static public void sort(int array[]) {
        QuickSort(array, 0, array.length - 1);
    }

    /**
     * Computes the root OID of the MIB.
     */
    public void getRootOid(Vector<Integer> result) {
        return;
    }

    //----------------------------------------------------------------------
    // PACKAGE METHODS
    //----------------------------------------------------------------------

    /**
     * This is a generic version of C.A.R Hoare's Quick Sort
     * algorithm.  This will handle arrays that are already
     * sorted, and arrays with duplicate keys.
     *
     * If you think of a one dimensional array as going from
     * the lowest index on the left to the highest index on the right
     * then the parameters to this function are lowest index or
     * left and highest index or right.  The first time you call
     * this function it will be with the parameters 0, a.length - 1.
     *
     * @param a An integer array.
     * @param lo0 Left boundary of array partition.
     * @param hi0 Right boundary of array partition.
     */
    static void QuickSort(int a[], int lo0, int hi0) {
        int lo = lo0;
        int hi = hi0;
        int mid;

        if ( hi0 > lo0) {

            /* Arbitrarily establishing partition element as the midpoint of
             * the array.
             */
            mid = a[ ( lo0 + hi0 ) / 2 ];

            // loop through the array until indices cross
            while( lo <= hi ) {
                /* find the first element that is greater than or equal to
                 * the partition element starting from the left Index.
                 */
                while( ( lo < hi0 )  && ( a[lo] < mid ))
                    ++lo;

                /* find an element that is smaller than or equal to
                 * the partition element starting from the right Index.
                 */
                while( ( hi > lo0 ) && ( a[hi] > mid ))
                    --hi;

                // if the indexes have not crossed, swap
                if( lo <= hi ) {
                    swap(a, lo, hi);
                    ++lo;
                    --hi;
                }
            }

            /* If the right index has not reached the left side of array
             * must now sort the left partition.
             */
            if( lo0 < hi )
                QuickSort( a, lo0, hi );

            /* If the left index has not reached the right side of array
             * must now sort the right partition.
             */
            if( lo < hi0 )
                QuickSort( a, lo, hi0 );

        }
    }

    //----------------------------------------------------------------------
    // PROTECTED METHODS
    //----------------------------------------------------------------------

    /**
     * This will give the first element greater than <CODE>value</CODE>
     * in a sorted array.
     * If there is no element of the array greater than <CODE>value</CODE>,
     * the method will throw a <CODE>SnmpStatusException</CODE>.
     *
     * @param table A sorted integer array.
     *
     * @param value The greatest value.
     *
     * @exception SnmpStatusException If there is no element greater than
     *     <CODE>value</CODE>.
     */
    final static protected int getNextIdentifier(int table[], long value)
        throws SnmpStatusException {

        final int[] a = table;
        final int val= (int) value;

        if (a == null)
            throw new SnmpStatusException(SnmpStatusException.noSuchObject);

        int low= 0;
        int max= a.length;
        int curr= low + (max-low)/2;
        int elmt= 0;

        // Basic check
        //
        if (max < 1)
            throw new SnmpStatusException(SnmpStatusException.noSuchObject);

        if (a[max-1] <= val)
            throw new SnmpStatusException(SnmpStatusException.noSuchObject);

        while (low <= max) {
            elmt= a[curr];
            if (val == elmt) {
                // We ned to get the next index ...
                //
                curr++;
                return a[curr];
            }
            if (elmt < val) {
                low= curr +1;
            } else {
                max= curr -1;
            }
            curr= low + (max-low)/2;
        }
        return a[curr];
    }


    //----------------------------------------------------------------------
    // PRIVATE METHODS
    //----------------------------------------------------------------------

    final static private void swap(int a[], int i, int j) {
        int T;
        T = a[i];
        a[i] = a[j];
        a[j] = T;
    }

    //----------------------------------------------------------------------
    // PROTECTED VARIABLES
    //----------------------------------------------------------------------

    /**
     * Contains the list of variable identifiers.
     */
    protected int[] varList;
}
