/* packet-dis-pdus.c
 * Routines and definitions for DIS PDU parsing.
 * Copyright 2005, Scientific Research Corporation
 * Initial implementation by Jeremy Ouellette <jouellet@scires.com>
 *
 * $Id: packet-dis-pdus.c 26646 2008-10-31 14:07:23Z stig $
 *
 * Wireshark - Network traffic analyzer
 * By Gerald Combs <gerald@wireshark.org>
 * Copyright 1998 Gerald Combs
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <string.h>
#include "packet-dis-pdus.h"
#include "packet-dis-fields.h"

guint32 numArticulations;
gint ettArticulations[DIS_PDU_MAX_ARTICULATIONS];

DIS_ParserNode DIS_PARSER_ENTITY_STATE_PDU[] =
{
    { DIS_FIELDTYPE_ENTITY_ID,               "Entity ID",0,0,0 },
    { DIS_FIELDTYPE_FORCE_ID,                "Force ID",0,0,0 },
    { DIS_FIELDTYPE_UINT8,                   "Number of Articulation Parameters",0,0,&numArticulations },
    { DIS_FIELDTYPE_ENTITY_TYPE,             "Entity Type",0,0,0 },
    { DIS_FIELDTYPE_ENTITY_TYPE,             "Alternative Entity Type",0,0,0 },
    { DIS_FIELDTYPE_LINEAR_VELOCITY,         "Entity Linear Velocity",0,0,0 },
    { DIS_FIELDTYPE_LOCATION_WORLD,          "Entity Location",0,0,0 },
    { DIS_FIELDTYPE_ORIENTATION,             "Entity Orientation",0,0,0 },
    { DIS_FIELDTYPE_APPEARANCE,              "Entity Appearance",0,0,0 },
    { DIS_FIELDTYPE_DEAD_RECKONING_PARAMS,   "Dead Reckoning Parameters",0,0,0 },
    { DIS_FIELDTYPE_ENTITY_MARKING,          "Entity Marking",0,0,0 },
    { DIS_FIELDTYPE_CAPABILITIES,            "Capabilities",0,0,0 },
    { DIS_FIELDTYPE_ARTICULATION_PARAMETERS, "Articulation Parameters",0,0,0 },
    { DIS_FIELDTYPE_END,                     NULL,0,0,0 }
};

DIS_ParserNode DIS_PARSER_FIRE_PDU[] =
{
    { DIS_FIELDTYPE_ENTITY_ID,        "Firing Entity ID",0,0,0 },
    { DIS_FIELDTYPE_ENTITY_ID,        "Target Entity ID",0,0,0 },
    { DIS_FIELDTYPE_ENTITY_ID,        "Munition ID",0,0,0 },
    { DIS_FIELDTYPE_EVENT_ID,         "Event ID",0,0,0 },
    { DIS_FIELDTYPE_UINT32,           "Fire Mission Index",0,0,0 },
    { DIS_FIELDTYPE_LOCATION_WORLD,   "Location in World Coordinates",0,0,0 },
    { DIS_FIELDTYPE_BURST_DESCRIPTOR, "Burst Descriptor",0,0,0 },
    { DIS_FIELDTYPE_LINEAR_VELOCITY,  "Velocity",0,0,0 },
    { DIS_FIELDTYPE_FLOAT32,          "Range",0,0,0 },
    { DIS_FIELDTYPE_END,              NULL,0,0,0 }
};

DIS_ParserNode DIS_PARSER_DETONATION_PDU[] =
{
    { DIS_FIELDTYPE_ENTITY_ID,               "Firing Entity ID",0,0,0 },
    { DIS_FIELDTYPE_ENTITY_ID,               "Target Entity ID",0,0,0 },
    { DIS_FIELDTYPE_ENTITY_ID,               "Munition ID",0,0,0 },
    { DIS_FIELDTYPE_EVENT_ID,                "Event ID",0,0,0 },
    { DIS_FIELDTYPE_LINEAR_VELOCITY,         "Velocity",0,0,0 },
    { DIS_FIELDTYPE_LOCATION_WORLD,          "Location in World Coordinates",0,0,0 },
    { DIS_FIELDTYPE_BURST_DESCRIPTOR,        "Burst Descriptor",0,0,0 },
    { DIS_FIELDTYPE_LOCATION_ENTITY,         "Location in Entity Coordinates",0,0,0 },
    { DIS_FIELDTYPE_DETONATION_RESULT,       "Detonation Result",0,0,0 },
    { DIS_FIELDTYPE_UINT8,                   "Number of Articulation Parameters",0,0,0 },
    { DIS_FIELDTYPE_PAD16,                   "Padding",0,0,0 },
    { DIS_FIELDTYPE_ARTICULATION_PARAMETERS, "Articulation Parameters",0,0,0 },
    { DIS_FIELDTYPE_END,                     NULL,0,0,0 }
};

/* Initialize the parsers for each PDU type and the standard DIS header.
 */
void initializeParsers(void)
{
    initializeParser(DIS_PARSER_ENTITY_STATE_PDU);
    initializeParser(DIS_PARSER_FIRE_PDU);
    initializeParser(DIS_PARSER_DETONATION_PDU);
    initializeParser(DIS_FIELDS_PDU_HEADER);
}

/* Create a specific subtree for a PDU or a composite PDU field.
 */
DIS_ParserNode *createSubtree(DIS_ParserNode parserNodes[], gint *ettVar)
{
    guint fieldIndex = 0;
    guint fieldCount;
    gint *ett[1];
    DIS_ParserNode *newSubtree;

    while (parserNodes[fieldIndex].fieldType != DIS_FIELDTYPE_END)
    {
        ++fieldIndex;
    }

    fieldCount = fieldIndex + 1;

    newSubtree = (DIS_ParserNode*)g_malloc(sizeof(DIS_ParserNode) * fieldCount);

    memcpy(newSubtree, parserNodes, sizeof(DIS_ParserNode) * fieldCount);

    initializeParser(newSubtree);

    ett[0] = ettVar;
    proto_register_subtree_array(ett, array_length(ett));

    return newSubtree;
}

/* Initialize an array of parser nodes.
 */
void initializeParser(DIS_ParserNode parserNodes[])
{
    guint parserIndex = 0;

    /* Create the parser subtrees for each of the composite field types.
     */
    while (parserNodes[parserIndex].fieldType != DIS_FIELDTYPE_END)
    {
        switch (parserNodes[parserIndex].fieldType)
        {
        case DIS_FIELDTYPE_APPEARANCE:
            parserNodes[parserIndex].children = createSubtree(
                DIS_FIELDS_NONE,
                &parserNodes[parserIndex].ettVar);
            break;
        case DIS_FIELDTYPE_ARTICULATION_PARAMETERS:
            parserNodes[parserIndex].children = createSubtree(
                DIS_FIELDS_ARTICULATION_PARAMETER,
                &parserNodes[parserIndex].ettVar);
            break;
        case DIS_FIELDTYPE_BURST_DESCRIPTOR:
            parserNodes[parserIndex].children = createSubtree(
                DIS_FIELDS_BURST_DESCRIPTOR,
                &parserNodes[parserIndex].ettVar);
            break;
        case DIS_FIELDTYPE_ENTITY_ID:
            parserNodes[parserIndex].children = createSubtree(
                DIS_FIELDS_ENTITY_ID,
                &parserNodes[parserIndex].ettVar);
            break;
        case DIS_FIELDTYPE_ENTITY_TYPE:
            parserNodes[parserIndex].children = createSubtree(
                DIS_FIELDS_ENTITY_TYPE,
                &parserNodes[parserIndex].ettVar);
            break;
        case DIS_FIELDTYPE_EVENT_ID:
            parserNodes[parserIndex].children = createSubtree(
                DIS_FIELDS_EVENT_ID,
                &parserNodes[parserIndex].ettVar);
            break;
        case DIS_FIELDTYPE_LINEAR_VELOCITY:
            parserNodes[parserIndex].children = createSubtree(
                DIS_FIELDS_LINEAR_VELOCITY,
                &parserNodes[parserIndex].ettVar);
            break;
        case DIS_FIELDTYPE_LOCATION_WORLD:
            parserNodes[parserIndex].children = createSubtree(
                DIS_FIELDS_LOCATION_WORLD,
                &parserNodes[parserIndex].ettVar);
            break;
        case DIS_FIELDTYPE_LOCATION_ENTITY:
            parserNodes[parserIndex].children = createSubtree(
                DIS_FIELDS_LOCATION_ENTITY,
                &parserNodes[parserIndex].ettVar);
            break;
        case DIS_FIELDTYPE_ORIENTATION:
            parserNodes[parserIndex].children = createSubtree(
                DIS_FIELDS_ORIENTATION,
                &parserNodes[parserIndex].ettVar);
            break;
        default:
            break;
        }
        ++parserIndex;
    }
}

/* Parse packet data based on a specified array of DIS_ParserNodes.
 */
gint parseFields(tvbuff_t *tvb, proto_tree *tree, gint offset, DIS_ParserNode parserNodes[])
{
    guint fieldIndex = 0;

    while (parserNodes[fieldIndex].fieldType != DIS_FIELDTYPE_END)
    {
        proto_item *newField = 0;
        switch(parserNodes[fieldIndex].fieldType)
        {
        case DIS_FIELDTYPE_INT8:
            offset = parseField_Int(tvb, tree, offset,
                parserNodes[fieldIndex], 1);
            break;
        case DIS_FIELDTYPE_INT16:
            offset = parseField_Int(tvb, tree, offset,
                parserNodes[fieldIndex], 2);
            break;
        case DIS_FIELDTYPE_INT32:
            offset = parseField_Int(tvb, tree, offset,
                parserNodes[fieldIndex], 4);
            break;
        case DIS_FIELDTYPE_INT64:
            offset = parseField_Int(tvb, tree, offset,
                parserNodes[fieldIndex], 8);
            break;
        case DIS_FIELDTYPE_UINT8:
            offset = parseField_UInt(tvb, tree, offset,
                parserNodes[fieldIndex], 1);
            break;
        case DIS_FIELDTYPE_UINT16:
            offset = parseField_UInt(tvb, tree, offset,
                parserNodes[fieldIndex], 2);
            break;
        case DIS_FIELDTYPE_UINT32:
            offset = parseField_UInt(tvb, tree, offset,
                parserNodes[fieldIndex], 4);
            break;
        case DIS_FIELDTYPE_UINT64:
            offset = parseField_UInt(tvb, tree, offset,
                parserNodes[fieldIndex], 8);
            break;
        case DIS_FIELDTYPE_FLOAT32:
            offset = parseField_Float(tvb, tree, offset,
                parserNodes[fieldIndex]);
            break;
        case DIS_FIELDTYPE_FLOAT64:
            offset = parseField_Double(tvb, tree, offset,
                parserNodes[fieldIndex]);
            break;
        case DIS_FIELDTYPE_PAD8:
            offset = parseField_Pad(tvb, tree, offset,
                parserNodes[fieldIndex], 1);
            break;
        case DIS_FIELDTYPE_PAD16:
            offset = parseField_Pad(tvb, tree, offset,
                parserNodes[fieldIndex], 2);
            break;
        case DIS_FIELDTYPE_PAD32:
            offset = parseField_Pad(tvb, tree, offset,
                parserNodes[fieldIndex], 4);
            break;
        case DIS_FIELDTYPE_APPEARANCE:
	    {
                proto_item *newSubtree;
                newField = proto_tree_add_text(tree, tvb, offset, 4, "%s",
                    parserNodes[fieldIndex].fieldLabel);
                newSubtree = proto_item_add_subtree(newField,
                    parserNodes[fieldIndex].ettVar);
                offset = parseField_Bitmask(tvb, newSubtree, offset,
                    parserNodes[fieldIndex], 4);
	    }
            break;
        case DIS_FIELDTYPE_ARTIC_PARAM_TYPE_DESIGNATOR:
            offset = parseField_UInt(tvb, tree, offset,
                parserNodes[fieldIndex], 1);
            break;
        case DIS_FIELDTYPE_ARTIC_PARAM_TYPE:
            offset = parseField_UInt(tvb, tree, offset,
                parserNodes[fieldIndex], 4);
            break;
        case DIS_FIELDTYPE_CAPABILITIES:
            offset = parseField_UInt(tvb, tree, offset,
                parserNodes[fieldIndex], 4);
            break;
        case DIS_FIELDTYPE_CATEGORY:
            offset = parseField_Enum(tvb, tree, offset,
                parserNodes[fieldIndex], 1);
            break;
        case DIS_FIELDTYPE_COUNTRY:
            offset = parseField_UInt(tvb, tree, offset,
                parserNodes[fieldIndex], 2);
            break;
        case DIS_FIELDTYPE_DEAD_RECKONING_PARAMS:
	    /* This is really a struct... needs a field parser.
             * For now, just skip the 12 bytes.
             */
            offset = parseField_Bytes(tvb, tree, offset,
                parserNodes[fieldIndex], 40);
            break;
        case DIS_FIELDTYPE_DETONATION_RESULT:
            offset = parseField_Enum(tvb, tree, offset,
                parserNodes[fieldIndex], 1);
            break;
        case DIS_FIELDTYPE_DOMAIN:
            offset = parseField_Enum(tvb, tree, offset,
                parserNodes[fieldIndex], 1);
            break;
        case DIS_FIELDTYPE_ENTITY_KIND:
            offset = parseField_Enum(tvb, tree, offset,
                parserNodes[fieldIndex], 1);
            break;
        case DIS_FIELDTYPE_ENTITY_MARKING:
	    /* This is really a struct... needs a field parser.
             * For now, just skip the 12 bytes.
             */
            offset = parseField_Bytes(tvb, tree, offset,
                parserNodes[fieldIndex], 12);
            break;
        case DIS_FIELDTYPE_EXTRA:
            offset = parseField_UInt(tvb, tree, offset,
                parserNodes[fieldIndex], 1);
            break;
        case DIS_FIELDTYPE_FORCE_ID:
            offset = parseField_UInt(tvb, tree, offset,
                parserNodes[fieldIndex], 1);
            break;
        case DIS_FIELDTYPE_FUSE:
            offset = parseField_UInt(tvb, tree, offset,
                parserNodes[fieldIndex], 2);
            break;
        case DIS_FIELDTYPE_PDU_TYPE:
            offset = parseField_Enum(tvb, tree, offset,
                parserNodes[fieldIndex], 1);
            break;
        case DIS_FIELDTYPE_PROTOCOL_FAMILY:
            offset = parseField_Enum(tvb, tree, offset,
                parserNodes[fieldIndex], 1);
            break;
        case DIS_FIELDTYPE_PROTOCOL_VERSION:
            offset = parseField_Enum(tvb, tree, offset,
                parserNodes[fieldIndex], 1);
            break;
        case DIS_FIELDTYPE_SPECIFIC:
            offset = parseField_UInt(tvb, tree, offset,
                parserNodes[fieldIndex], 1);
            break;
        case DIS_FIELDTYPE_SUBCATEGORY:
            offset = parseField_UInt(tvb, tree, offset,
                parserNodes[fieldIndex], 1);
            break;
        case DIS_FIELDTYPE_TIMESTAMP:
            offset = parseField_Timestamp(tvb, tree, offset,
                parserNodes[fieldIndex]);
            break;
        case DIS_FIELDTYPE_WARHEAD:
            offset = parseField_UInt(tvb, tree, offset,
                parserNodes[fieldIndex], 2);
            break;
        case DIS_FIELDTYPE_BURST_DESCRIPTOR:
        case DIS_FIELDTYPE_ENTITY_ID:
        case DIS_FIELDTYPE_EVENT_ID:
        case DIS_FIELDTYPE_LINEAR_VELOCITY:
        case DIS_FIELDTYPE_LOCATION_WORLD:
        case DIS_FIELDTYPE_LOCATION_ENTITY:
        case DIS_FIELDTYPE_ENTITY_TYPE:
        case DIS_FIELDTYPE_ORIENTATION:
            newField = proto_tree_add_text(tree, tvb, offset, -1, "%s",
                parserNodes[fieldIndex].fieldLabel);
            if (parserNodes[fieldIndex].children != 0)
            {
                proto_item *newSubtree = proto_item_add_subtree(newField,
                    parserNodes[fieldIndex].ettVar);
                offset = parseFields(tvb, newSubtree, offset,
                    parserNodes[fieldIndex].children);
            }
            proto_item_set_end(newField, tvb, offset);
	    break;
        case DIS_FIELDTYPE_ARTICULATION_PARAMETERS:
	    {
                guint i;

                if (numArticulations > DIS_PDU_MAX_ARTICULATIONS)
                {
                    numArticulations = DIS_PDU_MAX_ARTICULATIONS;
                }

	        for (i = 0; i < numArticulations; ++i)
	        {
		    proto_item *newSubtree;
                    newField = proto_tree_add_text(tree, tvb, offset, -1, "%s",
                        parserNodes[fieldIndex].fieldLabel);
                    newSubtree = proto_item_add_subtree(newField,
                        ettArticulations[i]);
                    offset = parseFields(tvb, newSubtree, offset,
                        parserNodes[fieldIndex].children);
                    proto_item_set_end(newField, tvb, offset);
	        }
            }
	    break;
	default:
	    break;
	}
        
        ++fieldIndex;
    }

    return offset;
}
