/*
 * Copyright (C) 2005 Alfresco, Inc.
 *
 * Licensed under the Mozilla Public License version 1.1 
 * with a permitted attribution clause. You may obtain a
 * copy of the License at
 *
 *   http://www.alfresco.org/legal/license.txt
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific
 * language governing permissions and limitations under the
 * License.
 */
package org.alfresco.filesys.smb.server;

import org.alfresco.filesys.netbios.RFCNetBIOSProtocol;
import org.alfresco.filesys.smb.PacketType;
import org.alfresco.filesys.util.DataPacker;

/**
 * SMB transact packet class
 */

public class SMBTransPacket extends SMBSrvPacket
{

    // Define the number of standard parameters

    protected static final int STD_PARAMS = 14;

    // Transaction status that indicates that this transaction has more data
    // to be returned.

    public static final int IsContinued = 234;

    // Transact name, not used for transact 2

    protected String m_transName;

    // Parameter count for this transaction

    protected int m_paramCnt;

    // Multiplex identifier, to identify each transaction request

    private static int m_nextMID = 1;

    /**
     * Construct an SMB transaction packet
     * 
     * @param buf Buffer that contains the SMB transaction packet.
     */
    public SMBTransPacket(byte[] buf)
    {
        super(buf);
    }

    /**
     * Construct an SMB transaction packet
     * 
     * @param siz Size of packet to allocate.
     */
    public SMBTransPacket(int siz)
    {
        super(siz);

        // Set the multiplex id for this transaction

        setMultiplexId(getNextMultiplexId());
    }

    /**
     * Get the next multiplex id to uniquely identify this transaction
     * 
     * @return Unique multiplex id for this transaction
     */
    public final static int getNextMultiplexId()
    {
        return m_nextMID++;
    }

    /**
     * Return the total parameter byte count
     * 
     * @return int
     */
    public final int getTotalParameterCount()
    {
        return getParameter(0);
    }

    /**
     * Return the total data byte count
     * 
     * @return int
     */
    public final int getTotalDataCount()
    {
        return getParameter(1);
    }

    /**
     * Return the parameter count size in bytes for this section
     * 
     * @return int
     */
    public final int getParameterBlockCount()
    {
        return getParameter(9);
    }

    /**
     * Return the parameter block offset
     * 
     * @return Paramter block offset within the SMB packet
     */
    public final int getParameterBlockOffset()
    {
        return getParameter(10) + RFCNetBIOSProtocol.HEADER_LEN;
    }

    /**
     * Return the data block size in bytes for this section
     * 
     * @return int
     */
    public final int getDataBlockCount()
    {
        return getParameter(11);
    }

    /**
     * Return the data block offset
     * 
     * @return Data block offset within the SMB packet.
     */
    public final int getDataBlockOffset()
    {
        return getParameter(12) + RFCNetBIOSProtocol.HEADER_LEN;
    }

    /**
     * Return the secondary parameter block size in bytes
     * 
     * @return int
     */
    public final int getSecondaryParameterBlockCount()
    {
        return getParameter(2);
    }

    /**
     * Return the secondary parameter block offset
     * 
     * @return int
     */
    public final int getSecondaryParameterBlockOffset()
    {
        return getParameter(3) + RFCNetBIOSProtocol.HEADER_LEN;
    }

    /**
     * Return the secondary parameter block displacement
     * 
     * @return int
     */
    public final int getParameterBlockDisplacement()
    {
        return getParameter(4);
    }

    /**
     * Return the secondary data block size in bytes
     * 
     * @return int
     */
    public final int getSecondaryDataBlockCount()
    {
        return getParameter(5);
    }

    /**
     * Return the secondary data block offset
     * 
     * @return int
     */
    public final int getSecondaryDataBlockOffset()
    {
        return getParameter(6) + RFCNetBIOSProtocol.HEADER_LEN;
    }

    /**
     * Return the secondary data block displacement
     * 
     * @return int
     */
    public final int getDataBlockDisplacement()
    {
        return getParameter(7);
    }

    /**
     * Return the transaction sub-command
     * 
     * @return int
     */
    public final int getSubFunction()
    {
        return getParameter(14);
    }

    /**
     * Unpack the parameter block into the supplied array.
     * 
     * @param prmblk Array to unpack the parameter block words into.
     */
    public final void getParameterBlock(short[] prmblk) throws java.lang.ArrayIndexOutOfBoundsException
    {

        // Determine how many parameters are to be unpacked, check if the user
        // buffer is long enough

        int prmcnt = getParameter(3) / 2; // convert to number of words
        if (prmblk.length < prmcnt)
            throw new java.lang.ArrayIndexOutOfBoundsException();

        // Get the offset to the parameter words, add the NetBIOS header length
        // to the offset.

        int pos = getParameter(4) + RFCNetBIOSProtocol.HEADER_LEN;

        // Unpack the parameter words

        byte[] buf = getBuffer();

        for (int idx = 0; idx < prmcnt; idx++)
        {

            // Unpack the current parameter word

            prmblk[idx] = (short) DataPacker.getIntelShort(buf, pos);
            pos += 2;
        }
    }

    /**
     * Initialize the transact SMB packet
     * 
     * @param pcnt Total parameter count for this transaction
     * @param paramblk Parameter block data bytes
     * @param plen Parameter block data length
     * @param datablk Data block data bytes
     * @param dlen Data block data length
     */
    public final void InitializeTransact(int pcnt, byte[] paramblk, int plen, byte[] datablk, int dlen)
    {

        // Set the SMB command code

        if (m_transName == null)
            setCommand(PacketType.Transaction2);
        else
            setCommand(PacketType.Transaction);

        // Set the parameter count

        setParameterCount(pcnt);

        // Save the parameter count, add an extra parameter for the data byte count

        m_paramCnt = pcnt;

        // Initialize the parameters

        setParameter(0, plen); // total parameter bytes being sent
        setParameter(1, dlen); // total data bytes being sent

        for (int i = 2; i < 9; setParameter(i++, 0))
            ;

        setParameter(9, plen); // parameter bytes sent in this packet
        setParameter(11, dlen); // data bytes sent in this packet

        setParameter(13, pcnt - STD_PARAMS); // number of setup words

        // Get the data byte offset

        int pos = getByteOffset();
        int startPos = pos;

        // Check if this is a named transaction, if so then store the name

        int idx;
        byte[] buf = getBuffer();

        if (m_transName != null)
        {

            // Store the transaction name

            byte[] nam = m_transName.getBytes();

            for (idx = 0; idx < nam.length; idx++)
                buf[pos++] = nam[idx];
        }

        // Word align the buffer offset

        if ((pos % 2) > 0)
            pos++;

        // Store the parameter block

        if (paramblk != null)
        {

            // Set the parameter block offset

            setParameter(10, pos - RFCNetBIOSProtocol.HEADER_LEN);

            // Store the parameter block

            for (idx = 0; idx < plen; idx++)
                buf[pos++] = paramblk[idx];
        }
        else
        {

            // Clear the parameter block offset

            setParameter(10, 0);
        }

        // Word align the data block

        if ((pos % 2) > 0)
            pos++;

        // Store the data block

        if (datablk != null)
        {

            // Set the data block offset

            setParameter(12, pos - RFCNetBIOSProtocol.HEADER_LEN);

            // Store the data block

            for (idx = 0; idx < dlen; idx++)
                buf[pos++] = datablk[idx];
        }
        else
        {

            // Zero the data block offset

            setParameter(12, 0);
        }

        // Set the byte count for the SMB packet

        setByteCount(pos - startPos);
    }

    /**
     * Set the specifiec setup parameter within the SMB packet.
     * 
     * @param idx Setup parameter index.
     * @param val Setup parameter value.
     */

    public final void setSetupParameter(int idx, int val)
    {
        setParameter(STD_PARAMS + idx, val);
    }

    /**
     * Set the transaction name for normal transactions
     * 
     * @param tname Transaction name string
     */

    public final void setTransactionName(String tname)
    {
        m_transName = tname;
    }
}