/* packet-rmt-fec.c
 * Reliable Multicast Transport (RMT)
 * FEC Building Block dissector
 * Copyright 2005, Stefano Pettini <spettini@users.sourceforge.net>
 *
 * Forward Error Correction (ALC):
 * -------------------------------
 *
 * The goal of the FEC building block is to describe functionality
 * directly related to FEC codes that is common to all reliable content
 * delivery IP multicast protocols, and to leave out any additional
 * functionality that is specific to particular protocols.
 *
 * References:
 *     RFC 3452, Forward Error Correction Building Block
 *     RFC 3695, Compact Forward Error Correction (FEC) Schemes
 *     Simple XOR, Reed-Solomon, and Parity Check Matrix-based FEC Schemes draft-peltotalo-rmt-bb-fec-supp-xor-pcm-rs-00
 *     IANA RMT FEC parameters (http://www.iana.org/assignments/rmt-fec-parameters)
 *
 * $Id: packet-rmt-fec.c 13540 2005-02-27 20:26:55Z lego $
 *
 * Ethereal - Network traffic analyzer
 * By Gerald Combs <gerald@ethereal.com>
 * 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 <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <glib.h>

#include <epan/packet.h>
#include <epan/prefs.h>

#include "packet-rmt-fec.h"

/* String tables */
const value_string string_fec_encoding_id[] =
{
	{ 0, "Compact No-Code" },
	{ 2, "Simple XOR, Reed-Solomon, and Parity Check Matrix-based FEC Codes" },
	{ 128, "Small Block, Large Block and Expandable FEC Codes" },
	{ 129, "Small Block Systematic FEC Codes" },
	{ 130, "Compact FEC Codes" },
	{ 132, "Simple XOR, Reed-Solomon, and Parity Check Matrix-based FEC Codes" },
	{ 0, NULL }
};

/* FEC exported functions */
/* ====================== */

/* Info */
/* ---- */

void fec_info_column(struct _fec *fec, packet_info *pinfo)
{
	if (fec->sbn_present)
		col_append_sep_fstr(pinfo->cinfo, COL_INFO, " ", "SBN: %u", fec->sbn);
	
	if (fec->esi_present)
		col_append_sep_fstr(pinfo->cinfo, COL_INFO, " ", "ESI: 0x%X", fec->esi);
}

/* Dissection */
/* ---------- */

/* Decode an EXT_FTI extension and fill FEC array */
void fec_decode_ext_fti(struct _ext *e, tvbuff_t *tvb, proto_tree *tree, gint ett, struct _fec_ptr f)
{
	proto_item* ti = NULL;
	proto_tree *ext_tree;
	
	if (tree)
		ti = proto_tree_add_none_format(tree, f.hf->fti_header, tvb, e->offset, e->length,
			"EXT_FTI, FEC Object Transmission Information (%u)", e->het);
	
	if (f.fec->encoding_id_present)
	{
		ext_tree = proto_item_add_subtree(ti, ett);
		rmt_ext_decode_default_header(e, tvb, ext_tree);

		/* Decode 48-bit length field */
		f.fec->transfer_length = tvb_get_ntoh64(tvb, e->offset) & G_GINT64_CONSTANT(0xFFFFFFFFFFFFU);
		
		if (f.fec->encoding_id >= 128)
		{
			/* Decode FEC Instance ID */
			f.fec->instance_id_present = TRUE;
			f.fec->instance_id = (guint8) tvb_get_ntohs(tvb, e->offset+8);
		}
		
		if (tree)
			proto_tree_add_uint64(ext_tree, f.hf->fti_transfer_length, tvb, e->offset+2, 6, f.fec->transfer_length);
	
		switch (f.fec->encoding_id)
		{
		case 0:
		case 2:
		case 128:
		case 130:
			f.fec->encoding_symbol_length = tvb_get_ntohs(tvb, e->offset+10);
			f.fec->max_source_block_length = tvb_get_ntohl(tvb, e->offset+12);
			
			if (tree)
			{
				proto_tree_add_uint(ext_tree, f.hf->fti_encoding_symbol_length, tvb, e->offset+10, 2, f.fec->encoding_symbol_length);
				proto_tree_add_uint(ext_tree, f.hf->fti_max_source_block_length, tvb, e->offset+12, 4, f.fec->max_source_block_length);
			}
			break;
			
		case 129:
			f.fec->encoding_symbol_length = tvb_get_ntohs(tvb, e->offset+10);
			f.fec->max_source_block_length = tvb_get_ntohs(tvb, e->offset+12);
			f.fec->max_number_encoding_symbols = tvb_get_ntohs(tvb, e->offset+14);
			
			if (tree)
			{
				proto_tree_add_uint(ext_tree, f.hf->fti_encoding_symbol_length, tvb, e->offset+10, 2, f.fec->encoding_symbol_length);
				proto_tree_add_uint(ext_tree, f.hf->fti_max_source_block_length, tvb, e->offset+12, 2, f.fec->max_source_block_length);
				proto_tree_add_uint(ext_tree, f.hf->fti_max_number_encoding_symbols, tvb, e->offset+14, 2, f.fec->max_number_encoding_symbols);
			}
			break;
		
		case 132:
			f.fec->encoding_symbol_length = tvb_get_ntohs(tvb, e->offset+10);
			f.fec->max_source_block_length = tvb_get_ntohl(tvb, e->offset+12);
			f.fec->max_number_encoding_symbols = tvb_get_ntohl(tvb, e->offset+16);
			
			if (tree)
			{
				proto_tree_add_uint(ext_tree, f.hf->fti_encoding_symbol_length, tvb, e->offset+10, 2, f.fec->encoding_symbol_length);
				proto_tree_add_uint(ext_tree, f.hf->fti_max_source_block_length, tvb, e->offset+12, 4, f.fec->max_source_block_length);
				proto_tree_add_uint(ext_tree, f.hf->fti_max_number_encoding_symbols, tvb, e->offset+16, 4, f.fec->max_number_encoding_symbols);
			}
			break;
		}
				
	} else
		if (tree)
			rmt_ext_decode_default_subtree(e, tvb, ti, ett);
}

/* Dissect a FEC header:
 * fec - ptr to the logical FEC packet representation to fill
 * hf - ptr to header fields array
 * ett - ptr to ett array
 * prefs - ptr to FEC prefs array
 * tvb - buffer
 * pinfo - packet info
 * tree - tree where to add FEC header subtree
 * offset - ptr to offset to use and update
 */
void fec_dissector(struct _fec_ptr f, tvbuff_t *tvb, proto_tree *tree, guint *offset)
{
	proto_item *ti;
	proto_tree *fec_tree;
	
	/* Create the FEC subtree */
	if (tree)
	{
		ti = proto_tree_add_item(tree, f.hf->header, tvb, *offset, -1, FALSE);
		fec_tree = proto_item_add_subtree(ti, f.ett->main);
	} else
		fec_tree = NULL;
	
	/* FEC Encoding ID and FEC Instance ID processing */
	if (f.fec->encoding_id_present)
	{
		if (tree)
		{
			proto_tree_add_uint(fec_tree, f.hf->encoding_id, tvb, *offset, 0, f.fec->encoding_id);
	
			if (f.fec->encoding_id >= 128 && f.fec->instance_id_present)
				proto_tree_add_uint(fec_tree, f.hf->instance_id, tvb, *offset, 0, f.fec->instance_id);
		}
		
		switch (f.fec->encoding_id)
		{
		case 0:
		case 130:
			f.fec->sbn = tvb_get_ntohs(tvb, *offset);
			f.fec->esi = tvb_get_ntohs(tvb, *offset+2);
			
			if (tree)
			{
				proto_tree_add_uint(fec_tree, f.hf->sbn, tvb, *offset, 2, f.fec->sbn);
				proto_tree_add_uint(fec_tree, f.hf->esi, tvb, *offset+2, 2, f.fec->esi);
			}
			
			f.fec->sbn_present = TRUE;
			f.fec->esi_present = TRUE;
			*offset += 4;
			break;
			
		case 2:
		case 128:
		case 132:
			f.fec->sbn = tvb_get_ntohl(tvb, *offset);
			f.fec->esi = tvb_get_ntohl(tvb, *offset+4);
			
			if (tree)
			{
				proto_tree_add_uint(fec_tree, f.hf->sbn, tvb, *offset, 4, f.fec->sbn);
				proto_tree_add_uint(fec_tree, f.hf->esi, tvb, *offset+4, 4, f.fec->esi);
			}
			
			f.fec->sbn_present = TRUE;
			f.fec->esi_present = TRUE;
			*offset += 8;
			break;
		
		case 129:
			f.fec->sbn = tvb_get_ntohl(tvb, *offset);
			f.fec->sbl = tvb_get_ntohs(tvb, *offset+4);
			f.fec->esi = tvb_get_ntohs(tvb, *offset+6);
			
			if (tree)
			{
				proto_tree_add_uint(fec_tree, f.hf->sbn, tvb, *offset, 4, f.fec->sbn);
				proto_tree_add_uint(fec_tree, f.hf->sbl, tvb, *offset+4, 2, f.fec->sbl);
				proto_tree_add_uint(fec_tree, f.hf->esi, tvb, *offset+6, 2, f.fec->esi);
			}
			
			f.fec->sbn_present = TRUE;
			f.fec->sbl_present = TRUE;
			f.fec->esi_present = TRUE;
			*offset += 8;
			break;
		}
	}
}

void fec_dissector_free(struct _fec *fec _U_)
{

}

/* Preferences */
/* ----------- */

/* Set/Reset preferences to default values */
void fec_prefs_set_default(struct _fec_prefs *prefs _U_)
{

}

/* Register preferences */
void fec_prefs_register(struct _fec_prefs *prefs _U_, module_t *module _U_)
{

}
