//
//    This file is part of Dire Wolf, an amateur radio packet TNC.
//
//    Copyright (C) 2021  John Langner, WB2OSZ
//
//    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, see <http://www.gnu.org/licenses/>.
//

#include "direwolf.h"

#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>

#include "textcolor.h"
#include "ax25_pad.h"
#include "ax25_pad2.h"
#include "il2p.h"



/*--------------------------------------------------------------------------------
 *
 * File:	il2p_header.c
 *
 * Purpose:	Functions to deal with the IL2P header.
 *
 * Reference:	http://tarpn.net/t/il2p/il2p-specification0-4.pdf
 *
 *--------------------------------------------------------------------------------*/



// Convert ASCII to/from DEC SIXBIT as defined here:
// https://en.wikipedia.org/wiki/Six-bit_character_code#DEC_six-bit_code

static inline int ascii_to_sixbit (int a) 
{
	if (a >= ' ' && a <= '_') return (a - ' ');
	return (31);	// '?' for any invalid.
}

static inline int sixbit_to_ascii (int s)
{
	return (s + ' ');
}

// Functions for setting the various header fields.
// It is assumed that it was zeroed first so only the '1' bits are set.

static void set_field (unsigned char *hdr, int bit_num, int lsb_index, int width, int value)
{
	while (width > 0 && value != 0) {
	    assert (lsb_index >= 0 && lsb_index <= 11); 
	    if (value & 1) {
	        hdr[lsb_index] |= 1 << bit_num;
	    }
	    value >>= 1;
	    lsb_index--;
	    width--;
	}
	assert (value == 0);
}

#define SET_UI(hdr,val) set_field(hdr, 6, 0, 1, val)

#define SET_PID(hdr,val) set_field(hdr, 6, 4, 4, val)

#define SET_CONTROL(hdr,val) set_field(hdr, 6, 11, 7, val)


#define SET_FEC_LEVEL(hdr,val) set_field(hdr, 7, 0, 1, val)

#define SET_HDR_TYPE(hdr,val) set_field(hdr, 7, 1, 1, val)

#define SET_PAYLOAD_BYTE_COUNT(hdr,val) set_field(hdr, 7, 11, 10, val)


// Extracting the fields.

static int get_field (unsigned char *hdr, int bit_num, int lsb_index, int width)
{
	int result = 0;
	lsb_index -= width - 1;
	while (width > 0) {
	    result <<= 1;
	    assert (lsb_index >= 0 && lsb_index <= 11); 
	    if (hdr[lsb_index] & (1 << bit_num)) {
	        result |= 1;
	    }
	    lsb_index++;
	    width--;
	}
	return (result);
}

#define GET_UI(hdr) get_field(hdr, 6, 0, 1)

#define GET_PID(hdr) get_field(hdr, 6, 4, 4)

#define GET_CONTROL(hdr) get_field(hdr, 6, 11, 7)


#define GET_FEC_LEVEL(hdr) get_field(hdr, 7, 0, 1)

#define GET_HDR_TYPE(hdr) get_field(hdr, 7, 1, 1)

#define GET_PAYLOAD_BYTE_COUNT(hdr) get_field(hdr, 7, 11, 10)



// AX.25 'I' and 'UI' frames have a protocol ID which determines how the
// information part should be interpreted.
// Here we squeeze the most common cases down to 4 bits.
// Return -1 if translation is not possible.  Fall back to type 0 header in this case.

static int encode_pid (packet_t pp)
{
	int pid = ax25_get_pid(pp);	

	if ((pid & 0x30) == 0x20) return (0x2);		// AX.25 Layer 3
	if ((pid & 0x30) == 0x10) return (0x2);		// AX.25 Layer 3
	if (pid == 0x01) return (0x3);			// ISO 8208 / CCIT X.25 PLP
	if (pid == 0x06) return (0x4);			// Compressed TCP/IP
	if (pid == 0x07) return (0x5);			// Uncompressed TCP/IP
	if (pid == 0x08) return (0x6);			// Segmentation fragmen
	if (pid == 0xcc) return (0xb);			// ARPA Internet Protocol
	if (pid == 0xcd) return (0xc);			// ARPA Address Resolution
	if (pid == 0xce) return (0xd);			// FlexNet
	if (pid == 0xcf) return (0xe);			// TheNET
	if (pid == 0xf0) return (0xf);			// No L3
	return (-1);
}

// Convert IL2P 4 bit PID to AX.25 8 bit PID.


static int decode_pid (int pid)
{
	static const unsigned char axpid[16] = {
	0xf0,			// Should not happen. 0 is for 'S' frames.
	0xf0,			// Should not happen. 1 is for 'U' frames (but not UI).
	0x20,			// AX.25 Layer 3
	0x01,			// ISO 8208 / CCIT X.25 PLP
	0x06,			// Compressed TCP/IP
	0x07,			// Uncompressed TCP/IP
	0x08,			// Segmentation fragment
	0xf0,			// Future
	0xf0,			// Future
	0xf0,			// Future
	0xf0,			// Future
	0xcc,			// ARPA Internet Protocol
	0xcd,			// ARPA Address Resolution
	0xce,			// FlexNet
	0xcf,			// TheNET
	0xf0 };			// No L3

	assert (pid >= 0 && pid <= 15);
	return (axpid[pid]);
}



/*--------------------------------------------------------------------------------
 *
 * Function:	il2p_type_1_header
 *
 * Purpose:	Attempt to create type 1 header from packet object.
 *
 * Inputs:	pp	- Packet object.
 *
 *		max_fec	- 1 to use maximum FEC symbols , 0 for automatic.
 *
 * Outputs:	hdr	- IL2P header with no scrambling or parity symbols.
 *			  Must be large enough to hold IL2P_HEADER_SIZE unsigned bytes.
 *
 * Returns:	Number of bytes for information part or -1 for failure.
 *		In case of failure, fall back to type 0 transparent encapsulation.
 *
 * Description:	Type 1 Headers do not support AX.25 repeater callsign addressing,
 *		Modulo-128 extended mode window sequence numbers, nor any callsign
 *		characters that cannot translate to DEC SIXBIT.
 *		If these cases are encountered during IL2P packet encoding,
 *		the encoder switches to Type 0 Transparent Encapsulation.
 *		SABME can't be handled by type 1.
 *
 *--------------------------------------------------------------------------------*/

int il2p_type_1_header (packet_t pp, int max_fec, unsigned char *hdr)
{
	memset (hdr, 0, IL2P_HEADER_SIZE);

	if (ax25_get_num_addr(pp) != 2) {
	    // Only two addresses are allowed for type 1 header.
	    return (-1);
	}

	// Check does not apply for 'U' frames but put in one place rather than two.

	if (ax25_get_modulo(pp) == 128) return(-1);

// Destination and source addresses go into low bits 0-5 for bytes 0-11.

	char dst_addr[AX25_MAX_ADDR_LEN];
	char src_addr[AX25_MAX_ADDR_LEN];

	ax25_get_addr_no_ssid (pp, AX25_DESTINATION, dst_addr);
	int dst_ssid = ax25_get_ssid (pp, AX25_DESTINATION);

	ax25_get_addr_no_ssid (pp, AX25_SOURCE, src_addr);
	int src_ssid = ax25_get_ssid (pp, AX25_SOURCE);

	unsigned char *a = (unsigned char *)dst_addr;
	for (int i = 0; *a != '\0'; i++, a++) {
	    if (*a < ' ' || *a > '_') {
	        // Shouldn't happen but follow the rule.
	        return (-1);
	    }
	    hdr[i] = ascii_to_sixbit(*a);
	}

	a = (unsigned char *)src_addr;
	for (int i = 6; *a != '\0'; i++, a++) {
	    if (*a < ' ' || *a > '_') {
	        // Shouldn't happen but follow the rule.
	        return (-1);
	    }
	    hdr[i] = ascii_to_sixbit(*a);
	}

	// Byte 12 has DEST SSID in upper nybble and SRC SSID in lower nybble and 
	hdr[12] = (dst_ssid << 4) | src_ssid; 

	ax25_frame_type_t frame_type;
	cmdres_t cr;			// command or response.
	char description[64];
	int pf;				// Poll/Final.
	int nr, ns;			// Sequence numbers.

	frame_type = ax25_frame_type (pp, &cr, description, &pf, &nr, &ns);

	//dw_printf ("%s(): %s-%d>%s-%d: %s\n", __func__, src_addr, src_ssid, dst_addr, dst_ssid, description);

	switch (frame_type) {

	    case frame_type_S_RR:		// Receive Ready - System Ready To Receive
	    case frame_type_S_RNR:		// Receive Not Ready - TNC Buffer Full
	    case frame_type_S_REJ:		// Reject Frame - Out of Sequence or Duplicate
	    case frame_type_S_SREJ:		// Selective Reject - Request single frame repeat

	        // S frames (RR, RNR, REJ, SREJ), mod 8, have control N(R) P/F S S 0 1
	        // These are mapped into    P/F N(R) C S S
	        // Bit 6 is not mentioned in documentation but it is used for P/F for the other frame types.
	        // C is copied from the C bit in the destination addr.
	        // C from source is not used here.  Reception assumes it is the opposite.
	        // PID is set to 0, meaning none, for S frames.

	        SET_UI(hdr, 0);
	        SET_PID(hdr, 0);
	        SET_CONTROL(hdr, (pf<<6) | (nr<<3) | (((cr == cr_cmd) | (cr == cr_11))<<2));
	
	        // This gets OR'ed into the above.
	        switch (frame_type) {
	            case frame_type_S_RR:	SET_CONTROL(hdr, 0);	break;
	            case frame_type_S_RNR:	SET_CONTROL(hdr, 1);	break;
	            case frame_type_S_REJ:	SET_CONTROL(hdr, 2);	break;
	            case frame_type_S_SREJ:	SET_CONTROL(hdr, 3);	break;
	            default:	break;
	        }

	        break;

	    case frame_type_U_SABM:		// Set Async Balanced Mode
	    case frame_type_U_DISC:		// Disconnect
	    case frame_type_U_DM:		// Disconnect Mode
	    case frame_type_U_UA:		// Unnumbered Acknowledge
	    case frame_type_U_FRMR:		// Frame Reject
	    case frame_type_U_UI:		// Unnumbered Information
	    case frame_type_U_XID:		// Exchange Identification
	    case frame_type_U_TEST:		// Test

	        // The encoding allows only 3 bits for frame type and SABME got left out.
	        // Control format:  P/F opcode[3] C n/a n/a
	        // The grayed out n/a bits are observed as 00 in the example.
	        // The header UI field must also be set for UI frames.
	        // PID is set to 1 for all U frames other than UI.

	        if (frame_type == frame_type_U_UI) {
	            SET_UI(hdr, 1);	// I guess this is how we distinguish 'I' and 'UI'
					// on the receiving end.
	            int pid = encode_pid(pp);
	            if (pid < 0) return (-1);
	            SET_PID(hdr, pid);
	        }
	        else {
	            SET_PID(hdr, 1);	// 1 for 'U' other than 'UI'.
	        }

		// Each of the destination and source addresses has a "C" bit.
		// They should normally have the opposite setting.
		// IL2P has only a single bit to represent 4 possbilities.
		//
		//	dst	src	il2p	meaning
		//	---	---	----	-------
		//	0	0	0	Not valid (earlier protocol version)
		//	1	0	1	Command (v2)
		//	0	1	0	Response (v2)
		//	1	1	1	Not valid (earlier protocol version)
		//
		// APRS does not mention how to set these bits and all 4 combinations
		// are seen in the wild.  Apparently these are ignored on receive and no
		// one cares.  Here we copy from the C bit in the destination address.
		// It should be noted that the case of both C bits being the same can't
		// be represented so the il2p encode/decode bit not produce exactly the
		// same bits.  We see this in the second example in the protocol spec.
		// The original UI frame has both C bits of 0 so it is received as a response.

	        SET_CONTROL(hdr, (pf<<6) | (((cr == cr_cmd) | (cr == cr_11))<<2));

	        // This gets OR'ed into the above.
	        switch (frame_type) {
	            case frame_type_U_SABM:	SET_CONTROL(hdr, 0<<3);	break;
	            case frame_type_U_DISC:	SET_CONTROL(hdr, 1<<3);	break;
	            case frame_type_U_DM:	SET_CONTROL(hdr, 2<<3);	break;
	            case frame_type_U_UA:	SET_CONTROL(hdr, 3<<3);	break;
	            case frame_type_U_FRMR:	SET_CONTROL(hdr, 4<<3);	break;
	            case frame_type_U_UI:	SET_CONTROL(hdr, 5<<3);	break;
	            case frame_type_U_XID:	SET_CONTROL(hdr, 6<<3);	break;
	            case frame_type_U_TEST:	SET_CONTROL(hdr, 7<<3);	break;
	            default:	break;
	        }
	        break;

	    case frame_type_I:		// Information

	        // I frames (mod 8 only)
	        // encoded control: P/F N(R) N(S)

	        SET_UI(hdr, 0);

	        int pid2 = encode_pid(pp);
	        if (pid2 < 0) return (-1);
	        SET_PID(hdr, pid2);

	        SET_CONTROL(hdr, (pf<<6) | (nr<<3) | ns);
	        break;

	    case frame_type_U_SABME:		// Set Async Balanced Mode, Extended
	    case frame_type_U:			// other Unnumbered, not used by AX.25.
	    case frame_not_AX25:		// Could not get control byte from frame.
	    default:

	        // Fall back to the header type 0 for these.
	        return (-1);
	}

// Common for all header type 1.

	// Bit 7 has [FEC Level:1], [HDR Type:1], [Payload byte Count:10]

	SET_FEC_LEVEL(hdr, max_fec);
	SET_HDR_TYPE(hdr, 1);

	unsigned char *pinfo;
	int info_len;

	info_len = ax25_get_info (pp, &pinfo);
	if (info_len < 0 || info_len > IL2P_MAX_PAYLOAD_SIZE) {
	    return (-2);
	}

	SET_PAYLOAD_BYTE_COUNT(hdr, info_len);
	return (info_len);
}


// This should create a packet from the IL2P header.
// The information part will not be filled in.

static void trim (char *stuff)
{
	char *p = stuff + strlen(stuff) - 1;
	while (strlen(stuff) > 0 && (*p == ' ')) {
	    *p = '\0';
	    p--;
	}
}



/*--------------------------------------------------------------------------------
 *
 * Function:	il2p_decode_header_type_1
 *
 * Purpose:	Attempt to convert type 1 header to a packet object.
 *
 * Inputs:	hdr - IL2P header with no scrambling or parity symbols.
 *
 *		num_sym_changed - Number of symbols changed by FEC in the header.
 *				Should be 0 or 1.
 *
 * Returns:	Packet Object or NULL for failure.
 *
 * Description:	A later step will process the payload for the information part.
 *
 *--------------------------------------------------------------------------------*/

packet_t il2p_decode_header_type_1 (unsigned char *hdr, int num_sym_changed)
{

	if (GET_HDR_TYPE(hdr) != 1 ) {
	    text_color_set(DW_COLOR_ERROR);
	    dw_printf ("IL2P Internal error.  Should not be here: %s, when header type is 0.\n", __func__);
	    return (NULL);
	}

// First get the addresses including SSID.

	char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN];
	int num_addr = 2;
	memset (addrs, 0, 2*AX25_MAX_ADDR_LEN);

// The IL2P header uses 2 parity symbols which means a single corrupted symbol (byte)
// can always be corrected.
// However, I have seen cases, where the error rate is very high, where the RS decoder
// thinks it found a valid code block by changing one symbol but it was the wrong one.
// The result is trash.  This shows up as address fields like 'R&G4"A' and 'TEW\ !'.
// I added a sanity check here to catch characters other than upper case letters and digits.
// The frame should be rejected in this case.  The question is whether to discard it
// silently or print a message so the user can see that something strange is happening?
// My current thinking is that it should be silently ignored if the header has been
// modified (correctee or more likely, made worse in this cases).
// If no changes were made, something weird is happening.  We should mention it for
// troubleshooting rather than sweeping it under the rug.

// The same thing has been observed with the payload, under very high error conditions,
// and max_fec==0.  Here I don't see a good solution.  AX.25 information can contain
// "binary" data so I'm not sure what sort of sanity check could be added.
// This was not observed with max_fec==1.  If we make that the default, same as Nino TNC,
// it would be extremely extremely unlikely unless someone explicitly selects weaker FEC.

// TODO: We could do something similar for header type 0.
// The address fields should be all binary zero values.
// Someone overly ambitious might check the addresses found in the first payload block.

	for (int i = 0; i <= 5; i++) {
	    addrs[AX25_DESTINATION][i] = sixbit_to_ascii(hdr[i] & 0x3f);
	}
	trim (addrs[AX25_DESTINATION]);
	for (int i = 0; i < strlen(addrs[AX25_DESTINATION]); i++) {
	    if (! isupper(addrs[AX25_DESTINATION][i]) && ! isdigit(addrs[AX25_DESTINATION][i])) {
	        if (num_sym_changed == 0) {
	            text_color_set(DW_COLOR_ERROR);
	            dw_printf ("IL2P: Invalid character '%c' in destination address '%s'\n", addrs[AX25_DESTINATION][i], addrs[AX25_DESTINATION]);
	        }
	        return (NULL);
	    }
	}
	snprintf (addrs[AX25_DESTINATION]+strlen(addrs[AX25_DESTINATION]), 4, "-%d", (hdr[12] >> 4) &0xf);

	for (int i = 0; i <= 5; i++) {
	    addrs[AX25_SOURCE][i] = sixbit_to_ascii(hdr[i+6] & 0x3f);
	}
	trim (addrs[AX25_SOURCE]);
	for (int i = 0; i < strlen(addrs[AX25_SOURCE]); i++) {
	    if (! isupper(addrs[AX25_SOURCE][i]) && ! isdigit(addrs[AX25_SOURCE][i])) {
	        if (num_sym_changed == 0) {
	            text_color_set(DW_COLOR_ERROR);
	            dw_printf ("IL2P: Invalid character '%c' in source address '%s'\n", addrs[AX25_SOURCE][i], addrs[AX25_SOURCE]);
	        }
	        return (NULL);
	    }
	}
	snprintf (addrs[AX25_SOURCE]+strlen(addrs[AX25_SOURCE]), 4, "-%d", hdr[12] &0xf);

// The PID field gives us the general type.
// 0 = 'S' frame.
// 1 = 'U' frame other than UI.
// others are either 'UI' or 'I' depending on the UI field.

	int pid = GET_PID(hdr);
	int ui = GET_UI(hdr);

	if (pid == 0) {

// 'S' frame.
// The control field contains: P/F N(R) C S S

	    int control = GET_CONTROL(hdr);
	    cmdres_t cr = (control & 0x04) ? cr_cmd : cr_res;
	    ax25_frame_type_t ftype;
	    switch (control & 0x03) {
	        case 0: ftype = frame_type_S_RR; break;
	        case 1: ftype = frame_type_S_RNR; break;
	        case 2: ftype = frame_type_S_REJ; break;
	        default: ftype = frame_type_S_SREJ; break;
	    }
	    int modulo = 8;
	    int nr = (control >> 3) & 0x07;
	    int pf = (control >> 6) & 0x01;
	    unsigned char *pinfo = NULL;	// Any info for SREJ will be added later.
	    int info_len = 0;
	    return (ax25_s_frame (addrs, num_addr, cr, ftype, modulo, nr, pf, pinfo, info_len));   
	}
	else if (pid == 1) {

// 'U' frame other than 'UI'.
// The control field contains: P/F OPCODE{3) C x x

	    int control = GET_CONTROL(hdr);
	    cmdres_t cr = (control & 0x04) ? cr_cmd : cr_res;
	    int axpid = 0;	// unused for U other than UI.
	    ax25_frame_type_t ftype;
	    switch ((control >> 3) & 0x7) {
	        case 0: ftype = frame_type_U_SABM; break;
	        case 1: ftype = frame_type_U_DISC; break;
	        case 2: ftype = frame_type_U_DM; break;
	        case 3: ftype = frame_type_U_UA; break;
	        case 4: ftype = frame_type_U_FRMR; break;
	        case 5: ftype = frame_type_U_UI; axpid = 0xf0; break;		// Should not happen with IL2P pid == 1.
	        case 6: ftype = frame_type_U_XID; break;
	        default: ftype = frame_type_U_TEST; break;
	    }
	    int pf = (control >> 6) & 0x01;
	    unsigned char *pinfo = NULL;	// Any info for UI, XID, TEST will be added later.
	    int info_len = 0;
	    return (ax25_u_frame (addrs, num_addr, cr, ftype, pf, axpid, pinfo, info_len));   
	}
	else if (ui) {

// 'UI' frame.
// The control field contains: P/F OPCODE{3) C x x

	    int control = GET_CONTROL(hdr);
	    cmdres_t cr = (control & 0x04) ? cr_cmd : cr_res;
	    ax25_frame_type_t ftype = frame_type_U_UI;
	    int pf = (control >> 6) & 0x01;
	    int axpid = decode_pid(GET_PID(hdr));
	    unsigned char *pinfo = NULL;	// Any info for UI, XID, TEST will be added later.
	    int info_len = 0;
	    return (ax25_u_frame (addrs, num_addr, cr, ftype, pf, axpid, pinfo, info_len));   
	}
	else {

// 'I' frame.
// The control field contains: P/F N(R) N(S)

	    int control = GET_CONTROL(hdr);
	    cmdres_t cr = cr_cmd;		// Always command.
	    int pf = (control >> 6) & 0x01;
	    int nr = (control >> 3) & 0x7;
	    int ns = control & 0x7;
	    int modulo = 8;
	    int axpid = decode_pid(GET_PID(hdr));
	    unsigned char *pinfo = NULL;	// Any info for UI, XID, TEST will be added later.
	    int info_len = 0;
	    return (ax25_i_frame (addrs, num_addr, cr, modulo, nr, ns, pf, axpid, pinfo, info_len));   
	}
	return (NULL);	// unreachable but avoid warning.

} // end


/*--------------------------------------------------------------------------------
 *
 * Function:	il2p_type_0_header
 *
 * Purpose:	Attempt to create type 0 header from packet object.
 *
 * Inputs:	pp	- Packet object.
 *
 *		max_fec	- 1 to use maximum FEC symbols, 0 for automatic.
 *
 * Outputs:	hdr	- IL2P header with no scrambling or parity symbols.
 *			  Must be large enough to hold IL2P_HEADER_SIZE unsigned bytes.
 *
 * Returns:	Number of bytes for information part or -1 for failure.
 *		In case of failure, fall back to type 0 transparent encapsulation.
 *
 * Description:	The type 0 header is used when it is not one of the restricted cases
 *		covered by the type 1 header.
 *		The AX.25 frame is put in the payload.
 *		This will cover: more than one address, mod 128 sequences, etc.
 *
 *--------------------------------------------------------------------------------*/

int il2p_type_0_header (packet_t pp, int max_fec, unsigned char *hdr)
{
	memset (hdr, 0, IL2P_HEADER_SIZE);

	// Bit 7 has [FEC Level:1], [HDR Type:1], [Payload byte Count:10]

	SET_FEC_LEVEL(hdr, max_fec);
	SET_HDR_TYPE(hdr, 0);

	int frame_len = ax25_get_frame_len (pp);

	if (frame_len < 14 || frame_len > IL2P_MAX_PAYLOAD_SIZE) {
	    return (-2);
	}

	SET_PAYLOAD_BYTE_COUNT(hdr, frame_len);
	return (frame_len);
}


/***********************************************************************************
 *
 * Name:        il2p_get_header_attributes
 *
 * Purpose:     Extract a few attributes from an IL2p header.
 *
 * Inputs:      hdr	- IL2P header structure.
 *
 * Outputs:     hdr_type - 0 or 1.
 *
 *		max_fec	- 0 for automatic or 1 for fixed maximum size.
 *
 * Returns:	Payload byte count.   (actual payload size, not the larger encoded format)
 *
 ***********************************************************************************/


int il2p_get_header_attributes (unsigned char *hdr, int *hdr_type, int *max_fec)
{
	*hdr_type = GET_HDR_TYPE(hdr);
	*max_fec = GET_FEC_LEVEL(hdr);
	return(GET_PAYLOAD_BYTE_COUNT(hdr));
}


/***********************************************************************************
 *
 * Name:        il2p_clarify_header
 *
 * Purpose:     Convert received header to usable form.
 *		This involves RS FEC then descrambling.
 *
 * Inputs:      rec_hdr	- Header as received over the radio.
 *
 * Outputs:     corrected_descrambled_hdr - After RS FEC and unscrambling.
 *
 * Returns:	Number of symbols that were corrected:
 *		 0 = No errors
 *		 1 = Single symbol corrected.
 *		 <0 = Unable to obtain good header.
 *
 ***********************************************************************************/

int il2p_clarify_header(unsigned char *rec_hdr, unsigned char *corrected_descrambled_hdr)
{
	unsigned char corrected[IL2P_HEADER_SIZE+IL2P_HEADER_PARITY];

	int e = il2p_decode_rs (rec_hdr, IL2P_HEADER_SIZE, IL2P_HEADER_PARITY, corrected);

	il2p_descramble_block (corrected, corrected_descrambled_hdr, IL2P_HEADER_SIZE);

	return (e);
}

// end il2p_header.c 