/*
 * Copyright (c) 2003-2005 Endace Technology Ltd, Hamilton, New Zealand.
 * All rights reserved.
 *
 * This source code is proprietary to Endace Technology Limited and no part
 * of it may be redistributed, published or disclosed except as outlined in
 * the written contract supplied with this product.
 *
 * $Id: dagname.c 12523 2010-03-10 01:20:05Z karthik.sharma $
 */

/* C Standard Library headers. */
#include <stdio.h>

#ifndef _WIN32
# include	<inttypes.h>
#else
# include	<wintypedefs.h>
#endif /* _WIN32 */

/* Endace headers */
#include "dagutil.h"
#include "dagname.h"

#define MAX_ENTRIES 20
#define MAX_FIELD_SIZE 128

/*to avoid memory leaks we declare a static array with atmost 25 characters to hold the data.*/
typedef struct table_entry {
	uint8_t id;
	uint8_t long_len;
	uint32_t len;
	char data[MAX_FIELD_SIZE];
}table_entry_t;

uint32_t crc_table[256];
void make_crc_table(void);
void validate_crc();
uint32_t calc_crc32(uint32_t crc, const uint8_t* buf, int len);
void print_hex(uint32_t len, char* data);
static table_entry_t table[MAX_ENTRIES];
static int last_entry = 0;
static int crc_valid = 0;
static off_t crc_offset = 0;
static int have_crc = 0;
static uint8_t *crc_ptr = NULL;
static int original_len = 0;
char *flat_table;
static const char* get_efb_string_by_id(efb_id_list_t in_id);

int 
build_table(uint8_t *xrev, uint32_t xlen, char* argv[5])
{
	int loop = 0;
	uint8_t id = 0, short_len = 0, len_is_long = 0;
	uint32_t long_len = 0;
	int done = 0;
	int cur_pos = 0;
	
	crc_ptr = xrev;
	original_len = xlen;
	if(xrev == NULL)
	{
		return -1;	
	}

	while(!done && (xlen > 0 ) && (cur_pos <  MAX_ENTRIES))/*come up with a better condition.*/
	{
		id = xrev[0];
		if((id == 0)||(id > EFB_ID_MAX ))
		{
			/*this is not an efb format file.*/
			if(cur_pos == 0) 
				return -1;
			/*id = 0; detect a padding.skip it.*/
			table[cur_pos].id = id;
			table[cur_pos].long_len = 0;
			table[cur_pos].len = 0;
			memset(table[cur_pos].data,0,MAX_FIELD_SIZE);
			xrev++;
		}
		else
		{
			
			xrev++;
			xlen--;
			short_len = xrev[0];
			if((short_len & 0x80) == 0x80)
			{
				len_is_long = 1;
				xrev--;
				xlen++;
				long_len = ((xrev[0] << 24) | (xrev[1] << 16) | (xrev[2] << 8) | (xrev[3]));
				long_len = ntohl(long_len);
				long_len &= 0x7fffffff;
				xrev+=4;
				xlen-=4;
			}
			else
			{	
				xrev++;
				xlen--;
				len_is_long = 0;
			}
			if (id == 0x4)
			{
				crc_offset = (original_len - xlen);
			}
			/*Assemble the table.*/
			table[cur_pos].id = id;
			table[cur_pos].long_len = len_is_long;
			table[cur_pos].len = (len_is_long ? long_len : (uint32_t) short_len);
			memset(table[cur_pos].data, 0, table[cur_pos].len+1);
			for(loop = 0; loop < table[cur_pos].len; loop++)
			{
				table[cur_pos].data[loop] = xrev[0];
				xrev++;
				xlen--;
			}
			table[cur_pos].data[table[cur_pos].len] = '\0';
		}
		if (id == EFB_ID_IMAGE )/* E-record. End of table */
		{
			done = 1;
		}
		cur_pos++;
	}
	if(xlen > 128)
		validate_crc();
	
	xrev-=(original_len - xlen);
	xlen+=(original_len - xlen);
	last_entry = cur_pos;
	return 0;
}

int print_table(char* argv[5])
{
	int idx, pad_cnt=0;
	char **ap = argv;
	char temp[25];
    static char constructed_efb_string[128] = {'\0'}; 
    memset(temp,0,sizeof(temp));
	for (idx=0; idx<last_entry; idx++)
	{
		switch (table[idx].id)
		{
		case 0x00:
			pad_cnt++;
			break;
		case 0x04:
                        print_hex(table[idx].len, table[idx].data);
                        break;
		case 0x0b:
			ap++;
			*ap = table[idx].data;
			break;
		case 0x0e:
			break;
		case 0x0c:
			memset(temp,0,sizeof(temp));
                        strncpy(temp,table[idx].data,4);
                        strncat(temp,"/",1);
                        strncat(temp+5,table[idx].data+4,2);
                        strncat(temp,"/",1);
                        strncat(temp+8,table[idx].data+6,2);
			temp[10] = '\0';
			memset(table[idx].data,0,MAX_FIELD_SIZE);
                        /*table[idx].data = (char*)realloc(table[idx].data,(table[idx].len+4));*/
                        strncpy(table[idx].data,temp,(strlen(temp)+ 1));
                        ap++;
                        *ap = table[idx].data;
			break;
		case 0x0d:
			memset(temp,0,sizeof(temp));
			strncpy(temp,table[idx].data,2);
			strncat(temp,":",1);
			strncat(temp+3,table[idx].data+2,2);
			strncat(temp,":",1);
			strncat(temp+6,table[idx].data+4,2);
			temp[8] = '\0';
			memset(table[idx].data,0,MAX_FIELD_SIZE);
			//table[idx].data = (char*)realloc(table[idx].data,(table[idx].len+5));
			strncpy(table[idx].data,temp,(strlen(temp) + 1));
			ap++;
			*ap = table[idx].data; 
			break;
		default:
			break;
		}
	}
    /* construct  a string with "platform""prefix"_"config"_"version" */
    snprintf(constructed_efb_string, 128, "%s%s_%s_%s",get_efb_string_by_id(EFB_ID_PLAT),get_efb_string_by_id(EFB_ID_PREFIX),get_efb_string_by_id(EFB_ID_CONFIG), get_efb_string_by_id(EFB_ID_STAMP) );

    ap++;
    argv[0] = constructed_efb_string;
	if(ap < &argv[5])
		*ap++ = NULL;	
	return 0;
}
/*
 * We put a lot of paranoia and redundancy into the code to make
 * it foolproof against spurious data.
 */
char**
dag_xrev_parse(uint8_t *xrev, uint32_t xlen, char* argv[5])
{
	int i;
	int rlen;
	int ret = 0;
	char **ap = argv;
	char expect[] = "abcd";

	make_crc_table();
	ret =  build_table(xrev,xlen,argv);
	if(ret == 0)
	{
		print_table(argv);
		return argv;
	}else
	{
		goto bit_streams;
	}
	
bit_streams:
	xrev += 13;
	xlen -= 13;

	for(i = 0 ; i < 4 ; i++) {
		/*
		 * Record tag: a,b,c,d
		 */
		if(xrev[0] != expect[i])
			goto done; /* must be a,b,c,d in that order */
		xrev++;
		xlen--;
		/*
		 * 2 Bytes record length, little endian
		 */
		rlen = (((uint32_t)xrev[0]) << 8) | xrev[1];
		xrev += 2;
		xlen -= 2;
		/*
		 * Actual record
		 */
		if(rlen > (int)xlen)
			goto done; /* record points past buffer */
		*ap++ = (char*) xrev;
		xrev += rlen;
		xlen -= rlen;
	}
	
done:
	while(ap < &argv[5])
		*ap++ = NULL;
	return argv;
}

/*
 * This routine will partially destroy the contents of xrev by
 * writing '\0' markers into the buffer and letting argv point
 * into the buffer.
 */
char*
dag_xrev_name(uint8_t* xrev, uint32_t xlen)
{
	static char retbuf[128];
	char* bp = retbuf;
	char* argv[5];
	char** ap = dag_xrev_parse(xrev, xlen, argv);

	while (*ap != NULL)
	{
		bp += sprintf(bp, "%s ", *ap++);
	}
	
	if (ap != argv)
	{
		*--bp = '\0'; /* remove trailing space */
	}
	else
	{
		*bp = '\0'; /* empty buffer */
	}
	
	return retbuf;
}
void validate_crc(void)
{
	uint32_t ncrc, crc;
	uint32_t temp_crc;
	if( original_len != 0 && crc_ptr != NULL && crc_offset != 0 )
	{
		have_crc = 1;
		/*network byte order - reverse read.*/
		ncrc = ((crc_ptr[crc_offset] << 24) | (crc_ptr[crc_offset+1] << 16) | (crc_ptr[crc_offset+2] << 8) | (crc_ptr[crc_offset+3]));
		dagutil_verbose_level(3,"ncrc read value %x \n",ncrc);
		/*zeros in wrong byte order.- the same pretty much.*/
		*(uint32_t*)(crc_ptr + crc_offset) = 0x0;  
		crc = calc_crc32(0x0, crc_ptr, original_len);
		dagutil_verbose_level(3,"calculated crc %x\n",crc);
		if (ncrc == crc )
		{
			temp_crc = ((ncrc & 0xff) << 24) | ((ncrc & 0xff00) << 8) | ((ncrc & 0xff0000) >> 8) | ((ncrc & 0xff000000) >> 24);
			*(uint32_t*)(crc_ptr + crc_offset) = temp_crc;
			crc_valid = 1;
			  
		}
		else
		{
			
			dagutil_warning("There is a mismatch between the calculated CRC and the read CRC.The file may be corrupt.\n");
			crc_valid = 0;
		}
		
	}
	else
	{
		crc_valid=0;
		have_crc=0;
		dagutil_panic("The EFB file does not have CRC Field or Unable to extract CRC\n");
	}

}
uint32_t calc_crc32(uint32_t crc, const uint8_t* buf, int len)
{
        uint32_t c = crc ^ 0xffffffffL;
        
        while (len--)
        {
                int t = (c ^ (*buf)) & 0xff;
                
                buf++;
                c = crc_table[t] ^ (c >> 8);
        }
        
        return c ^ 0xffffffffL;
}
void make_crc_table(void)
{
        uint32_t c;
        uint32_t n;
        int k;
        
        for (n = 0; n < 256; n++)
        {
                c =  n;
                for (k = 0; k < 8; k++)
                {
                        if (c & 1)
                        {
                                c = 0xedb88320L ^ (c >> 1);
                        }
                        else
                        {
                                c = c >> 1;
                        }
                }
                crc_table[n] = c;
        }
}

const char* get_efb_string_by_id(efb_id_list_t in_id)
{
    int idx = 0;
    for (idx=0; idx<last_entry; idx++)
    {
        if ( in_id == table[idx].id)
        {
            /* found match and now return the data */
            return table[idx].data;
        }
    }
    /* Not founda any match . Return empty */
    return "";
}

void print_hex(uint32_t len, char* data)
{
        int idx;
        for (idx=0; idx<len; idx++)
               dagutil_verbose_level(3,"%02x ", (uint8_t) data[idx]);
        return;
}

